<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kat</title>
    <description>The latest articles on DEV Community by Kat (@kocreative).</description>
    <link>https://dev.to/kocreative</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1699884%2F1fa19b11-6b71-4b52-bce1-3eea6031d039.png</url>
      <title>DEV Community: Kat</title>
      <link>https://dev.to/kocreative</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kocreative"/>
    <language>en</language>
    <item>
      <title>Scripting: Creating Windows For Your After Effects Scripts</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Wed, 22 Jan 2025 11:38:18 +0000</pubDate>
      <link>https://dev.to/kocreative/scripting-creating-windows-for-your-after-effects-scripts-1o8</link>
      <guid>https://dev.to/kocreative/scripting-creating-windows-for-your-after-effects-scripts-1o8</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contents&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1. Introduction&lt;/td&gt;
&lt;td&gt;6. Buttons&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2. Windows&lt;/td&gt;
&lt;td&gt;7. Options&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3. Groups&lt;/td&gt;
&lt;td&gt;8. Example&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4. Panels&lt;/td&gt;
&lt;td&gt;9. Conclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5. Text&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;br&gt;

&lt;h2&gt;
  
  
  Introduction &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Creating a window for a script is ideal when you need the end user to input information, and display instructions. In this article I'll go through the basics of creating a window for your AE script using some basic javascript and Adobe Extendscript Toolkit CC.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Windows &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To make your window, it's very simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//New Window
var mainWindow = new Window("palette", "Title", undefined);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;new Window()&lt;/code&gt; is the argument to create the window. Then, inside you need to specify the &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;bounds&lt;/code&gt;. A &lt;code&gt;palette&lt;/code&gt; window is suitable for After Effects scripts, and leaving the bounds as &lt;code&gt;undefined&lt;/code&gt; is also recommended, as the size and position can be affected later.&lt;/p&gt;

&lt;p&gt;To call your window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mainWindow.show();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since there is nothing currently in the window, this will simply call a small window to show it is working.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn402rfa2idgabplsih72.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn402rfa2idgabplsih72.png" alt="Screenshot of window with nothing in it" width="276" height="227"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Groups &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The next thing to do is to create an object to house information for your window.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;"group"&lt;/code&gt; allows you to organize objects inside of it. Before &lt;code&gt;mainWindow.show()&lt;/code&gt; add this line to your script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;group01 = mainWindow.add("group", undefined, "Group 01");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We add the group to our mainWindow, outlining the &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;bounds&lt;/code&gt;, and &lt;code&gt;text&lt;/code&gt; properties. Since we are adding a group, the first argument is &lt;code&gt;"group"&lt;/code&gt;. Again, you can leave the bounds undefined. Lastly, include what you want the group to be named. This is not displayed by default in the window.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj97zdr07bc696xdiy8mh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj97zdr07bc696xdiy8mh.png" alt="Screenshot of a group object with the text " width="560" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The default orientation of panels is by &lt;code&gt;row&lt;/code&gt;. If you want to change this to sort of &lt;code&gt;column&lt;/code&gt;, you can do so by adding this to your script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;group01.orientation = "column";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;br&gt;

&lt;h2&gt;
  
  
  Panels &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;However, if you &lt;em&gt;do&lt;/em&gt; want the name of the group to be visible, you could consider using a "panel" object instead. This type of object is helpful for sectioning off parts of the window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;group02 = mainWindow.add("panel", undefined, "Group 02");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has the same properties as the &lt;code&gt;"group"&lt;/code&gt; object, but displays slightly differently.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2b4hd6beseuisms8e0a1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2b4hd6beseuisms8e0a1.png" alt="Screenshot of a panel object with the text " width="581" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The default orientation of panels is by &lt;code&gt;column&lt;/code&gt;. If you want to change this to sort of &lt;code&gt;row&lt;/code&gt;, you can do so by adding this to your script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;group02.orientation = "row";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;br&gt;

&lt;h2&gt;
  
  
  Text &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;There are likely 2 types of text you would want to include in your script: static text, and editable text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Static Text&lt;/strong&gt;&lt;br&gt;
Static text can be used for instructions to the end user. Add &lt;code&gt;"statictext"&lt;/code&gt;to your object like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var staticText = group01.add("statictext", undefined, "Here is some static text.");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again we use the &lt;code&gt;add&lt;/code&gt; control object, with the same &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;bounds&lt;/code&gt;, and &lt;code&gt;text&lt;/code&gt; arguments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Editable Text&lt;/strong&gt;&lt;br&gt;
Editable text provides a text box field for the end user to type into. This is useful if our script requires information from the end user, like customizable text layers. Add "edittext" to your object like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var editText = group02.add("edittext", undefined, "Here is some editable text.");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is simply a different type of object. The text we provide in the text argument will initially be inside the text box, ready for the end user to delete and write their own text inside.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1v1l0h0mscq7ppshm5h6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1v1l0h0mscq7ppshm5h6.png" alt="Screenshot of script displaying a static text and editable text example." width="512" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, with the size of the boxes undefined, the text in the editable box is slightly cut off. We can fix this by defining the size after the fact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var editText = group02.add("edittext", undefined, "Here is some editable text.");
editText.size = [200, 25];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows us to make the textbox size adaptable later, if we so choose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mfmc3rfu9tthaiptn0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mfmc3rfu9tthaiptn0d.png" alt="Screenshot of script displaying a static text and editable text example,with a slightly bigger text box." width="626" height="353"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;br&gt;

&lt;h2&gt;
  
  
  Buttons &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It is also likely you will need buttons for your script, at the very least to give the end user an option to "run" the script.&lt;/p&gt;

&lt;p&gt;Adding a button is a similar process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var button01 = group02.add("button", undefined, "Run");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates our button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnbj69bg64j7jqrcou75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnbj69bg64j7jqrcou75.png" alt="Screenshot of a script with a run button." width="425" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, for our button to do anything, we will need to tell our script what to do when our button is pressed. I like to create a function to house all the tasks I want the button to complete.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;button01.onClick = function(){
    app.beginUndoGroup("Tutorial");
    completeTasks();
    };

function completeTasks(){
    mainWindow.close();
    app.endUndoGroup("Tutorial");
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we use &lt;code&gt;button01.onClick&lt;/code&gt; to run our first function, where we ask it to run &lt;code&gt;app.beginUndoGroup("Tutorial")&lt;/code&gt; so that undoing the script is a single action from inside After Effects. Next, we tell it to run our function &lt;code&gt;completeTasks&lt;/code&gt;. Underneath, I define the function &lt;code&gt;completeTasks&lt;/code&gt;. Here, include all the actions you want your button to complete (for now, mine only closes the script window), and finish the function by adding &lt;code&gt;app.endUndoGroup("Tutorial")&lt;/code&gt; to close the undo group.&lt;/p&gt;

&lt;p&gt;I'll be going into more detail about this in a future article.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Options &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Other options for control objects available are located on the &lt;a href="https://extendscript.docsforadobe.dev/user-interface-tools/control-objects.html#control-types-and-creation-parameters" rel="noopener noreferrer"&gt;Javascript Tools Guide CC&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Objects such as checkboxes, dropdown menus, radio buttons, and sliders are covered here, to name a few, and follow similar procedures to adding text or buttons.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Example &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Let's connect what we've learned about windows to the previous article, and make an example script which creates and opens a new composition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Create window and groups
var mainWindow = new Window("palette", "Title", undefined);
group01 = mainWindow.add("panel", undefined, "Description");
group02 = mainWindow.add("group", undefined, "Button");
group02.orientation = "column";

//Create objects
var staticText = group01.add("statictext", undefined, "Enter the width and height of the new composition.");
var compWidth = group02.add("edittext", undefined, "Comp Width");
compWidth.size = [100, 25];
var compHeight = group02.add("edittext", undefined, "Comp Height");
compHeight.size = [100, 25];
var button01 = group02.add("button", undefined, "Run");


//Functions
button01.onClick = function(){
    app.beginUndoGroup("NewComp");
    completeTasks();
    };

function completeTasks(){
    var compWidthSize = parseInt(compWidth.text);
    var compHeightSize = parseInt(compHeight.text);
    var comp01 = app.project.items.addComp("New Comp", compWidthSize, compHeightSize, 1, 10, 25);
    comp01.openInViewer()
    mainWindow.close();
    app.endUndoGroup("NewComp");
    };

//Return
mainWindow.show();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go through this script.&lt;/p&gt;

&lt;p&gt;I start off by creating my window and my groups. Group01 is my &lt;code&gt;"panel"&lt;/code&gt;, adding the heading "description" to the static text. Meanwhile the rest of my objects go inside Group02, a &lt;code&gt;"group"&lt;/code&gt; object. Since I want the orientation of Group02 to be a column, I set that here.&lt;/p&gt;

&lt;p&gt;Then, I create my objects. I start with my &lt;code&gt;statictext&lt;/code&gt; inside of Group01. Next, I create 2 &lt;code&gt;edittext&lt;/code&gt; objects inside of Group02, for the user to specify the size of the new composition they want to create, and I specify the size of both those text fields. Lastly, I create a &lt;code&gt;button&lt;/code&gt; so that the script can be run by the end user.&lt;/p&gt;

&lt;p&gt;Once I have created all my variables, I create my functions. I first set my &lt;code&gt;onClick&lt;/code&gt; function for my button as explained, by opening the undo group and running the custom function &lt;code&gt;completeTasks&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I then define &lt;code&gt;completeTasks&lt;/code&gt;. First, I need to use &lt;code&gt;parseInt()&lt;/code&gt; on the text of both &lt;code&gt;compWidth&lt;/code&gt; and &lt;code&gt;compHeight&lt;/code&gt; edit text objects. This converts them from text to integers, so that the rest of the script can understand the inputs. Once this is done, I create a new composition, setting the width and height to the values inputted by the user. The composition is then opened and becomes the active comp, before the script window closes, and the undo loop is closed.&lt;/p&gt;

&lt;p&gt;Running this script allows you to make a new composition set to the width and height values specified in the text fields.&lt;/p&gt;

&lt;p&gt;This script can be improved by adding an alarm, should anything be added to the text fields that isn't an integer (or perhaps setting a maximum value), to warn the user that the script has failed. This is something I will go over in more detail in another article. For now, if there is an illegal character in the text field, the script will simply not work until both the width and height text fields have numbers typed into them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthfhkp5mo49bb8mke0s7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthfhkp5mo49bb8mke0s7.png" alt="Screenshot of completed script" width="678" height="502"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This concludes the beginners guide to creating windows for After Effects scripts using Adobe Extendscript Toolkit CC. Next article I will cover creating different types of layers for your compositions.&lt;/p&gt;

</description>
      <category>aftereffects</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>After Effects: The Modulo Operator (%)</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Mon, 20 Jan 2025 10:46:23 +0000</pubDate>
      <link>https://dev.to/kocreative/after-effects-using-the-modulo-operator-in-motion-graphics-expressions-to-make-digital-and-45lj</link>
      <guid>https://dev.to/kocreative/after-effects-using-the-modulo-operator-in-motion-graphics-expressions-to-make-digital-and-45lj</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Introduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Looping Expressions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example: Digital Clock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example: Analog Clock&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;br&gt;

&lt;h2&gt;
  
  
  Introduction &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The Modulo operator (written &lt;code&gt;%&lt;/code&gt;), aka the remainder operator, is a useful part of expression making that isn't immediately easy to understand. So what does it do, and what is it used for?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;%&lt;/code&gt; is used to work out the remainder of an equation. Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10 % 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the expression will return &lt;code&gt;1&lt;/code&gt;. This is because when you divide 10 by 3, you get 3 &lt;strong&gt;remainder&lt;/strong&gt; 1.&lt;/p&gt;

&lt;p&gt;This is very useful for creating loops when used on the &lt;code&gt;time&lt;/code&gt; variable.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Looping Expressions &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Most designers starting out with After Effects expressions will know the &lt;code&gt;loopOut()&lt;/code&gt; expression. This allows us to loop keyframes in our property, using &lt;code&gt;"cycle"&lt;/code&gt; (looping back from start to finish), &lt;code&gt;"pingpong"&lt;/code&gt; (looping from start - finish - then backwards to the start again), &lt;code&gt;"offset"&lt;/code&gt; (repeating the keyframes but &lt;em&gt;offsetting&lt;/em&gt; the values each time to build the animation), or &lt;code&gt;"continue"&lt;/code&gt; (uses the velocity from the last keyframe to continue the motion). This is very comprehensive, and covers everything you could need while keyframing animation.&lt;/p&gt;

&lt;p&gt;But, if you want to loop an &lt;strong&gt;expression&lt;/strong&gt;, &lt;code&gt;loopOut&lt;/code&gt; isn't a viable solution. There could be a few reasons for not wanting to use keyframes, but the main reason is if a value needs to be dynamic, and constantly updated. It is much easier to update an expression attached to a slider, than a set of keyframes.&lt;/p&gt;

&lt;p&gt;If the motion is continuous, then &lt;code&gt;Linear&lt;/code&gt; or &lt;code&gt;Ease&lt;/code&gt; would work just fine. But for complex animations that require a loop, we can use the modulo operator against &lt;code&gt;time&lt;/code&gt; to achieve that loop.&lt;/p&gt;

&lt;p&gt;To understand how it works, copy and paste this expression into a text layer's Source Text property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Math.floor(time % 5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see the layer counts from 0-4 each second, looping back to 0 on every 5 second interval. This is because, as time moves, the remainder of our expression will change each second:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Remainders over time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1 % 5 = 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2 % 5 = 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3 % 5 = 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4 % 5 = 4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5 % 5 = 0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;Math.floor&lt;/code&gt; addition rounds the argument to return only whole numbers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mktp3ffqwdkelkscsc9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mktp3ffqwdkelkscsc9.gif" alt="Image description" width="1280" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, it is easy to see how this can be employed when needing to animate numbers between certain parameters.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Example: Digital Clock &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Let’s use &lt;code&gt;%&lt;/code&gt; to make a digital clock.&lt;/p&gt;

&lt;p&gt;The seconds need to count between 0 - 60, meanwhile the minutes need to count up at each interval of 60. Let's paste this into our text layer's Source Text property again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sec = Math.floor(time % 60);
minute = Math.floor(time / 60);

if (sec &amp;lt; 10) sec = "0" + sec;

minute + ":" + sec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking the expression down, our &lt;code&gt;sec&lt;/code&gt; variable will count from 0 - 60, while the &lt;code&gt;minute&lt;/code&gt; variable will increase on every multiple of 60 (again, we use &lt;code&gt;Math.floor&lt;/code&gt; to round out the numbers). The &lt;code&gt;if&lt;/code&gt; statement that follows adds a 0 to the front of the &lt;code&gt;sec&lt;/code&gt; variable if it is less than 10, ensuring that our seconds variable always has 2 digits (which could also be repeated for the minutes if desired). Then, it is just a case of stringing it all together with a time separator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz80nkmv7hzku7rdl8ehk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz80nkmv7hzku7rdl8ehk.gif" alt="GIF of a clock counting up" width="1280" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you need the counter to work independently of &lt;code&gt;time&lt;/code&gt;, you can achieve the same effect by swapping time out with a slider, and animating its value.&lt;/p&gt;

&lt;p&gt;You can also make the time separator blink, using the modulo operator and the After Effects text expression selector.&lt;/p&gt;

&lt;p&gt;Go to your text layer, and add an &lt;code&gt;opacity&lt;/code&gt; animation option to your text layer (if you're not sure how to do this, you can check out &lt;a href="https://dev.to/kocreative/after-effects-text-animation-and-the-expression-selector-a2l"&gt;this article&lt;/a&gt; all about it). Then add an expression selector, and remove the range selector.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvo4vksymo7ws6wdpxnvr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvo4vksymo7ws6wdpxnvr.png" alt="Screenshot of the text layer in the AE project with the new animator property" width="651" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the opacity within the animator to 0, and then add this expression to the &lt;code&gt;amount&lt;/code&gt; property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Digital Clock Divider Blink
//Add to the expression selector

minute = Math.floor(time / 60);

minute &amp;lt; 10 &amp;amp;&amp;amp; textIndex == 2 ? Math.floor(time*2 % 1.5) * 100 : minute &amp;gt;= 10 &amp;amp;&amp;amp; textIndex == 3 ? Math.floor(time*2 % 1.5) * 100 : 0;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have written a conditional statement, based on the number of digits within the minutes variable not being fixed. First, I copy the &lt;code&gt;minute&lt;/code&gt; variable from my source text property. Then, I used it to calculate my time separator's &lt;code&gt;textIndex&lt;/code&gt; value. When there is 1 digit in the minute display, it will equal 2. When the minute display is over 10, then it will be 3. The conditional statement can also be written as an &lt;code&gt;if&lt;/code&gt; statement, like this, to explain further what it is doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (minute &amp;lt; 10 &amp;amp;&amp;amp; textIndex == 2) Math.floor(time*2 % 1.5) * 100
    else if (minute &amp;gt;= 10 &amp;amp;&amp;amp; textIndex == 3) Math.floor(time*2 % 1.5) * 100
        else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the minutes are under 10 and the &lt;code&gt;textIndex&lt;/code&gt;equals 2, then &lt;code&gt;Math.floor(time*2 % 1.5) * 100&lt;/code&gt; affects the 2nd character in the text layer. This will make the letter flash (to a ratio of 2:1 on:off) thanks to the modulo operator. The &lt;code&gt;Math.floor&lt;/code&gt; argument rounds the numbers, while the whole thing is multiplied by 100 at the end to toggle between the numbers 0 and 100, the range of our expression selector.&lt;/p&gt;

&lt;p&gt;However, if the minutes are equal to or above 10 and the &lt;code&gt;textIndex&lt;/code&gt; equals 3, the effect will then be applied to the 3rd character in the text layer instead. This accounts for the extra digit in the minutes display. If your minutes display will need to go past 99, another argument will need to be added to affect the time separator when it is in the 4th position.&lt;/p&gt;

&lt;p&gt;However, if your minutes display is set to a constant number of digits, the statement becomes much simpler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dividerIndex = 3;
textIndex == dividerIndex ? Math.floor(time*2 % 1.5) * 100 : 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprba3qenql608fw7z5vq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fprba3qenql608fw7z5vq.gif" alt="GIF of a digital clock with blinking time divider" width="1280" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And with that, you have a digital clock!&lt;/p&gt;

&lt;p&gt;After displaying how the modulo operator helps create loops, we can now think about how to apply this to other properties.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Example: Analog Clock &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Let's now make an analog clock. When the hands tick, it usually isn't one continuous motion, but instead something which stops and starts abruptly. This is the kind of loop the modulo operator can help with.&lt;/p&gt;

&lt;p&gt;Let's go through the following expression we can paste into the rotation property of a clock hand layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Second Hand Rotation
frames = thisComp.frameDuration;

loopTime = 1;
dur = frames * 6;
strength = 6;

counter = Math.floor(time/loopTime);
t = time % loopTime;

ease(t, 0, dur, strength * counter, strength * (counter + 1))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we set some variables. &lt;code&gt;frames&lt;/code&gt; is how long a frame in our composition lasts, making it able to work across multiple framerates.&lt;/p&gt;

&lt;p&gt;Set &lt;code&gt;loopTime&lt;/code&gt; to how much time you want to loop. I want the loop to last a second, so I set it to 1. &lt;code&gt;dur&lt;/code&gt; is the duration of the animation within the loop, so I set mine to &lt;code&gt;frames * 6&lt;/code&gt;, making it last for 6 frames. &lt;code&gt;strength&lt;/code&gt; is the change in value of the animation, and since I'm animating a clock hand, I set mine to 6, so the clock hand will make a full rotation in 60 ticks.&lt;/p&gt;

&lt;p&gt;Next, I make a &lt;code&gt;counter&lt;/code&gt; variable, which will help offset my values. I create this with &lt;code&gt;Math.floor(time/loopTime)&lt;/code&gt;, rounding the number with &lt;code&gt;Math.floor&lt;/code&gt; and setting the speed of the counter to match the loops. Lastly, &lt;code&gt;t&lt;/code&gt; is the variable we can use to time our expression driven animation. This is &lt;code&gt;time % loopTime&lt;/code&gt;, so the time loops whenever it reaches the number stored inside &lt;code&gt;loopTime&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After that, we can make our animation. I'm using an &lt;code&gt;ease&lt;/code&gt; expression in this example. By setting the first argument to &lt;code&gt;t&lt;/code&gt;, we remap our rotation value to our loop time variable. The next 2 arguments are &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;dur&lt;/code&gt;, the start and end points of the animation. The last 2 arguments are &lt;code&gt;strength * counter&lt;/code&gt; and &lt;code&gt;strength * (counter + 1)&lt;/code&gt;, the value of our rotation property. By multiplying the &lt;code&gt;strength&lt;/code&gt; by the &lt;code&gt;counter&lt;/code&gt;, we can offset the values each loop, ending on &lt;code&gt;strength * (counter + 1)&lt;/code&gt;, ready for the next loop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjp16d5kf9pg63bzyavou.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjp16d5kf9pg63bzyavou.gif" alt="GIF 01 of animated analog clock with a second hand ticking" width="1280" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The advantage of powering the motion via expressions rather than keyframes in this instance would be if you need to build a clock template for constantly changing times. The static values of the expression can be connected to sliders, making it much easier to update constantly.&lt;/p&gt;

&lt;p&gt;You could use a more advanced expression or build your own function to make more bespoke animation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Second Hand Rotation
frames = thisComp.frameDuration;

loopTime = 1;
dur = frames * 6;
change = 6;

counter = Math.floor(time/loopTime);
t = time % loopTime;

function easeInOutBack (t, b, c, d, s) {
    if (s == undefined) s = 1.70158;
    if ((t/=d/2) &amp;lt; 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
    return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
}

easeInOutBack(clamp(t, 0, dur), change * counter, change, dur)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xpi5wijhrca2526fr9w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xpi5wijhrca2526fr9w.gif" alt="GIF 02 of animated analog clock with a second hand ticking" width="1280" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lastly, you could create a variable to set the start value and use an &lt;code&gt;if&lt;/code&gt; statement to skip the first iteration of the animation for the minute (and possibly hour) hands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Minute Hand Rotation
frames = thisComp.frameDuration;

loopTime = 60;
dur = frames * 6;
strength = 6;
startValue = 180;

counter = Math.floor(time/loopTime);
t = time % loopTime;

function easeInOutBack (t, b, c, d, s) {
    if (s == undefined) s = 1.70158;
    if ((t/=d/2) &amp;lt; 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
    return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
}

if (time &amp;lt; loopTime) startValue
    else easeInOutBack(clamp(t, 0, dur), (strength * counter) + startValue, strength, dur)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7s3qlgck1n7y3f6mgxfp.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7s3qlgck1n7y3f6mgxfp.gif" alt="GIF 03 of animated analog clock with a second and minute hand ticking" width="1280" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From here, it is just a case of connecting a slider to our &lt;code&gt;startValue&lt;/code&gt; variable. From there, you have an analog clock that can be updated by simply changing the value in the slider.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The modulo operator is very useful for creating loops to aid dynamic expressions, where other means do not fit the project's needs.&lt;/p&gt;

&lt;p&gt;Try testing it out in some of your own projects!&lt;/p&gt;

&lt;p&gt;Have any comments? Something not adding up? Let me know down below.&lt;/p&gt;

</description>
      <category>aftereffects</category>
      <category>tutorial</category>
      <category>productivity</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Scripting: After Effects Projects and Compositions</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Wed, 04 Dec 2024 14:17:00 +0000</pubDate>
      <link>https://dev.to/kocreative/scripting-after-effects-projects-and-compositions-18jb</link>
      <guid>https://dev.to/kocreative/scripting-after-effects-projects-and-compositions-18jb</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Introduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Folders And Compositions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Example&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quick Tips&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;br&gt;

&lt;h2&gt;
  
  
  Introduction &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In this article I'll be going through some of the basics of navigating After Effects projects and compositions using a script. I'll be referencing some of the most useful parts of the &lt;a href="https://ae-scripting.docsforadobe.dev/index.html" rel="noopener noreferrer"&gt;scripting guide&lt;/a&gt;, and showing off some practical examples of how it works.&lt;/p&gt;

&lt;p&gt;Let's get started.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Application &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;app&lt;/code&gt; references the application After Effects itself. To reference anything inside After Effects, you'll need to start by telling your script to look at the application.&lt;/p&gt;

&lt;p&gt;While you can reference the settings, files and the computer system After Effects is installed on (i'll be going into these options in another article), it is more than likely the main object you will need to reference after the application is an After Effects project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.newProject()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;newProject()&lt;/code&gt; creates a new, empty project. You'll be prompted by After Effects if you'd like to save your current work before this happens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.open(file)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;open()&lt;/code&gt; meanwhile allows you to open an existing project. Leaving the brackets blank, it will bring up the open project dialogue box as if navigating to &lt;code&gt;File &amp;gt; Open Project...&lt;/code&gt; in the After Effects menu. Alternatively, you can reference a file inside the brackets to open a specific project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project = new File ("...FilePath/AE Project.aep");
app.open(project);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will need to create a &lt;code&gt;new File()&lt;/code&gt; to locate the file from within your script. I prefer to store this in a variable to keep things tidy. Again, you'll be prompted to save your current project before the file opens.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Project &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;project&lt;/code&gt; references the &lt;em&gt;current&lt;/em&gt; project open in After Effects. From here, we can access all the items from within our project, create new ones, and access the render queue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.project.save([file])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;save()&lt;/code&gt; saves the project. Without adding the option of a file, or if the project has not been previously saved, this method will bring up the save dialogue for the user to save their project. Remember - you need to create a &lt;code&gt;new File&lt;/code&gt; in your project before you can reference it in this method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.project.importFile(importOptions)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;importFile()&lt;/code&gt; works a little like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new ImportOptions().file = new File("...FilePath/My File.jpg");
app.project.importFile(file)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'll be going into importing files in more detail in another article. But as a quick overview, you use this method to import files into your project. Not only do you need to create a &lt;code&gt;new File&lt;/code&gt;, but you also have to create &lt;code&gt;new ImportOptions&lt;/code&gt; to specify what you are importing, and how. This allows us to do things like import image sequences, import files as, and force alphabetical order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.project.importFileWithDialog()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;importFileWithDialog()&lt;/code&gt; meanwhile opens up the import footage dialogue box, for the end user to select their file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.project.renderQueue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;renderQueue&lt;/code&gt; grants us access to the render queue, and allows us to set render settings and even render compositions. I will be going more into this in another article.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.project.activeItem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;activeItem&lt;/code&gt; refers to the current item being viewed, usually a composition, footage layer, placeholder, or solid. It only references one item at a time, and returns a null if multiple items are active, or if none are active. It can be handy to reference the active composition, for scripts which add layers or affect what the user is currently working on in some way. Note that this isn't the same as an item being &lt;em&gt;selected&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.project.selection
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;selection&lt;/code&gt; refers to all the items currently selected inside the project panel. This is what you need when referencing the items selected, rather than the item that is active.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.project.item(index)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;item()&lt;/code&gt; refers specifically to a single item inside of your project - be it a composition, solid, or what have you. Like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.project.item(1)
app.project.item("Comp 01")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;index&lt;/code&gt; represents either the index number of the item inside the project window, or, can also refer to the name of the layer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.project.items
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;items&lt;/code&gt; meanwhile refers to the &lt;em&gt;collection&lt;/em&gt; of items inside your project. It is used to create new compositions and folders.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Folders And Compositions &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This brings us nicely onto folders and compositions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.project.items.addFolder(name);
app.project.items.addComp(name, width, height, pixelAspect, duration, frameRate);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;addFolder()&lt;/code&gt; creates a new folder for your project. Make sure the name argument is a string (in " " or ' ').&lt;/p&gt;

&lt;p&gt;&lt;code&gt;addComp()&lt;/code&gt; however has many more arguments to consider. This is because there is a lot of information that is needed to create a new composition:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Argument&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The name of the composition. Needs to be a string (in " " or ' ')&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;width&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The width of your composition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;height&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The height of your composition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pixelAspect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The pixel aspect ratio. You are almost certainly looking to set this to Square Pixels, which you can do by setting the ratio to &lt;code&gt;1&lt;/code&gt;. Any other pixel aspect ratio can be set by entering the correct ratio (for example, Anamorphic 2:1 can be set by entering &lt;code&gt;2&lt;/code&gt;, and D1/DV PAL Widescreen can be set by entering &lt;code&gt;1.46&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;duration&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The duration of the composition in seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;frameRate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The frame rate of the composition&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can create new comps inside of folders by referencing the folder instead, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;folder01 = app.project.items.addFolder("Comps");
Comp01 = folder01.items.addComp("Comp 01", 1920, 1080, 1, 5, 25);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And can move items into the folder after the fact, by setting the item's &lt;code&gt;parentFolder&lt;/code&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;folder01 = app.project.items.addFolder("Comps");
Comp01 = app.project.items.addComp("Comp 01", 1920, 1080, 1, 5, 25);

Comp01.parentFolder = folder01;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've created a composition, you can set it as your active item by using &lt;code&gt;openInViewer()&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;comp1.openInViewer();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;br&gt;

&lt;h2&gt;
  
  
  Example &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Using a little of what I've covered, here is a short script which allows you to open a new project, create 2 folders and 2 compositions, and add one comp to the other as a precomp.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.newProject();

folder1 = app.project.items.addFolder("_Final");
folder2 = app.project.items.addFolder("Precomps");
comp1 = folder1.items.addComp("Comp 01", 1920, 1080, 1, 10, 25);
comp2 = folder2.items.addComp("Comp 02", 1920, 1080, 1, 10, 25);

comp1.openInViewer();
app.project.activeItem.layers.add(comp2);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;br&gt;

&lt;h2&gt;
  
  
  Quick Tips &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;You'll find, after running this script, if you were to press undo in After Effects, it will only undo each action one at a time. Most of the time this isn't ideal, as scripts often undergo many actions, making this very time consuming and annoying for the end user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.beginUndoGroup(undoString)
app.endUndoGroup(undoString)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is where &lt;code&gt;beginUndoGroup()&lt;/code&gt; and &lt;code&gt;endUndoGroup()&lt;/code&gt; come in. They allow you to group the script's actions together, so that they can be undone in one motion. The &lt;code&gt;undoString&lt;/code&gt; is what you will see next to the undo option in After Effects. While you don't necessarily need to add &lt;code&gt;endUndoGroup()&lt;/code&gt; if you only have one instance of &lt;code&gt;beginUndoGroup()&lt;/code&gt; in your script (as it will close automatically), it is good practice to add it to the end of your script, to keep your script tidy.&lt;/p&gt;


&lt;br&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I hope this has helped to shed some light on how to reference After Effects projects and compositions while making your After Effects scripts. In the next article, I will go over creating pop up windows for users to interact with your scripts.&lt;/p&gt;

&lt;p&gt;Have any questions? Something wrong here or not working? Let me know in the comments.&lt;/p&gt;

</description>
      <category>aftereffects</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Scripting: Beginner's Guide To Creating After Effects Scripts</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Thu, 28 Nov 2024 11:25:57 +0000</pubDate>
      <link>https://dev.to/kocreative/scripting-beginners-guide-to-creating-after-effects-scripts-2ahl</link>
      <guid>https://dev.to/kocreative/scripting-beginners-guide-to-creating-after-effects-scripts-2ahl</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Introduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Why Scripts?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extendscript Toolkit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;br&gt;

&lt;h2&gt;
  
  
  Introduction &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;After spending years with After Effects expressions, it seems natural to branch out into writing custom scripts. However, I found it's not that easy. It can be quite daunting for beginners - with information in the &lt;a href="https://ae-scripting.docsforadobe.dev/index.html" rel="noopener noreferrer"&gt;scripting guide&lt;/a&gt; available, but with very few practical examples to learn from. I found myself constantly asking "Okay, but how do I &lt;em&gt;apply&lt;/em&gt; these functions without throwing an error?"&lt;/p&gt;

&lt;p&gt;In this series, I aim to create some basic guides to help you create your own scripts for After Effects. I'll be referencing all the useful information in the scripting guide, but aim to contextualize it a little.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Why Scripts? &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;So, why make scripts? There are many reasons to create a script for After Effects.&lt;/p&gt;

&lt;p&gt;My main reason for wanting to make scripts is to have a tangible, separate file, meaning there's no need to open up or import an existing After Effects project. Instead of copy-pasting information, adjusting sliders, or using the Essential Graphics panel, a script will allow you to create your own panel to store information, or create buttons which can trigger custom code.&lt;/p&gt;

&lt;p&gt;In short, they are useful for making templates to hand over to clients, but also for creating shortcuts for yourself and your own workflow. Many of the most popular scripts out there improve efficiency in this way.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Extendscript Toolkit &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;To create your script, you will need software. Any program which lets you generate a .jsx file will work, but I personally like to use Extendscript Toolkit because it can connect directly into Adobe software while testing.&lt;/p&gt;

&lt;p&gt;If you have Adobe Creative Cloud, you will already have access to Extendscript Toolkit, however you may be unaware of this. This is because Extendscript Toolkit is old software, and hidden away from view.&lt;/p&gt;

&lt;p&gt;In order to find and download Extendscript Toolkit, open up Creative Cloud, and navigate to your preferences menu. Click on the "Apps" tab, and scroll all the way to the bottom until you find the toggle labelled &lt;code&gt;Show Older Apps&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kqgd0s27zj53txbmtoi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5kqgd0s27zj53txbmtoi.png" alt="Screenshot of Adobe Creative Cloud Show Older Apps option" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once this toggle is on, Extendscript Toolkit will be available to download on your app page.&lt;/p&gt;

&lt;p&gt;Opening up Extendscript Toolkit, you'll see this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87wy93bszlal27xbpd4a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87wy93bszlal27xbpd4a.png" alt="Screenshot of Extendscript Toolkit" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is where we will write our scripts. At the top, navigate to the dropdown menu &lt;code&gt;Extendscript Toolkit CC&lt;/code&gt;. You'll see the option to select any Adobe software currently installed on your machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwf7ygf544wrbi52bou81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwf7ygf544wrbi52bou81.png" alt="Screenshot of Extendscript Toolkit menu" width="568" height="601"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will allow you to connect Extendscript Toolkit directly to whichever Adobe program you are writing scripts for. I have selected my most up to date version of After Effects. Now, whenever I press the play button on the top toolbar, it will run the script directly in After Effects without me having to save out the file - which is great for testing things out.&lt;/p&gt;

&lt;p&gt;With After Effects open, try adding the following code to Extendscript Toolkit and running it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var mainWindow = new Window("palette", "My First Window", undefined);
var mainGroup = mainWindow.add("group", undefined);
var mainText = mainGroup.add("statictext", undefined, "Hello World!");


mainWindow.show();
mainWindow.center();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have selected the correct version of After Effects, you should see your first window pop up!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcquewtfgeefi0z3w85u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzcquewtfgeefi0z3w85u.png" alt="Hello World window" width="186" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll go into detail about creating windows and the options available in another article.&lt;/p&gt;

&lt;p&gt;Now that you have Extendscript Toolkit installed, you're ready to start making scripts!&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Scripts are powerful tools which let us create After Effects templates and improve our own workflows by adding custom, unique tools.&lt;/p&gt;

&lt;p&gt;In upcoming articles I will go over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigating After Effects and selecting projects and compositions&lt;/li&gt;
&lt;li&gt;Creating windows and panels&lt;/li&gt;
&lt;li&gt;Adding new layers and compositions&lt;/li&gt;
&lt;li&gt;Creating, editing, and animating text layers&lt;/li&gt;
&lt;li&gt;Creating, editing, and animating shape layers&lt;/li&gt;
&lt;li&gt;And more!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me know in the comments if there is a specific topic you would like to see me cover.&lt;/p&gt;

</description>
      <category>aftereffects</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>After Effects: The Essential Graphics Panel</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Tue, 12 Nov 2024 14:49:07 +0000</pubDate>
      <link>https://dev.to/kocreative/project-using-the-essential-graphics-panel-for-next-level-after-effects-templates-5405</link>
      <guid>https://dev.to/kocreative/project-using-the-essential-graphics-panel-for-next-level-after-effects-templates-5405</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Setting Up Your Precomps&lt;/li&gt;
&lt;li&gt;Adding To The Essential Graphics Panel&lt;/li&gt;
&lt;li&gt;Adding A Dropdown Menu&lt;/li&gt;
&lt;li&gt;Advanced Text Options&lt;/li&gt;
&lt;li&gt;Changing Fill Options&lt;/li&gt;
&lt;li&gt;Limitations&lt;/li&gt;
&lt;li&gt;Other Ideas&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Creating efficient After Effects templates is incredibly important, especially when passing the projects to clients who may not be as software savvy. In the past, this meant shy-guying layers away out of sight, locking everything down, and hoping they wouldn't be too confused by the concept of opening a precomp.&lt;/p&gt;

&lt;p&gt;However, with the addition of the Essential Graphics panel, we can now put all of our template options in one place. And, if we combine this with some helpful expressions - we can create all sorts of options for our users.&lt;/p&gt;

&lt;p&gt;Let me show you how I achieve this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Your Precomps&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;First thing first, design and animate your layout. You will want to get this nailed down first before you version your project (if necessary).&lt;/p&gt;

&lt;p&gt;If your project will contain multiple options, for instance a different number of people, I suggest starting with the maximum number and working backwards. I have made this quick example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhiwspcjlg2ekbcajing.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxhiwspcjlg2ekbcajing.gif" alt="GIF of a 4 person title screen animating on" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that I'm happy with my layout, I need to make the rest of my versions, so I end up with this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcz16fl1iof9hv32esd5u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcz16fl1iof9hv32esd5u.png" alt="image of 1 person layout" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7tj1tfe7qrgh7ozh2529.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7tj1tfe7qrgh7ozh2529.png" alt="Image of 2 people layout" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmi4997t5kw3hp9ivpf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwmi4997t5kw3hp9ivpf1.png" alt="Image of 3 people layout" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fia64d819ge9klydiy0ir.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fia64d819ge9klydiy0ir.png" alt="Image of 4 people layout" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is also important that your project is tidy before you start making your template, otherwise you may have problems down the line. I have separated each of my layouts with a precomp:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnj6ik3cyd9vfn41cq2ln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnj6ik3cyd9vfn41cq2ln.png" alt="Image of the AE timeline, showing 4 precomps and a background solid" width="536" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And all of my people are inside of their own precomps (with their images inside a further precomp for swapping out images later):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43d9j8elw99scef20ux2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43d9j8elw99scef20ux2.png" alt="Image of the AE project panel, showing precomps clearly labeled and in folders." width="389" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38ligle2haso1mfix95p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38ligle2haso1mfix95p.png" alt="Image inside the 4 people layout precomp, showing the precomps of people 1-4" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxx0kgbvq7ruze1zet1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftxx0kgbvq7ruze1zet1k.png" alt="Image inside of the 1 person precomp, showing the text layers and the image precomp" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbs17pmp8ud8bzb3yy8k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmbs17pmp8ud8bzb3yy8k.png" alt="Image inside of the 1 person image precomp, showing the 2 layers which make up the image" width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that my project is tidy and I'm happy with the animation and layout, I can begin importing information into my Essential Graphics Panel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding To Your Essential Graphics Panel&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;If it is not already open, open the panel by selecting &lt;code&gt;window&lt;/code&gt; &amp;gt; &lt;code&gt;Essential Graphics&lt;/code&gt;. You will see the panel open. Select your main comp as your &lt;code&gt;primary&lt;/code&gt; composition, and name your template something appropriate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjaxa2jeekodm8b3e3l7q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjaxa2jeekodm8b3e3l7q.png" alt="Image of the Essential Graphics Panel" width="739" height="934"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the spirit of keeping our template tidy, let's start with making 4 folders to store information for the 4 different speakers. At the bottom of the Essential Graphics panel, selecting &lt;code&gt;Add Formatting&lt;/code&gt; &amp;gt; &lt;code&gt;Add Group&lt;/code&gt;. Name your group, and repeat for all your speakers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgy4q7cdk9t9vogrm879s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgy4q7cdk9t9vogrm879s.png" alt="Image of the Essential Graphics Panel with 4 folders" width="740" height="937"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are ready to add our layers. Navigate to the precomp which contains the layers you wish to edit as part of your template. For me, that is &lt;code&gt;Person_01&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For text layers, open up the layer options until you see &lt;code&gt;Text&lt;/code&gt; &amp;gt; &lt;code&gt;Source Text&lt;/code&gt;. Click and drag the Source Text option into your &lt;code&gt;Person 01&lt;/code&gt; folder in the Essential Graphics panel to add it to your template. At this point, you can rename your text field label if you wish.&lt;/p&gt;

&lt;p&gt;For images, such as our display image, click and drag the image into your folder. Since my image is multiple layers, I create 1 more precomp and drag that into my template.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbszhbawilky12z3y5pcb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbszhbawilky12z3y5pcb.png" alt="Image of the Essential Graphics panel with 2 text layers and an image inside of the Person 01 folder" width="740" height="937"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Repeat these steps for all your other precomps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7j186p7rgow6un1qj99t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7j186p7rgow6un1qj99t.png" alt="Image of the Essential Graphics panel with all folders containing 2 text layers and 1 image" width="739" height="941"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you are done, you can close all other timeline panels except your main composition. To test your template, edit the text in all your layers. You should see them update in your main comp, without having to go into all your individual precomps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkre68855t134bllzklf8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkre68855t134bllzklf8.gif" alt="GIF of renaming layers" width="1080" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! Our text layers all work. But what about our images? To test your images, double click on them in your Essential Graphics panel. This will open up the exact precomp which contains the image. Drag in a new image from your project, then close the precomp. You'll see it updated in your main comp.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4a0cqjhmvedc0i06cusn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4a0cqjhmvedc0i06cusn.gif" alt="GIF of updating image from the Essential Graphics panel" width="1006" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I prefer this method as it opens the correct precomp for the user, as opposed to them having to look for it. This is why I make sure my images are precomped nicely. If your images and precomps are correctly sized, the end users should only have to drag and drop assets (although you may also want to make a Photoshop or Canva template to help them scale their images too).&lt;/p&gt;

&lt;p&gt;We have our basic template! Now we have to add a way to toggle between the layouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding A Dropdown Menu&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Add a null layer to your composition, and name it "DROPDOWN". In the effects and presets panel, search for the "Dropdown Menu Control". Add it to your new null layer. You'll see it in your effect controls panel like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnely8wf5mr58mkd44mu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcnely8wf5mr58mkd44mu.png" alt="Image of the basic Dropdown Menu Control" width="396" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press the edit button. You'll see another box pop up with 3 items by default in the menu. Add as many items as you need, and rename them to match your template. In my case, I will rename them after the number of people in my layouts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5tp0dazg5q7gkrdunwgl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5tp0dazg5q7gkrdunwgl.png" alt="Image of the Dropdown Menu Control Edit" width="275" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you're happy, press ok to close the box. Now we need to connect the menu to the opacity of our precomps. We can do this with some simple expressions.&lt;/p&gt;

&lt;p&gt;Effectively, we want to create an expression so After Effects understands "When the 1 person option is selected in the dropdown menu, only display the 1 person precomp." And so on with all our other options.&lt;/p&gt;

&lt;p&gt;This is easily achieved with an &lt;code&gt;if&lt;/code&gt; statement inside each precomp's opacity property.&lt;/p&gt;

&lt;p&gt;Starting with our 1 person precomp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (thisComp.layer("DROPDOWN").effect("Dropdown Menu Control")("Menu") == 1) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If our dropdown menu has its first option selected, the 1 person precomp will be set to 100% opacity. If any other option is selected however, the 1 person precomp will be set to 0% opacity.&lt;/p&gt;

&lt;p&gt;We can add this expression to all our other precomps, making sure to change it slightly to target the correct menu option for each layer.&lt;/p&gt;

&lt;p&gt;2 people precomp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (thisComp.layer("DROPDOWN").effect("Dropdown Menu Control")("Menu") == 2) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3 people precomp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (thisComp.layer("DROPDOWN").effect("Dropdown Menu Control")("Menu") == 3) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4 people precomp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (thisComp.layer("DROPDOWN").effect("Dropdown Menu Control")("Menu") == 4) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this, our dropdown menu is ready to test. Drag it to the top of the Essential Graphics panel, outside each of the folders. One by one, select each option, to make sure it is working.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93scgdkcx8j0lvvyi0fy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93scgdkcx8j0lvvyi0fy.gif" alt="GIF cycling through the dropdown menu" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Text Options&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now that our basic template is in place, we need to consider what other options need to be available for the end user. For instance, if the goal is to prevent the user from going into precomps for text, we should add an option for them to change things like text size. What if our name is too long at the current font size?&lt;/p&gt;

&lt;p&gt;I've opted to change the text size across all of my precomps with one slider, so the text size stays consistent across the layout. However, if you want to have separate controls for each speaker, you can do so by repeating the following actions for each precomp separately, instead of inside the main comp.&lt;/p&gt;

&lt;p&gt;First, I make a null object in my main comp and name it "FONT SIZE OPTIONS." Here, I add 4 sliders: &lt;code&gt;Name Font Size&lt;/code&gt;, &lt;code&gt;Title Font Size&lt;/code&gt;, &lt;code&gt;Padding&lt;/code&gt; and &lt;code&gt;Position Adjust&lt;/code&gt;. I then lock my effects panel, so I can still access these sliders while I'm inside the other layer precomps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59v8c394wr4kb0guaw10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F59v8c394wr4kb0guaw10.png" alt="Image of Effects Controls panel with 4 sliders inside" width="391" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, I head inside my first precomp. Here, I'll add expressions to allow the end user to change the text size of both the name and job layers, as well as the padding between these layers, and the overall position in relation to the display image.&lt;/p&gt;

&lt;p&gt;To begin, I turn my attention to the Name layer. To fix the anchor point of this layer, you can use this simple expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var copy = thisLayer;

var cWidth = copy.sourceRectAtTime().width;
var cHeight = copy.sourceRectAtTime().height;
var cTop = copy.sourceRectAtTime().top;
var cLeft = copy.sourceRectAtTime().left;

var x = cLeft + cWidth / 2;
var y = cTop;

[x, y]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expression &lt;code&gt;sourceRectAtTime()&lt;/code&gt; works out the size of a layer at any given time. This means that as the text changes, After Effects is able to recalculate the size of the layer. Using this, we are able to work out the layer's width, height, top and left parameters.&lt;/p&gt;

&lt;p&gt;Since I would like my anchor point to sit top-centre, my y coordinate = the layer's &lt;code&gt;sourceRectAtTime().top&lt;/code&gt; value. My x coordinate = the sum of the left and width, divided by 2 to find the middle.&lt;/p&gt;

&lt;p&gt;You may have to move your layer back into position after changing the anchor point's location, but once you're done, the anchor point will always sit in the top-centre of the layer, no matter the layer's size.&lt;/p&gt;

&lt;p&gt;Speaking of size, we're now ready to add an expression to affect the layer's font size. In the layer's Source Text property, we can use this expression to do so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;textSize = comp("MAIN COMP").layer("FONT SIZE OPTIONS").effect("Name Font Size")("Slider")
text.sourceText.style.setFontSize(textSize)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we reference the slider from our main comp labelled "Name Font Size", so that this layer knows what to look for. Then, we use the &lt;code&gt;sourceText.style&lt;/code&gt; expression to set the font size to match the number on this slider.&lt;/p&gt;

&lt;p&gt;Now that the name layer is complete, we can do the same again for the Title text layer. Copy and paste the same expression inside the anchor point property, and the Source Text property, changing the slider effect in the latter to "Title Font Size" so it has its own unique control.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;textSize = comp("MAIN COMP").layer("FONT SIZE OPTIONS").effect("Title Font Size")("Slider")
text.sourceText.style.setFontSize(textSize)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One final trick we need to set up is the position of both of these layers. Ideally, I would like to control the padding between these layers, and the position overall in case this needs a bit of tweaking. We can do this with some simple expressions in the position properties.&lt;/p&gt;

&lt;p&gt;First, we can use this inside the Name layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;y = value[1] + comp("MAIN COMP").layer("FONT SIZE OPTIONS").effect("Position Adjust")("Slider");

[value[0], y]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To reference the "Position Adjust" slider in our main comp. This simply adds the value of that slider to this layer's position, allowing us to move it up or down.&lt;/p&gt;

&lt;p&gt;For the title layer, to ensure the padding changes along with the name font size, we need to be a little more clever. First, parent the title layer to the Name layer. Then, we write this inside the position property:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nameLayer = thisComp.layer("Name Copy 01");

nWidth = nameLayer.sourceRectAtTime().width;
nHeight = nameLayer.sourceRectAtTime().height;
nTop = nameLayer.sourceRectAtTime().top;
nLeft = nameLayer.sourceRectAtTime().left;

padding = nHeight / 1.7;
paddingSlider = comp("MAIN COMP").layer("FONT SIZE OPTIONS").effect("Padding")("Slider")
paddingNew = padding * paddingSlider;

x = value[0];
y = nTop + nHeight + paddingNew;

[x, y]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we create variables to store the data from our name layer's &lt;code&gt;sourceRectAtTime()&lt;/code&gt; values in our Title layer's position property. This will allow us to change position based on the size of the Name layer. Next, we work out our padding. The space between my text = the height of the name layer divided by 1.7. Yours may differ, so feel free to change the number 1.7 for something that is more suitable to your template.&lt;/p&gt;

&lt;p&gt;I then reference my main comp slider "Padding", and multiply the value by my padding variable. This will allow me to change the padding value by a percentage, rather than addition, keeping the spacing consistent across text sizes.&lt;/p&gt;

&lt;p&gt;Lastly, I establish my x and y values. Since I don't want there to be any adjustment in the x value, I simply keep it &lt;code&gt;value[0]&lt;/code&gt;. The y coordinate however should equal the sum of the name layer's top and height values, plus the variable &lt;code&gt;paddingNew&lt;/code&gt;, the percentage change to our default padding.&lt;/p&gt;

&lt;p&gt;Phew! That was a lot of calculations. But after all that, we can return to our main comp and see whether or not it works. Drag each slider into the Essential Graphics panel, and then change the values one by one in order to test them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc0re2374a8sywch3ck7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc0re2374a8sywch3ck7.gif" alt="GIF testing out the new text size sliders" width="1260" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works! Now that we're happy with these, we can go ahead and copy and paste the expressions in the rest of the people precomps. Remember to update the expressions with the correct precomp layer names.&lt;/p&gt;

&lt;p&gt;Once you have copied the expressions to all of the precomps, you'll be able to resize them all so they are uniform with adaptive leading between your text layers. Useful when you're handing the template over to someone who may not have a designer's eye!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frp4i7ebm086mjbhkxd6z.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frp4i7ebm086mjbhkxd6z.gif" alt="GIF adjusting the Name font size across all 4 people" width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can just as easily have the title font size be adaptive based on the name layer size too, as well as the leading. In fact, with hindsight, this is probably what I would want to do to prevent overwhelming the end user with options. Providing the user with just a "Name Font Size" option (and perhaps an "overall position adjustment") would be enough to change everything proportionately!&lt;/p&gt;

&lt;p&gt;For an adaptive Title Font size, simply use this expression inside of the Title Source Text property instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nameTextSize = comp("MAIN COMP").layer("FONT SIZE OPTIONS").effect("Name Font Size")("Slider");
textSize = (nameTextSize/15)*9;
textSizeSlider = comp("MAIN COMP").layer("FONT SIZE OPTIONS").effect("Title Font Size")("Slider")
textValue = textSize * textSizeSlider;


text.sourceText.style.setFontSize(textValue)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ratio between my Name text and Title text in this template is 15:9, which is why I divide the Name Font Size slider by 15, and then multiply it by 9. If your ratio is different, simply change these values to suit your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdr53yvoow1niwgxcyxf7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdr53yvoow1niwgxcyxf7.gif" alt="GIF of the text adjusting with a slider, with the title text size adapting to the name text size" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing Fill Options&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Depending on your template, you may find you need a variety of other options for your template. One of the main ones I encounter is colour options. There are 2 ways which I tackle this in templates.&lt;/p&gt;

&lt;p&gt;The first is simple, and allows the user to pick whatever colour they desire. Let's say we would like to change the colour of the circles in my template. Simply navigate to this layer in your project, and add a Fill effect. Then, drag that fill effect into the essential graphics panel.&lt;/p&gt;

&lt;p&gt;Then from the main comp, you can change the colour of the circle to any other colour.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggpy0zu0v1rvst6ft96x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggpy0zu0v1rvst6ft96x.png" alt="Image of the fill switch inside the Essential Graphics Panel" width="509" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7skiepcbziwm1tr4l8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu7skiepcbziwm1tr4l8l.png" alt="Image of the fill switch and the first circle changing to the colour blue" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This method however may not be suitable for all users. What if the end user doesn't know the colour codes to use for their brand? When colours need to be matched completely, it's best to give the user less options. I achieve this by combining a Color Control effect with another dropdown menu.&lt;/p&gt;

&lt;p&gt;Create another null and add a Color Control effect to it. Name it "Font Colour". We will use this to change the colours of all the fonts in our template.&lt;/p&gt;

&lt;p&gt;In turn, go into each precomp and add a fill effect to each text layer. Then, inside the fill effect, write an expression referencing the Color Control effect in the main comp:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;comp("MAIN COMP").layer("COLOUR CONTROL").effect("FONT COLOUR")("Color")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do this to every text layer in every precomp. Once you are done, return to the main precomp and create a new dropdown effect. Name it "Colour Dropdown". Edit the dropdown to have as many options as you need colours. In my case, I am creating a purple, pink and black option.&lt;/p&gt;

&lt;p&gt;Drag your dropdown menu into your Essential Graphics panel. After this, we can add our expression to our Color Control effect.&lt;/p&gt;

&lt;p&gt;After Effects references colour with 4 arguments: Red, Green, Blue and Alpha. So we will need to create variables for each colour we plan to make.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Purple
r1 = 102 / 255;
g1 = 36 / 255;
b1 = 131 / 255;

//pink
r2 = 240 / 255;
g2 = 82 / 255;
b2 = 118 / 255;

//black
r3 = 0;
g3 = 0;
b3 = 0;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the Color Control effect, I create my RGB variables for each colour. I divide each entry by 255, since this is how After Effects processes colours for expressions.&lt;/p&gt;

&lt;p&gt;Once this is done, I need to add a variable for my dropdown menu, and create a final &lt;code&gt;if&lt;/code&gt; statement to connect all the variables to the dropdown menu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dropdown = thisComp.layer("DROPDOWN").effect("Colour Dropdown")("Menu");

//Purple
r1 = 102 / 255;
g1 = 36 / 255;
b1 = 131 / 255;

//pink
r2 = 240 / 255;
g2 = 82 / 255;
b2 = 118 / 255;

//black
r3 = 0;
g3 = 0;
b3 = 0;


if (dropdown == 1) [r1, g1, b1, 1]
    else if (dropdown == 2) [r2, g2, b2, 1]
    else [r3, g3, b3, 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the menu has the first option selected, it will display the first colour (in this case, purple). If it has the second selected, it will display the second colour. If any other option is selected, it will display the third colour.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0c1g2nxxf84w7s73y1mm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0c1g2nxxf84w7s73y1mm.gif" alt="GIF of the text changing colour with a dropdown" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This method is a little more technical, but overall means that the user can never select a colour they are not supposed as part of the template.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Of course, it's important to know the limitations of every template. After all, we should remember a template can never be all inclusive - some things we just have to do manually. But understanding &lt;em&gt;why&lt;/em&gt; that’s the case goes a long way, and can help you explain to clients and coworkers quality work takes time, and cannot simply be automated.&lt;/p&gt;

&lt;p&gt;In terms of the limitations I encountered in my project - sometimes you need to be mindful of using the text animator in conjunction with the fixed anchor point expression. Because of the way &lt;code&gt;sourceRecAtTime&lt;/code&gt; works, you may find your layer moves around while the text animator is in motion. I have gotten around this by duplicating my text layer and doing the following:&lt;/p&gt;

&lt;p&gt;Paste the anchor point expression in layer 1. Then, parent layer 2 to layer 1 (without the anchor point expression). Your text animator motion goes on layer 2. Connect the source text property of layer 1 to layer 2, so layer 1's text automatically updates when layer 2's source text is changed. Finally, turn off layer 1 so it is not visible. Layer 1 will constantly be centred and updated with the same text as layer 2 - but layer 2 will have the added bonus of the Text Animator and be moved thanks to the parenting.&lt;/p&gt;

&lt;p&gt;One other limitation to consider when making your template is its longevity. No one wants to make a template that is old news before a project is finished (or within a month for those ongoing projects). So it's important to consider the template's functionality. Will you, in the future, need more options than you have right now? If you know this to be the case, you should consider what will be the easiest course of action to update your templates later. Build your projects so that they work for you - not the other way around!&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Ideas&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;You can go further with your projects than I did in this article. Here are some ideas:&lt;/p&gt;

&lt;p&gt;You can connect data in your project with .csv files for your clients to use and update, should they understand spreadsheets better than After Effects. I've gone through the process in my &lt;a href="https://dev.to/kocreative/project-spreadsheet-to-video-kinetic-text-4ad8"&gt;Kinetic Text project here&lt;/a&gt;, for more information on how to achieve this.&lt;/p&gt;

&lt;p&gt;Your template may need to change duration length depending on your options. This is something we can accomplish with on and off animations, &lt;code&gt;if&lt;/code&gt; statements, and a slider. You can find examples of how I tackle on and off animations in my &lt;a href="https://dev.to/kocreative/after-effects-favourite-expressions-part-1-linear-and-ease-14p1"&gt;&lt;code&gt;linear&lt;/code&gt; and &lt;code&gt;ease&lt;/code&gt; article&lt;/a&gt;, and work it out from there.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion&lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;In conclusion, by deploying the Essential Graphics panel we can create much more responsive, easier to use After Effects templates than ever before.&lt;/p&gt;

&lt;p&gt;Did this article help? Do you have any questions? Let me know in the comments.&lt;/p&gt;

</description>
      <category>aftereffects</category>
      <category>tutorial</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>After Effects: While Loops</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Thu, 10 Oct 2024 12:22:05 +0000</pubDate>
      <link>https://dev.to/kocreative/after-effects-while-loops-447e</link>
      <guid>https://dev.to/kocreative/after-effects-while-loops-447e</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;while&lt;/code&gt; Loops Vs &lt;code&gt;if&lt;/code&gt; Statements&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;while&lt;/code&gt; Loops Vs &lt;code&gt;for&lt;/code&gt; Loops&lt;/li&gt;
&lt;li&gt;When To Use &lt;code&gt;while&lt;/code&gt; Loops&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Full disclosure: I hate &lt;code&gt;while&lt;/code&gt; loops. It has taken me a long time to get my head around them, but I think I finally understand their purpose.&lt;/p&gt;

&lt;p&gt;Starting out, everytime I tried to write my own &lt;code&gt;while&lt;/code&gt; loop, I would crash After Effects immediately. Now that I have a bit more knowledge on what went wrong, I want to write up what a &lt;code&gt;while&lt;/code&gt; loop is, when to use it, and how you can incorporate it in your After Effects expressions without crashing your project (remember to save regularly, folks).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;while&lt;/code&gt; Loops Vs &lt;code&gt;if&lt;/code&gt; Statements&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;while&lt;/code&gt; loop, like the name suggests, is a loop which executes as long as the expression is true.&lt;/p&gt;

&lt;p&gt;"Executes as long as the expression is true" - that sounds a little bit like an &lt;code&gt;if&lt;/code&gt; statement, doesn't it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (time &amp;lt; 2) "Text On"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you add this expression to the &lt;code&gt;Source Text&lt;/code&gt; property of a text layer, you will find that the text reads "Text On" for the first 2 seconds of your timeline, and displays no text after that time. An &lt;code&gt;if&lt;/code&gt; statement checks whether or not an expression is true, and if it is, it executes the command &lt;strong&gt;once&lt;/strong&gt;, and only once.&lt;/p&gt;

&lt;p&gt;However, if you changed it to &lt;em&gt;this&lt;/em&gt; expression, you would find that After Effects crashes almost immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;while (time &amp;lt; 2) "Text On"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because the &lt;code&gt;while&lt;/code&gt; loop &lt;em&gt;is a loop&lt;/em&gt;, so it executes the command &lt;strong&gt;repeatedly&lt;/strong&gt; if the statement is true. As long as your cursor is in the first 2 seconds of your timeline, the &lt;code&gt;while&lt;/code&gt; loop will loop &lt;strong&gt;infinitely&lt;/strong&gt;. Not exactly ideal, and certainly not what we were trying to achieve! It is important to make sure that your &lt;code&gt;while&lt;/code&gt; loop will, eventually, become false, to avoid these infinity loops.&lt;/p&gt;

&lt;p&gt;For more on &lt;code&gt;if&lt;/code&gt; statements, see my &lt;a href="https://dev.to/kocreative/after-effects-favourite-expressions-part-3-the-if-statement-58nl"&gt;previous article here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;while&lt;/code&gt; Loops Vs &lt;code&gt;for&lt;/code&gt; Loops&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Okay, so it's important to note not to use &lt;code&gt;while&lt;/code&gt; loops like &lt;code&gt;if&lt;/code&gt; statements. But there are other types of loops we can use to make expressions. How does a &lt;code&gt;while&lt;/code&gt; loop differ from a &lt;code&gt;for&lt;/code&gt; loop, for instance?&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;for&lt;/code&gt; loop works with 3 arguments, and is ideal when you &lt;strong&gt;know how many times you need to loop your commands&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Take this example, generating random numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//variables
let counter = 0;
let num = 0;
let numArray = [];

//For loop
for (let i = 0; time &amp;gt;= i/2; i++) {
    seedRandom(counter, timeless = true);
    num = Math.floor(random(1, 6));
    numArray.push(num);
    counter++
}

//return
numArray[counter - 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After setting up my variables, the loop starts with &lt;code&gt;let i = 0&lt;/code&gt;, setting the first argument which executes &lt;em&gt;once&lt;/em&gt;. Then, &lt;code&gt;time &amp;gt;= i/2&lt;/code&gt; establishes how often the loop should execute. Setting the loop this way will mean the loop executes twice every second, because &lt;code&gt;time&lt;/code&gt; needs to be more than or equal to half of &lt;code&gt;i&lt;/code&gt;. Finally, the third argument &lt;code&gt;i++&lt;/code&gt; will run &lt;em&gt;everytime&lt;/em&gt; after the loop is executed. In this case, the value of &lt;code&gt;i&lt;/code&gt; increases by 1.&lt;/p&gt;

&lt;p&gt;Running this expression again in the &lt;code&gt;Source Text&lt;/code&gt; property of a text layer will display a random number between 1 and 5 twice every second.&lt;/p&gt;

&lt;p&gt;For more information on &lt;code&gt;for&lt;/code&gt; loops, &lt;a href="https://dev.to/kocreative/after-effects-favourite-expressions-part-4-for-loops-18cp"&gt;see my previous article here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;while&lt;/code&gt; loop is best used when you &lt;strong&gt;do not know how many times your loop will need to execute.&lt;/strong&gt; Because of this, I've found that they don't work well with expressions which involve &lt;code&gt;time&lt;/code&gt; like &lt;code&gt;for&lt;/code&gt; loops do so well, as that traps you in those pesky infinite loops.&lt;/p&gt;

&lt;p&gt;So when &lt;em&gt;should&lt;/em&gt; we use &lt;code&gt;while&lt;/code&gt; loops?&lt;/p&gt;

&lt;h2&gt;
  
  
  When To Use &lt;code&gt;while&lt;/code&gt; Loops&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Keeping all of this in mind, a &lt;code&gt;while&lt;/code&gt; loop should be used in these conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We need to execute the expression more than once&lt;/li&gt;
&lt;li&gt;We do not know how many times we need to execute our loop&lt;/li&gt;
&lt;li&gt;We are able to break the loop, to avoid it executing to infinity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I recently found a use for this within After Effects, while working on my &lt;a href="https://dev.to/kocreative/project-random-number-generator-but-excluding-the-previous-entry-22ch"&gt;random number generator&lt;/a&gt; project.&lt;/p&gt;

&lt;p&gt;Let's look at the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//variables
let counter = 0;
let num = 0;
let numArray = [];

//For loop
for (let i = 0; time &amp;gt;= i/2; i++) {
    seedRandom(counter, timeless = true);
    num = Math.floor(random(1, 6));

//While loop inside of For loop
        while (num == numArray[counter - 1]) {
        num = Math.floor(random(1, 6));
        }

    numArray.push(num);
    counter++
}

//return
numArray[counter - 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this instance, the &lt;code&gt;while&lt;/code&gt; loop is checking to see if the current value of &lt;code&gt;num&lt;/code&gt; equals the previous value, stored in the &lt;code&gt;numArray&lt;/code&gt; array. If it does, it generates a new random number between 1 and 5. The loop will execute as long as &lt;code&gt;num&lt;/code&gt; and the previous number stored in the array are the same. However, once the numbers do not match, the &lt;code&gt;while&lt;/code&gt; loop ends, and the expression is free to continue to the next line of code.&lt;/p&gt;

&lt;p&gt;Because I &lt;strong&gt;may have to generate a new random number more than once&lt;/strong&gt;, an &lt;code&gt;if&lt;/code&gt; statement doesn't work here.&lt;/p&gt;

&lt;p&gt;And because I &lt;strong&gt;don't know how many times I will need to refresh the random number&lt;/strong&gt;, the number of loops is also a mystery: so a &lt;code&gt;for&lt;/code&gt; loop is no good here either.&lt;/p&gt;

&lt;p&gt;Therefore a &lt;code&gt;while&lt;/code&gt; loop is my best option. It will execute as many times as I need it to, &lt;strong&gt;until the argument inside it is false&lt;/strong&gt;, which will certainly happen as long as the &lt;code&gt;random()&lt;/code&gt; function generates a number which does not match the previous one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In conclusion, &lt;code&gt;while&lt;/code&gt; loops are likely to be an uncommon tool in your After Effects expression belt. However they are useful to learn, for instances where the usual suspects may not be suitable.&lt;/p&gt;

&lt;p&gt;Did you find this helpful? Do you have an example of using a &lt;code&gt;while&lt;/code&gt; loop in your project? Did I get something wrong? Please leave me a comment and let me know.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>aftereffects</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Project: Random Number Generator But Excluding The Previous Entry</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Mon, 07 Oct 2024 16:59:49 +0000</pubDate>
      <link>https://dev.to/kocreative/project-random-number-generator-but-excluding-the-previous-entry-22ch</link>
      <guid>https://dev.to/kocreative/project-random-number-generator-but-excluding-the-previous-entry-22ch</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Introduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;random&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Creating An Array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;while&lt;/code&gt; Loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Applying It To Things Other Than Numbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Introduction&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Sometimes you just need a random number. But not &lt;em&gt;that&lt;/em&gt; random. I find that, when having numbers flash randomly on the screen for a video effect, that it works best when there are no repeats in the sequence. This can happen from time to time, especially if your range isn't that large, when using the &lt;code&gt;random()&lt;/code&gt; function. While it is perfectly understandable for what the function does, it still makes our animation look a little staggered.&lt;/p&gt;

&lt;p&gt;So I set myself the challenge of writing an expression that excludes the previous number from the random generation, and thought about how this could be applied to projects in the future.&lt;/p&gt;

&lt;p&gt;Here's what I did.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;random()&lt;/code&gt;&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Let's start at the beginning. If you need a random number in After Effects, using the &lt;code&gt;random(minVal, maxVal)&lt;/code&gt; function is a great place to start. By inputting a minimum and maximum value into the function, After Effects will generate a random number from within those bounds every frame.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;random(1, 11)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2rfitardcz8mnax5iid.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2rfitardcz8mnax5iid.png" alt="image showing the number the random function produced" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's a lot of decimal places! Since I just want integers, I use &lt;code&gt;Math.floor&lt;/code&gt; to round the numbers down. Because the range of the &lt;code&gt;random&lt;/code&gt; function is 1 to 11, this method will produce a random number between 1 and 10, since the top of the range is not included.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Math.floor(random(1, 11))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdnqp9z66cyryv7u1zgki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdnqp9z66cyryv7u1zgki.png" alt="Image showing the number of the random function wrapped inside the math.floor function" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Much better. However I want to control how often the number changes, not on every frame. To do that, we need to use the &lt;code&gt;seedRandom(seed, timeless?)&lt;/code&gt; function. This function requires 2 arguments, the seed, and whether or not timeless mode is set to true or false. By default, timeless is set to false, meaning that the &lt;code&gt;random&lt;/code&gt; function will generate a new number each frame. However, if we set this to true, the &lt;code&gt;random&lt;/code&gt; function will only generate a number once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;seedRandom(0, timeless = true);
Math.floor(random(1, 11))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that the number only generates once, I can manipulate the seed number using a simple &lt;code&gt;for&lt;/code&gt; loop to control how often the number generates. If you're unfamiliar with &lt;code&gt;for&lt;/code&gt; loops &lt;a href="https://dev.to/kocreative/after-effects-favourite-expressions-part-4-for-loops-18cp"&gt;I've written up an article about them in depth here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is my basic loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let counter = 0;
let num = 0;

for (i = 0; time &amp;gt;= i; i++) {
    seedRandom(counter, timeless = true);
    num = Math.floor(random(1, 11));
    counter++
}

num
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, I set up the variables &lt;code&gt;counter&lt;/code&gt; to track the changes in time, and &lt;code&gt;num&lt;/code&gt;, to store my random number. The loop is set to add 1 to &lt;code&gt;i&lt;/code&gt; whenever &lt;code&gt;time &amp;gt;= i&lt;/code&gt;, meaning the loop updates every second the timeline is active. Everytime the loop refreshes, &lt;code&gt;seedRandom&lt;/code&gt; is updated by the change in &lt;code&gt;counter&lt;/code&gt;'s value, producing a new random number when &lt;code&gt;num&lt;/code&gt; is called at the end of the expression.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt9bjohmh5p2whx9gqg8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt9bjohmh5p2whx9gqg8.gif" alt="GIF of numbers changing every second" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By adjusting &lt;code&gt;i&lt;/code&gt; in the &lt;code&gt;for&lt;/code&gt; loop, we can control how often the number changes per second. For instance, if we were to change the argument to &lt;code&gt;time &amp;gt;= i/2&lt;/code&gt;, the number will change twice every second instead of once.&lt;/p&gt;

&lt;p&gt;Now that we have an expression which allows us to generate a new random number at an interval of our choosing, it needs to be amended to ensure we never have any duplicate entries one after another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating An Array&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In order for After Effects to be able to compare numbers, the numbers need to be saved somewhere for After Effects to review. So I decided to save my random numbers to an array as they are generated over time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let counter = 0;
let num = 0;
let allNum = [];

for (i = 0; time &amp;gt;= i/2; i++) {
    seedRandom(counter, timeless = true);
    num = Math.floor(random(1, 11));
    allNum.push(num);
    counter++
}

allNum
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I create the variable &lt;code&gt;allNum&lt;/code&gt; outside of my &lt;code&gt;for&lt;/code&gt; loop, and then &lt;code&gt;push()&lt;/code&gt; the number currently inside of our &lt;code&gt;num&lt;/code&gt; variable to the array. This saves each number as it is generated across the timeline.&lt;/p&gt;

&lt;p&gt;By calling &lt;code&gt;allNum&lt;/code&gt; at the end, we are able to see all the numbers in our array by the end of our timeline:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4mrjve48g72khl2ebqxx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4mrjve48g72khl2ebqxx.png" alt="Image of all numbers in the random array" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, this is less than ideal with a set of double 7s and triple 1s in our sequence. If we called &lt;code&gt;num&lt;/code&gt; and played our timeline, the doubles could make it look as if our timeline had frozen or stopped animating. Therefore, we need to ensure that our expression takes into account the previous entry of this array, and excludes it from being added again in such quick succession.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;while&lt;/code&gt; Loop&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I found the answer to this was to add a &lt;code&gt;while&lt;/code&gt; loop within the existing &lt;code&gt;for&lt;/code&gt; loop.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;while&lt;/code&gt; loop allows you to specify a command while certain criteria is true. In this case, we want it to be active while the current value of &lt;code&gt;num&lt;/code&gt; matches the previous value, listed in our &lt;code&gt;allNum&lt;/code&gt; array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;while (num == allNum[counter - 1]) num = Math.floor(random(1, 11));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the current value of &lt;code&gt;num&lt;/code&gt; matches the previous entry inside of &lt;code&gt;allNum&lt;/code&gt;, the &lt;code&gt;while&lt;/code&gt; loop will generate a new random number using a new iteration of &lt;code&gt;Math.floor(random(1, 11))&lt;/code&gt;. This loop remains active as long as the numbers stay the same. Therefore, until a different number is generated, the &lt;code&gt;while&lt;/code&gt; loop will prevent the same number from being added to the &lt;code&gt;allNum&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;The while loop is inserted into our &lt;code&gt;for&lt;/code&gt; loop like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let counter = 0;
let num = 0;
let allNum = [];

for (i = 0; time &amp;gt;= i/2; i++) {
    seedRandom(counter, timeless = true);
    num = Math.floor(random(1, 11));
    while (num == allNum[counter - 1]) num = Math.floor(random(1, 11));
    allNum.push(num);
    counter++
}

allNum
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is added after &lt;code&gt;num&lt;/code&gt; has attempted to generate a new number with the update to &lt;code&gt;seedRandom&lt;/code&gt;, but before it is pushed to the array so it can be re-generated if necessary.&lt;/p&gt;

&lt;p&gt;With this amend, our array now looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqm6q6bo1dkgpjyuoxgwi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqm6q6bo1dkgpjyuoxgwi.png" alt="Image of updated array" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, we no longer have double 7s or triple 1s in our sequence.&lt;/p&gt;

&lt;p&gt;Therefore, if we call &lt;code&gt;num&lt;/code&gt; at the end of our expression instead of &lt;code&gt;allNum&lt;/code&gt;, we will have a random number between 1 and 10, excluding the previous entry generated, twice every second. Hooray!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3225zrh3wclaqzv8ig3c.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3225zrh3wclaqzv8ig3c.gif" alt="GIF of random number generator in action" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying It To Things Other Than Numbers&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;While random numbers are all well and good, there are other uses for this expression too. For instance, what if we wanted to randomise words on the screen instead, without duplicate repeats?&lt;/p&gt;

&lt;p&gt;All we need to do is add another array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
var ranText = ["Banana", "Apple", "Pear", "Peach", "Pulm", "Orange", "Mango", "Lychee", "Pinapple", "Dragonfruit"];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an array housing the text we want randomised. There are 10 entries, matching the range of the number generator we made previously.&lt;/p&gt;

&lt;p&gt;By calling an entry of our new array using our &lt;code&gt;num&lt;/code&gt; variable, we can call the entry whose index matches that number:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
//Text
var ranText = ["Banana", "Apple", "Pear", "Peach", "Pulm", "Orange", "Mango", "Lychee", "Pinapple", "Dragonfruit"];

//variables
let counter = 0;
let num = 0;
let allNum = [];

//For loop
for (i = 0; time &amp;gt;= i/2; i++) {
    seedRandom(counter, timeless = true);
    num = Math.floor(random(1, 11));
    while (num == allNum[counter - 1]) num = Math.floor(random(1, 11));
    allNum.push(num);
    counter++
}

//return
ranText[num - 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo0o8dn3ursdga4kr12c.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo0o8dn3ursdga4kr12c.gif" alt="GIF of random text generator" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Useful! Just remember to make sure that your &lt;code&gt;num&lt;/code&gt; variable at the end includes &lt;code&gt;-1&lt;/code&gt;, to account for the array index starting at 0.&lt;/p&gt;

&lt;p&gt;Instead of using an array, it is possible to connect your expression to data inside of a .csv file. I go into how this is possible in my &lt;a href="https://dev.to/kocreative/project-spreadsheet-to-video-kinetic-text-4ad8"&gt;kinetic text breakdown here&lt;/a&gt;, should you want to make a template that is constantly updatable.&lt;/p&gt;

&lt;p&gt;However you can use it in other ways too. Images can be randomly displayed as well, by connecting this expression to a slider controlling the frames of a precomp.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In conclusion, it is possible to add a little bit more control over random numbers in order to make them look a little more aesthetically pleasing to the eye.&lt;/p&gt;

&lt;p&gt;What did you think of this method? Is there an easier way? Did this help you with your project in any way? Please leave a comment and let me know.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>aftereffects</category>
      <category>adobe</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Project: Spreadsheet To Video, Kinetic Text</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Tue, 13 Aug 2024 09:38:33 +0000</pubDate>
      <link>https://dev.to/kocreative/project-spreadsheet-to-video-kinetic-text-4ad8</link>
      <guid>https://dev.to/kocreative/project-spreadsheet-to-video-kinetic-text-4ad8</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Initial Testing With Arrays&lt;/li&gt;
&lt;li&gt;Setting Up My Spreadsheet&lt;/li&gt;
&lt;li&gt;Using Data From The .CSV File&lt;/li&gt;
&lt;li&gt;Adding Line Breaks&lt;/li&gt;
&lt;li&gt;Aligning The Text&lt;/li&gt;
&lt;li&gt;Text Size&lt;/li&gt;
&lt;li&gt;Using The Essential Graphics Panel&lt;/li&gt;
&lt;li&gt;Animation: Opacity&lt;/li&gt;
&lt;li&gt;Animation: Position&lt;/li&gt;
&lt;li&gt;Animation: Scale&lt;/li&gt;
&lt;li&gt;Animation: Other Ideas&lt;/li&gt;
&lt;li&gt;Limitations&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Recently I set myself the target of writing expressions for After Effects which allow me to animate text, song lyrics, or captions, using a spreadsheet to dictate the copy and timings. Here is how I achieved this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Testing With Arrays&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Before creating my .csv file, I wanted to work with some arrays to ensure I was able to get my expression working the way I wanted. In the "Source Text" parameter of a text layer, I create my arrays:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var textCopy = ["Apple", "Banana", "Strawberry", "Orange", "Peach"];
var textDuration = [.5, 1, .5, 1, 1];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;textCopy&lt;/code&gt; is the text I want to see on screen. Meanwhile, &lt;code&gt;textDuration&lt;/code&gt; is the length of time a line is on screen, in seconds. My idea was to look at the textDuration entries and cross reference them with &lt;code&gt;time&lt;/code&gt;. Once &lt;code&gt;time&lt;/code&gt; = first number in the &lt;code&gt;textDuration&lt;/code&gt; array, After Effects would add the second entry to work out the total duration. Then, once &lt;code&gt;time&lt;/code&gt; = first entry + second entry, the next number in the array would be added, and so on, until all the numbers in the array were added together. For this, I used &lt;code&gt;for&lt;/code&gt; loops. The first &lt;code&gt;for&lt;/code&gt; loop was created to calculate the &lt;code&gt;totalDuration&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var sum = 0;
var totalDuration = [];

for (i = 0; i &amp;lt; textDuration.length; i++) {
    sum = sum + textDuration[i];
    totalDuration.push(sum);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this loop, the &lt;code&gt;textDuration&lt;/code&gt; array entries are added together one by one, using the &lt;code&gt;sum&lt;/code&gt; variable to store them in temporarily, and then pushed to the &lt;code&gt;totalDuration&lt;/code&gt; array before a new number is added. The loop continues until it cycles through all the numbers in the &lt;code&gt;textDuration&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;I then create a second &lt;code&gt;for&lt;/code&gt; loop, using the new &lt;code&gt;totalDurations&lt;/code&gt; array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var entry = 0;
for (i = 0; time &amp;gt;= totalDuration[i] &amp;amp;&amp;amp; time &amp;lt; totalDuration[totalDuration.length-1]; i++){
    if (true) entry++;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this loop, I'm telling After Effects to loop only when &lt;code&gt;time&lt;/code&gt; is &lt;em&gt;equal to or bigger&lt;/em&gt; than the current &lt;code&gt;totalDuration&lt;/code&gt; entry, but &lt;em&gt;lower&lt;/em&gt; than the very last entry of the &lt;code&gt;totalDuration&lt;/code&gt; array. If both of these are true, it will add 1 to the &lt;code&gt;entry&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;Lastly, I create an &lt;code&gt;if&lt;/code&gt; statement telling After Effects what to display in the text layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (time &amp;lt; totalDuration[totalDuration.length-1])textCopy[entry]
    else "Thanks For Watching!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;time&lt;/code&gt; is less than the last entry in &lt;code&gt;totalDuration&lt;/code&gt;, it will cycle through the copy, using the &lt;code&gt;for&lt;/code&gt; loops to update the array index. However once it reaches the end, it will display "Thanks For Watching!", signifying the end of the loop. This is to stop any errors from appearing, if the timeline is longer than &lt;code&gt;totalDuration&lt;/code&gt;'s last value. If you simply prefer the text to disappear once it reaches the end, you can delete the text from between the quotes on the &lt;code&gt;else&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wq7pvs95dz5w1nfxbmx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5wq7pvs95dz5w1nfxbmx.gif" alt="array powered animation gif" width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that I had a proof of concept, I was ready to introduce my .csv file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up My Spreadsheet&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It was time to set up my spreadsheet. I decided I would need 3 columns: "Text", "Text Duration" and "Total Duration". The Total Duration column would mean I could lose the &lt;code&gt;for&lt;/code&gt; loop which added up the Text Durations and clean up my script.&lt;/p&gt;

&lt;p&gt;I use the Total Duration column to work out the value of my Text Duration, so I can simply scroll through my audio and note down the end of each of the lines. If at any point you want to include a pause, simply leave the Text column empty for that row, and update the Duration columns accordingly. At this point, the Line Duration column seems redundant now that I have a Total Duration column, but I've kept it in the spreadsheet for animation purposes later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wpgrsg7e0j32efu2j64.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wpgrsg7e0j32efu2j64.png" alt="speadsheet screen shot" width="585" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save the spreadsheet as a .csv file, so it can be imported into After Effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Data From The .CSV File&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Actually using data from the spreadsheet was a little more complicated than I had initially thought. My main hurdle was trying to figure out how to cycle through the rows. I wanted to make this script adaptable so it would work no matter the number of rows in the spreadsheet. Therefore, I needed a way to count the number of rows, to work out the total.&lt;/p&gt;

&lt;p&gt;First things first, I needed to reference the .csv file with the expression. I replace my arrays with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var data = footage("SPREADSHEET.csv");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SPREADSHEET.csv should be the name of your spreadsheet in the project panel.&lt;/p&gt;

&lt;p&gt;Now I was successfully telling After Effects to look at the information in the .csv file, it was time to start pulling information.&lt;/p&gt;

&lt;p&gt;But in order to do that, I needed to count the rows in the .csv file. I came up with the idea of turning the data from the .csv file into a string, splitting the string on every line break, then calculating the length to get the number of entries. However, I wasn't quite sure how to turn my .csv file data into a string from within an After Effects parameter. Luckily, thanks to &lt;a href="https://creativecow.net/forums/thread/csv-number-of-rows-adjust-automatically-in-afx/" rel="noopener noreferrer"&gt;this post&lt;/a&gt; on Creative Cow, I was able to find I could do what I needed using the &lt;code&gt;sourceText&lt;/code&gt; function. Like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var dataSplit = data.sourceText.split(/\n/);
var rows = dataSplit.length-2;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;sourceText&lt;/code&gt; function transforms the data in the .csv file into a string. Then, the &lt;code&gt;split&lt;/code&gt; function is separating that string line by line, searching for line breaks (represented by "\n"). We can measure how many times our string was split with the &lt;code&gt;length&lt;/code&gt; function. To calculate the number of rows correctly, we need to subtract 2 from this number: 1 to account for the headings on our spreadsheet, and 1 because our index starts at 0.&lt;/p&gt;

&lt;p&gt;Now that I had calculated the number of rows in the .csv file, I could go ahead and make my &lt;code&gt;for&lt;/code&gt; loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var entry = 0;
for (i = 0; i &amp;lt;= rows &amp;amp;&amp;amp; time &amp;gt;= data.dataValue([2, i]); i++){
    if (true) entry++
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similar to the initial tests, while the variable &lt;code&gt;entry&lt;/code&gt; is less than the variable &lt;code&gt;rows&lt;/code&gt;, and &lt;code&gt;time&lt;/code&gt; is more than the current total duration, &lt;code&gt;entry&lt;/code&gt;'s value increases by 1. This means when &lt;code&gt;time&lt;/code&gt; is less then the current total duration, After Effects will look to the next one, until it has cycled through all entries in the column.&lt;/p&gt;

&lt;p&gt;Finally, I needed to rewrite the last &lt;code&gt;if&lt;/code&gt; statement to complete the expression:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (time &amp;lt; data.dataValue([2, rows])) data.dataValue([0, entry])
    else "Thanks For Watching!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, like the tests, while &lt;code&gt;time&lt;/code&gt; is less than the very last value in the total duration column, the &lt;code&gt;for&lt;/code&gt; loop will provide the timings to tick through all instances of the text column in the spreadsheet. Then, once it has gone through all entries, it will finish by flashing up the message, "Thanks For Watching!"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0tn2w7slu0vr90ya3ei.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy0tn2w7slu0vr90ya3ei.gif" alt="speadsheet powered animation gif" width="800" height="428"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And there it is, the basic expression! Pasted into the &lt;code&gt;sourceText&lt;/code&gt; parameter of a text layer, it will allow the text to cut from one line to another, using timings specified in the .csv file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var data = footage("SPREADSHEET.csv");


//Number of rows
var dataSplit = data.sourceText.split(/\n/);
var rows = dataSplit.length-2;


//For loop 
var entry = 0;
for (i = 0; i &amp;lt;= rows &amp;amp;&amp;amp; time &amp;gt;= data.dataValue([2, i]); i++){
    if (true) entry++
};


//Return
if (time &amp;lt; data.dataValue([2, rows])) data.dataValue([0, entry])
    else "Thanks For Watching!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the basic concept which I set out to achieve. However, I wanted to see how far I could push things. So I thought about how I could format the text, and how I could create more interesting motion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Line Breaks&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The first thing I wanted to add was the ability to include line breaks in the copy. Since the expression relies on the line breaks in the .csv file to separate the entries, it would break the expression to include line breaks in the copy of the spreadsheet.&lt;/p&gt;

&lt;p&gt;The first work around I could think of was simply applying this code to a layer with a text box, rather than a simple text layer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffseogmbm54lgg8o2ksrc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffseogmbm54lgg8o2ksrc.png" alt="thanks for watching text box line break" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This works if you don't mind where your line breaks fall. However, I wanted an option which gave the user control of where to put their line breaks. So I settled on the idea that a symbol could be used in the spreadsheet, and replaced later by the expression.&lt;/p&gt;

&lt;p&gt;I decided to use an underscore. I added one to my spreadsheet as an example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbsvxau1xerr9coa84j3c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbsvxau1xerr9coa84j3c.png" alt="speadsheet screen shot underscore" width="571" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and updated my .csv file in After Effects. To finish, I added the &lt;code&gt;replace&lt;/code&gt; function to the end of the &lt;code&gt;dataValue&lt;/code&gt; in the final &lt;code&gt;if&lt;/code&gt; statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (time &amp;lt; data.dataValue([2, rows])) data.dataValue([0, entry]).replace(/_/g, "\n")
    else "Thanks For Watching!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the first argument, I placed an underscore between the slashes to specify the symbol I wanted to replace, and added the &lt;code&gt;g&lt;/code&gt; modifier (the global modifier) to replace all instances. The second argument is what the function is replacing it with (again, "/n" meaning a line break). So now the expression searches for "_" and replaces it with a line break.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmb0h9k9jyxkan4le5i0q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmb0h9k9jyxkan4le5i0q.png" alt="mango lychee line break" width="800" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because the intention of this expression is to stop the user from editing the code themselves, I felt that having the line breaks be specified in the spreadsheet was the best course of action.&lt;/p&gt;

&lt;p&gt;If you want to include spaces on either side of your symbol for better readability in your spreadsheet, you can:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;replace(/ _ /g, "\n")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you'd rather use a different symbol, simply change the underscore to whatever symbol works best for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aligning The Text&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now that I have line breaks, I have a new formatting issue: aligning my text. While After Effects provides horizontal alignment tools for text, it does not provide vertical alignment tools. Therefore, if our text drops onto extra lines, it will always drop "down" in space, and make our text look off center.&lt;/p&gt;

&lt;p&gt;To correct this, I will need to fix the anchor point of the text layer, so no matter the size of the layer, it calculates where it should be to keep the text formatted correctly. I will start with center alignment, or keeping the anchor point in the center of the text layer.&lt;/p&gt;

&lt;p&gt;This is fairly straightforward, and requires a simple expression written in the anchor point parameter of the layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var copy = thisLayer;

var cWidth = copy.sourceRectAtTime().width;
var cHeight = copy.sourceRectAtTime().height;
var cTop = copy.sourceRectAtTime().top;
var cLeft = copy.sourceRectAtTime().left;

var x = cLeft + cWidth / 2;
var y = cTop + cHeight /2;

[x, y]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the function &lt;code&gt;sourceRectAtTime&lt;/code&gt;, we are able to measure the size of the text layer. As the text changes, its width, height, top, and left values will change. We can find the center of the layer by creating an x and y variable. For the x, I add the left and width values to get the right side of the layer, then divide by 2. For the y, I add the top and the height values to get the bottom of the layer, then divide by 2. When these variables are entered as coordinates for our anchor point, they will always sit in the center of the layer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuectuav2aj9ukh1s469g.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuectuav2aj9ukh1s469g.gif" alt="scrub through titles centered gif" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is useful for full screen graphics, and caption style text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqxi7moophuhlavh13fmz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqxi7moophuhlavh13fmz.gif" alt="scrub through caption style gif" width="800" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you need the anchor point to be in a different place, you will need to adjust the equation. Such as:&lt;/p&gt;

&lt;p&gt;Top-Center:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = cLeft + cWidth / 2;
y = cTop;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bottom-Center:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = cLeft + cWidth / 2;
y = cTop + cHeight;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Top-Left:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x = cLeft;
y = cTop;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just remember: Top and left aligned only need the &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;left&lt;/code&gt; values, right and bottom aligned need the full &lt;code&gt;left + width&lt;/code&gt; and &lt;code&gt;top + height&lt;/code&gt; values, and center alignment need the full values to be divided by 2.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text Size&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Other formatting options I was considering was text size. What if I want different lines to be a different size text? Again, a simple way of adjusting this is with keyframes at every line change. However this is a manual, time consuming process. Also, I want to assume the user has very basic After Effects skills. So I wanted to connect the font size to the spreadsheet.&lt;/p&gt;

&lt;p&gt;I added another column for text size.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz1v0sr1kh6vxg4vif23.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz1v0sr1kh6vxg4vif23.png" alt="spreadsheet screen shot, text size" width="695" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once I saved a new .csv file, I knew I could cycle through these values in the same way I was cycling through the Text column. But what I needed to figure out was how to format the text. At first, I tried the usual method of formatting text in After Effects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;text.sourceText.style.setFontSize(data.dataValue([3, entry]))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since our new Font Size column is number 3 in our .csv file (because the first entry starts at 0), the number will change as the copy does. However, because &lt;code&gt;style&lt;/code&gt; references &lt;code&gt;sourceText&lt;/code&gt;, this does not work with the rest of our expression. If it is placed before our final if statement, it is ignored. If it is placed after, it supersedes the &lt;code&gt;if&lt;/code&gt; statement and reverts the text layer to its original source text.&lt;/p&gt;

&lt;p&gt;There was a simple fix however. All I had to do was &lt;code&gt;.setText&lt;/code&gt; using the data from the spreadsheet. This meant I could add the styling options to the final &lt;code&gt;if&lt;/code&gt; statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (time &amp;lt; data.dataValue([2, rows])) text.sourceText.style.setFontSize(data.dataValue([3, entry])).setText(data.dataValue([0, entry]).replace(/_/g, "\n"))
    else "Thanks For\nWatching!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it works! However, this changes the font size only. To update the leading, I will have to add that to the style options too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (time &amp;lt; data.dataValue([2, rows])) text.sourceText.style.setFontSize(data.dataValue([3, entry])).setLeading(data.dataValue([3, entry])).setText(data.dataValue([0, entry]).replace(/_/g, "\n"))
    else "Thanks For\nWatching!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've opted to set the leading to the same value as my font size. Now the font size will change along with the text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffy1rkz3v72vwh8xcm7mm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffy1rkz3v72vwh8xcm7mm.gif" alt="Image description" width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To alter the leading as needed, without editing the expression, I created a slider, to adjust a percentage of the Text Size column. A percentage rather than adding a number will keep the line spacing between different copy consistent.&lt;/p&gt;

&lt;p&gt;However, as you can tell, my final &lt;code&gt;if&lt;/code&gt; statement is becoming quite long. I needed to start storing information in variables to make this more manageable. The trouble was, if I took the &lt;code&gt;dataValue&lt;/code&gt;'s out of the &lt;code&gt;if&lt;/code&gt; statement, the &lt;code&gt;entry&lt;/code&gt; variable would cause an error, its last value ticking up 1 higher than the number of entries in the spreadsheet. This perplexed me for a long time, but the answer to solve this issue was obvious. I could simply &lt;code&gt;clamp&lt;/code&gt; the entry value to ensure it never went over the max number of &lt;code&gt;rows&lt;/code&gt;. Once I figured this out, I was able to make variables to store data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var maxDuration = data.dataValue([2, clamp(entry, 0, rows)]);
var copy = data.dataValue([0, clamp(entry, 0, rows)]);
var copySize = data.dataValue([3, clamp(entry, 0, rows)]);
var copyStyle = text.sourceText.style.setFontSize(copySize).setLeading(copySize);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Taking my &lt;code&gt;if&lt;/code&gt; statement down to a much more manageable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (time &amp;lt; maxDuration) copyStyle.setText(copy.replace(/_/g, "\n"))
    else "Thanks For\nWatching!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hooray! Now I'd tidied up my code, I turned my attention back to my slider. I created a new variable for my &lt;code&gt;copyLeading&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var copyLeading = (copySize/100) * effect("Leading Padding")("Slider");
var copyStyle = text.sourceText.style.setFontSize(copySize).setLeading(copySize + copyLeading);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6i8bp6x8o9snoobkx72p.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6i8bp6x8o9snoobkx72p.gif" alt="mango lychee leading gif" width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final thing to consider about text size - the sign off message is not affected by the styling, as it is not from the spreadsheet. So, if you want to have a sign off message, you need to account for that in the &lt;code&gt;else&lt;/code&gt; statement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var signOffStyle = text.sourceText.style.setFontSize(150).setLeading(150);

if (time &amp;lt; maxDuration) copyStyle.setText(copy.replace(/_/g, "\n"))
    else signOffStyle.setText("Thanks For\nWatching!")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I have just input the values, but this should also be connected to sliders to make the options editable for the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using The Essential Graphics Panel To Control Formatting Options&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now that sliders have been introduced to the project, I need to make sure the user is able to change their value without being confused. For this, I like to set up my &lt;strong&gt;Essential Graphics&lt;/strong&gt; panel. This panel is fairly new, and is used to create .mogrt files (motion graphics templates) for use in Premiere. However, I think it also does a wonderful job of facilitating After Effects templates, by being a place where all editable values can be placed for the user to find (no more having to search every precomp until you find the layer you need!).&lt;/p&gt;

&lt;p&gt;First things first, I want to make sure all the parameters a user could want for changing the sign off message are in place. I create a new text layer, and connect its source text to my expression to create a place for the user to type their own message. Next, I make some more sliders and create more variables to control the sign off font size, and leading. Here is the list of my sign off variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var signOffMessage = thisComp.layer("SIGN OFF").text.sourceText;
var signOffSize = effect("Sign Off Font Size")("Slider");
var signOffLeading = (signOffSize/100) * effect("Sign Off Leading Padding")("Slider");
var signOffStyle = text.sourceText.style.setFontSize(signOffSize).setLeading(signOffSize + signOffLeading);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and my updated else statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;else signOffStyle.setText(signOffMessage)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, I open my Essential Graphics panel (Window &amp;gt; Essential Graphics). I drag in the parameters I want my user to use, such as the sign off message's source text, and all of my sliders. I make some folders to keep my parameters organised. Lastly, I turn off, lock, and shy-guy away my sign off message text layer, so the user isn't distracted.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhkaiuage7uhqvr586f6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhkaiuage7uhqvr586f6.png" alt="Essential graphics panel" width="628" height="925"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now all parameters which need to be editable for the user connected to my code are present in the panel. What a tidy template!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46b1nkqfrdh9h65a6m1n.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F46b1nkqfrdh9h65a6m1n.gif" alt="Essential graphics effecting the sign off message gif" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to add more parameters to your Essential Graphics panel, simply drag and drop them in as desired.&lt;/p&gt;

&lt;p&gt;Full expression with new formatting:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source Text&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var data = footage("SPREADSHEET.csv");

//Number of rows
var dataSplit = data.sourceText.split(/\n/);
var dataFirstRow = dataSplit[0].split(",");
//dataSplit.length-2 // number of Row
//dataFirstRow.length // number of Columns

var rows = dataSplit.length-2;

//For loop 
var entry = 0;
for (i = 0; i &amp;lt;= rows &amp;amp;&amp;amp; time &amp;gt;= data.dataValue([2, i]); i++){
    if (true) entry++
};


//Return
var maxDuration = data.dataValue([2, clamp(entry, 0, rows)]);
var copy = data.dataValue([0, clamp(entry, 0, rows)]);
var copySize = data.dataValue([3, clamp(entry, 0, rows)]);
var copyLeading = (copySize/100) * effect("Leading Padding")("Slider");
var copyStyle = text.sourceText.style.setFontSize(copySize).setLeading(copySize + copyLeading);
var signOffMessage = thisComp.layer("SIGN OFF").text.sourceText;
var signOffSize = effect("Sign Off Font Size")("Slider");
var signOffLeading = (signOffSize/100) * effect("Sign Off Leading Padding")("Slider");
var signOffStyle = text.sourceText.style.setFontSize(signOffSize).setLeading(signOffSize  + signOffLeading);

if (time &amp;lt; maxDuration) copyStyle.setText(copy.replace(/_/g, "\n"))
    else signOffStyle.setText(signOffMessage)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Anchor Point&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var copy = thisLayer;

var cWidth = copy.sourceRectAtTime().width;
var cHeight = copy.sourceRectAtTime().height;
var cTop = copy.sourceRectAtTime().top;
var cLeft = copy.sourceRectAtTime().left;

var x = cLeft + cWidth / 2;
var y = cTop + cHeight /2;

[x, y]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Animation: Opacity&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now the basic script is written, and formatting options have been applied, the fun part can begin! Animation.&lt;/p&gt;

&lt;p&gt;I decided to start simple. My goal was to create a simple fade in and out for every line, to soften the hard cut. The opacity parameter is also only 1 value, so it will be easier to work with as we start out.&lt;/p&gt;

&lt;p&gt;Since we will still need to know when our lines change, we need to keep the start of our code the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var data = footage("SPREADSHEET.csv");

//Number of rows
var dataSplit = data.sourceText.split(/\n/);
var dataFirstRow = dataSplit[0].split(",");
//dataSplit.length-2 // number of Row
//dataFirstRow.length // number of Columns

var rows = dataSplit.length-2;

//For loop 
var entry = 0;
for (i = 0; i &amp;lt;= rows &amp;amp;&amp;amp; time &amp;gt;= data.dataValue([2, i]); i++){
    if (true) entry++
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But now, what I want to return is a change in value. Essentially, at the start of every line, the opacity will change from 0 to 100, over a duration set by the user. Then, at the end of the line, the opacity changes from 100 to 0 at the same speed, so there is a smooth transition between each line. This means as one animation brings the line &lt;em&gt;on&lt;/em&gt;, and the reverse animation brings it &lt;em&gt;off&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;With that reasoning, I set up the variables I would need to make this possible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var maxDuration = data.dataValue([2, rows]);
var lineDuration = data.dataValue([1, clamp(entry, 0, rows)]);
var totalDuration = data.dataValue([2, clamp(entry, 0, rows)]);

var startAni = totalDuration - lineDuration;
var endAni = totalDuration;
var aniDuration = (1*thisComp.frameDuration) * effect("aniDuration")("Slider");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At last, a reason for the line duration column on our spreadsheet! Having this data accessible in our .csv file means less calculations for After Effects, meaning more efficient code.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;lineDuration&lt;/code&gt; and &lt;code&gt;totalDuration&lt;/code&gt; match their appropriate columns in the spreadsheet. Meanwhile, &lt;code&gt;maxDuration&lt;/code&gt; is the last entry of &lt;code&gt;totalDuration&lt;/code&gt;, and will be the time it takes to cycle through all of our text.&lt;/p&gt;

&lt;p&gt;With these variables set, I can now set the animation on, and animation off times. Since I need the start of the animation to be at the beginning of the line, I set &lt;code&gt;startAni&lt;/code&gt; to &lt;code&gt;totalDuration - lineDuration&lt;/code&gt; to find that value. Meanwhile, &lt;code&gt;endAni&lt;/code&gt; = &lt;code&gt;totalDuration&lt;/code&gt;, the time the current line animates off of screen.&lt;/p&gt;

&lt;p&gt;Lastly, I create a variable so I can control the speed of the animation. I create a new slider, and multiply it by &lt;code&gt;1*thisComp.frameDuration&lt;/code&gt;. This way, no matter what the composition's frame rate is set to, the slider will always be setting the animation's duration in frames. My composition is at 25fps, so I set my &lt;code&gt;aniDuration&lt;/code&gt; to 10 frames.&lt;/p&gt;

&lt;p&gt;With all these variables set, it's time to use a trusty &lt;code&gt;ease&lt;/code&gt; function to create the move from 0 opacity to 100.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var aniOn = ease(time, startAni, startAni + aniDuration, 0, 100);
var aniOff = ease(time, endAni - aniDuration, endAni, 100, 0);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the &lt;code&gt;aniOn&lt;/code&gt; variable will change the opacity parameter from 0 to 100, between the start time of each line, and the start time + the animation duration set by the slider. Meanwhile, the &lt;code&gt;aniOff&lt;/code&gt; variable will change the opacity parameter from 100 to 0, between the end time of each line - the animation duration, and the end time.&lt;/p&gt;

&lt;p&gt;In order to use both of these animations, we need to set an &lt;code&gt;if&lt;/code&gt; statement. I decided that the time the line is on screen should be divided by 2, giving equal time for the line to animate on and off. To work out where the midpoint of each line sits, I just have to add the &lt;code&gt;startAni&lt;/code&gt; and &lt;code&gt;endAni&lt;/code&gt; together, and divide by 2.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (time &amp;lt; (startAni + endAni)/2) aniOn
    else aniOff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with that, we have our first bit of animation!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dzhpk73kzogyrfqgb4g.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dzhpk73kzogyrfqgb4g.gif" alt="animating opacity gif" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But I can't celebrate just yet. This current &lt;code&gt;if&lt;/code&gt; statement doesn't include the sign off message. At present, the text will animate off after the last line in our spreadsheet, making the sign off message invisible.&lt;/p&gt;

&lt;p&gt;To fix this, I need to make a variable for the sign off message to animate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var signOffAni = ease(time, maxDuration, maxDuration + aniDuration, 0, 100);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and nest my current &lt;code&gt;if&lt;/code&gt; statement inside &lt;em&gt;another&lt;/em&gt; &lt;code&gt;if&lt;/code&gt; statement, to let After Effects know what should happen after all the entries of the spreadsheet have been read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (time &amp;lt; maxDuration)
    if (time &amp;lt; (startAni + endAni)/2) aniOn
        else aniOff
    else signOffAni
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when the current &lt;code&gt;time&lt;/code&gt; is less than the total time it will take to cycle through all entries on the spreadsheet, After Effects &lt;em&gt;then&lt;/em&gt; checks whether or not the current &lt;code&gt;time&lt;/code&gt; is in the first or second half of the time the current line is on screen. Once it has that information, it chooses whether or not to display the &lt;code&gt;startAni&lt;/code&gt; or &lt;code&gt;endAni&lt;/code&gt; variable. But - if the current time is &lt;em&gt;equal to or more than&lt;/em&gt; the &lt;code&gt;maxDuration&lt;/code&gt;, it will display the new &lt;code&gt;signOffAni&lt;/code&gt; variable, accounting for the sign off message.&lt;/p&gt;

&lt;p&gt;But before I could move on to testing other bits of information, I started to wonder what would happen if the &lt;code&gt;aniDuration&lt;/code&gt; was set to 0. Sadly, this breaks things a little bit. The first half of the time it takes a line to display is permanently set to 0 opacity, while the second half is permanently set to 100. True, I could avoid setting the slider to 0, but then again, wouldn't it be cool if setting the slider to 0 turned the animation &lt;em&gt;off&lt;/em&gt;, so we could choose whether or not we wanted to use it?&lt;/p&gt;

&lt;p&gt;And I can do that! It involves further nesting my &lt;code&gt;if&lt;/code&gt; statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (time &amp;lt; maxDuration)
    if (time &amp;lt; (startAni + endAni)/2)
        if (aniDuration == 0) value
            else aniOn
        else
            if (aniDuration == 0) value
                else aniOff
    else
        if (aniDuration == 0) value
            else signOffAni
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Breaking this down, everytime the previous &lt;code&gt;if&lt;/code&gt; statement gave a value, I created a new condition for After Effects to check. If the duration slider was set to 0, the &lt;code&gt;if&lt;/code&gt; statement would set the opacity to it's default value, turning the animation off. However, if it was any other value, it would run as expected.&lt;/p&gt;

&lt;p&gt;By setting the opacity to its default value rather than 100, it allows the user to change the opacity of the text layer. After doing this, the thought occurred to me that the &lt;code&gt;ease&lt;/code&gt; functions should also be changed to the default layer value, to give the user more customisation options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var aniOn = ease(time, startAni, startAni + aniDuration, 0, value);
var aniOff = ease(time, endAni - aniDuration, endAni, value, 0);
var signOffAni = ease(time, maxDuration, maxDuration + aniDuration, 0, value);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, I have some seriously cool customisation options for the first animatable parameter of the script!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full Opacity Expression&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var data = footage("SPREADSHEET.csv");

//Number of rows
var dataSplit = data.sourceText.split(/\n/);
var dataFirstRow = dataSplit[0].split(",");
//dataSplit.length-2 // number of Row
//dataFirstRow.length // number of Columns

var rows = dataSplit.length-2;

//For loop 
var entry = 0;
for (i = 0; i &amp;lt;= rows &amp;amp;&amp;amp; time &amp;gt;= data.dataValue([2, i]); i++){
    if (true) entry++
};


//Return
var maxDuration = data.dataValue([2, rows]);
var lineDuration = data.dataValue([1, clamp(entry, 0, rows)]);
var totalDuration = data.dataValue([2, clamp(entry, 0, rows)]);

var startAni = totalDuration - lineDuration;
var endAni = totalDuration;
var aniDuration = (1*thisComp.frameDuration) * effect("Opacity Animation Duration")("Slider");

var aniOn = ease(time, startAni, startAni + aniDuration, 0, value);
var aniOff = ease(time, endAni - aniDuration, endAni, value, 0);
var signOffAni = ease(time, maxDuration, maxDuration + aniDuration, 0, value);


if (time &amp;lt; maxDuration)
    if (time &amp;lt; (startAni + endAni)/2)
        if (aniDuration == 0) value
            else aniOn
        else
            if (aniDuration == 0) value
                else aniOff
    else
        if (aniDuration == 0) value
            else signOffAni
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Animation: Position&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;While I certainly don't intend to walk through every single parameter in the same way, I do want to write up how I am choosing to animate the parameters with more than 1 value.&lt;/p&gt;

&lt;p&gt;However, the position parameter is unique, in that it can be separated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3s56kapgximqnqn5ib8v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3s56kapgximqnqn5ib8v.png" alt="Separated position screen shot" width="605" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because of this, controlling each value was simple. I copy-pasted my code from the opacity parameter, and changed the applicable variables.&lt;/p&gt;

&lt;p&gt;Here are the variables under the &lt;code&gt;X Position&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var maxDuration = data.dataValue([2, rows]);
var lineDuration = data.dataValue([1, clamp(entry, 0, rows)]);
var totalDuration = data.dataValue([2, clamp(entry, 0, rows)]);

var startAni = totalDuration - lineDuration;
var endAni = totalDuration;

var aniDuration = (1*thisComp.frameDuration) * effect("X Position Animation Duration")("Slider");
var posMoveIn = effect("X position move in")("Slider");
var posMoveOut = effect("X position move out")("Slider");

var aniOn = ease(time, startAni, startAni + aniDuration, value + posMoveIn, value);
var aniOff = ease(time, endAni - aniDuration, endAni, value, value + posMoveOut);
var signOffAni = ease(time, maxDuration, maxDuration + aniDuration, value + posMoveIn, value);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created a few more sliders for the position parameter than I did for opacity, to control the movement in and out separately. I made sure to add &lt;code&gt;posMoveIn&lt;/code&gt; to the in-animation variables, and &lt;code&gt;posMoveOut&lt;/code&gt; to the out-animation.&lt;/p&gt;

&lt;p&gt;I then did all of this for the &lt;code&gt;Y Position&lt;/code&gt;. If you also want to animate within Z space, make sure to enable 3D on the layer, and do the same for its &lt;code&gt;Z Position&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With that in place, I could now animate the position of my layers in and out for each line.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7452debktp8573dafrca.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7452debktp8573dafrca.gif" alt="position x gif" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpy2z324az96z0b7q70d7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpy2z324az96z0b7q70d7.gif" alt="position y gif" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9qz4kv1nyzkeit0v7z24.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9qz4kv1nyzkeit0v7z24.gif" alt="position xy gif" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And, if I wanted, I could combine the position and the opacity animation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2wmzzyiv2szz08gina4k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2wmzzyiv2szz08gina4k.gif" alt="position opacity gif" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Animation: Scale&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Unlike position, we are unable to separate our scale values. This is because we are able to tether and untether our x and y scale values with a switch in our properties panel - which under normal circumstances works perfectly fine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foy80xg356i2tx1dhwu41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foy80xg356i2tx1dhwu41.png" alt="scale chain link" width="283" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, this means we will have to do some more thinking about our expression, if we want to have the option of affecting the x and y scale independently. &lt;/p&gt;

&lt;p&gt;Here's how I worked this out.&lt;/p&gt;

&lt;p&gt;I started by making a dropdown menu. I made 2 options, &lt;em&gt;combined scale&lt;/em&gt; and &lt;em&gt;separated scale&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gl8mtxggzcwkbicwyay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gl8mtxggzcwkbicwyay.png" alt="scale dropdown menu options" width="281" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I then added 9 more sliders. 3 for affecting the animation duration, animation in, and animation out while the scale was unified. 3 for those aspects for the x scale only. And 3 for those aspects for the y scale only.&lt;/p&gt;

&lt;p&gt;I then created all my variables. There were considerably more than for position, as effectively, I had to times the number I needed by 3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var maxDuration = data.dataValue([2, rows]);
var lineDuration = data.dataValue([1, clamp(entry, 0, rows)]);
var totalDuration = data.dataValue([2, clamp(entry, 0, rows)]);

var startAni = totalDuration - lineDuration;
var endAni = totalDuration;
var aniDuration = (1*thisComp.frameDuration) * effect("Scale Animation Duration")("Slider");

var scaleDrop = effect("Scale Dropdown")("Menu");

var scaleUniIn = effect("Scale Unified scale In")("Slider");
var scaleUniOut = effect("Scale Unified scale Out")("Slider");
var scaleXIn = effect("Scale X scale In")("Slider");
var scaleXOut = effect("Scale X scale Out")("Slider");
var scaleYIn = effect("Scale Y scale In")("Slider");
var scaleYOut = effect("Scale Y scale Out")("Slider");

var aniOnUni = ease(time, startAni, startAni + aniDuration, value[0] + scaleUniIn, value[0]);
var aniOffUni = ease(time, endAni - aniDuration, endAni, value[0], value[0] + scaleUniOut);
var signOffAniUni = ease(time, maxDuration, maxDuration + aniDuration, value[0] + scaleUniIn, value[0]);

var aniOnX = ease(time, startAni, startAni + aniDuration, value[0] + scaleXIn, value[0]);
var aniOffX = ease(time, endAni - aniDuration, endAni, value[0], value[0] + scaleXOut);
var signOffAniX = ease(time, maxDuration, maxDuration + aniDuration, value[0] + scaleXIn, value[0]);

var aniOnY = ease(time, startAni, startAni + aniDuration, value[1] + scaleYIn, value[1]);
var aniOffY = ease(time, endAni - aniDuration, endAni, value[1], value[1] + scaleYOut);
var signOffAniY = ease(time, maxDuration, maxDuration + aniDuration, value[1] + scaleYIn, value[1]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also includes a variable for the dropdown menu.&lt;/p&gt;

&lt;p&gt;From there, I had to extend my &lt;code&gt;if&lt;/code&gt; statement again, to check what the dropdown menu was set to. If it was set to option 1, combined scale, it would display the unified variables. If it was anything else, it would display the separated variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
if (time &amp;lt; maxDuration)
    if (time &amp;lt; (startAni + endAni)/2)
        if (aniDuration == 0) [value[0], value[1]]
            else
                if (scaleDrop == 1) [aniOnUni, aniOnUni]
                    else [aniOnX, aniOnY]
        else
            if (aniDuration == 0) [value[0], value[1]]
                else
                    if (scaleDrop == 1) [aniOffUni, aniOffUni]
                    else [aniOffX, aniOffY]
    else
        if (aniDuration == 0) [value[0], value[1]]
            else
                if (scaleDrop == 1) [signOffAniUni, signOffAniUni]
                    else [signOffAniX, signOffAniY]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once all these pieces were in place, the template could be toggled between a combined scale animation, and a separated scale animation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft39v3ue9ns0xwml83407.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft39v3ue9ns0xwml83407.gif" alt="scale animation options gif" width="838" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Animation: Other Ideas&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Using the techniques above, you could quite easily make a rotation option for your template.&lt;/p&gt;

&lt;p&gt;However, why stop there? You could switch your layer to 3D mode (as mentioned before in the &lt;em&gt;Animation: Position&lt;/em&gt; section) and introduce a camera. Moving a camera around across your text, while your text animates, can create some really interesting effects!&lt;/p&gt;

&lt;p&gt;Alternatively, if you want to keep your layer 2D, you could introduce a null object, and parent your text layer to it. Then, try to move it around in weird and wonderful ways. Since all our expressions reference the original values of our text layer, parenting our layer shouldn't result in any nasty surprises. You could also add expressions to your null layer, so it animates as every line in the spreadsheet comes and goes.&lt;/p&gt;

&lt;p&gt;You can introduce shape layers, footage, or colour changes. Perhaps even dive into the text animator and animate your text line by line, or letter by letter (although I found that you will need a separate layer to fix your anchor point for this). The sky is the limit!&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Well... that isn't strictly true. There are of course some limitations to creating a project this way.&lt;/p&gt;

&lt;p&gt;Such as the number of lines the expression can support. The more rows in your .csv file, the more loops and calculations. Meaning the harder After Effects and your computer is going to have to work. This can make things run rather slow.&lt;/p&gt;

&lt;p&gt;My suggestion, if you have lots of lines and it's causing too much lag in your project, is to separate your spreadsheet into multiple .csv files, and create videos in sections. You could then render each section separately, and stitch your video together in Premiere, or another editing program of choice. &lt;/p&gt;

&lt;p&gt;Also, because this layer is only 1 text layer, there is no way for the transitions between your lines to overlap. If you require that, you will have to write an expression which works with 2 text layers. How I would test this is to have 2 spreadsheets, one with copy in the text column on all odd number rows, and the other with copy in the text column on all even number rows. That way, you could stagger the layers in such a way that the animation of the two overlap.&lt;/p&gt;

&lt;p&gt;For similar reasons, adding a delay to the expression is also very tricky. I recommend accounting for the delay in your spreadsheet, rather than factoring it into After Effects, so the expression doesn't get more complex. You could make an extra column to adjust your total durations.&lt;/p&gt;

&lt;p&gt;It is also important to note that this is a &lt;em&gt;template&lt;/em&gt; project. No matter how much you add, it will never replace the almost limitless potential of a designer creating a project from scratch. If you need something truly unique and special, you're going to have to roll up your sleeves and prepare for creating the project by hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion And Learnings&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I learned a lot from this project. This was the first time I ever used a &lt;code&gt;for&lt;/code&gt; loop, which this project heavily relies on. It fact, it was a project full of first times: the first time importing data into After Effects using a .csv file; the first time working with text styling options, and the first time crafting complex, &lt;code&gt;if&lt;/code&gt; statements. I learned how to turn data into a string in After Effects by using the &lt;code&gt;sourceText&lt;/code&gt; function, and learned about the &lt;code&gt;split&lt;/code&gt;, &lt;code&gt;clamp&lt;/code&gt; and &lt;code&gt;replace&lt;/code&gt; functions.&lt;/p&gt;

&lt;p&gt;I am still experimenting with what I can do with this template. There may be a follow up article to this, with some of my more complex discoveries around using text animator and expression selector options, and creating variables for changing out the type of easing used to create the animations once I am satisfied.&lt;/p&gt;

&lt;p&gt;Overall, this was a great project to boost my knowledge of javascript within After Effects, and I feel I have more than achieved what I set out to do.&lt;/p&gt;

&lt;p&gt;If you have any questions, please leave a comment and I'll try to answer them. Alternatively, if you think there is a more efficient way of doing things than I have done them, please let me know.&lt;/p&gt;

</description>
      <category>aftereffects</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>After Effects: Text Animation And The Expression Selector</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Mon, 12 Aug 2024 08:00:00 +0000</pubDate>
      <link>https://dev.to/kocreative/after-effects-text-animation-and-the-expression-selector-a2l</link>
      <guid>https://dev.to/kocreative/after-effects-text-animation-and-the-expression-selector-a2l</guid>
      <description>&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Introduction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Using The Range Selector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Using The Expression Selector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Advanced Techniques&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conclusion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;br&gt;

&lt;h2&gt;
  
  
  Introduction &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In the first article of this series, &lt;a href="https://dev.to/kocreative/after-effects-the-basics-915"&gt;After Effects Basics&lt;/a&gt;, I briefly mentioned the text animator. It is a tool in After Effects, which allows for animation of independent characters, words, or lines, as opposed to the layer controls, which affect the entire text layer as a whole.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwisju8ojm8lb04237pny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwisju8ojm8lb04237pny.png" alt="Screenshot of the text animator options" width="800" height="1397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However it isn't the most intuitive to use. Therefore I wanted to go over it again in depth, starting with basic concepts, all the way to advanced techniques.&lt;/p&gt;

&lt;p&gt;Let's get started.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h3&gt;
  
  
  Using The Range Selector &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;From the properties panel, click the &lt;code&gt;+Add Animator&lt;/code&gt; button. Select the property you want to animate. For this example, I will be using &lt;code&gt;position&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When you select a property, the layer will have new options open up in the timeline, like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6iqfpskecdxetpynjln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo6iqfpskecdxetpynjln.png" alt="Screenshot after adding a position text animator" width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll see the new property, &lt;code&gt;Animator 1&lt;/code&gt; added. Inside of it, there is &lt;code&gt;Range Selector 1&lt;/code&gt;, and a new position stopwatch. This is the basic setup for the text animator.&lt;/p&gt;

&lt;p&gt;Change the y coordinate of your position to &lt;code&gt;100&lt;/code&gt;. You'll notice the text shift down 100 pixels. Next, open up the Range Selector property. You'll see &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;end&lt;/code&gt; and &lt;code&gt;offset&lt;/code&gt; parameters (as well as some advanced options). To demonstrate how the range selector works, make a keyframe at the beginning of your timeline for the &lt;code&gt;start&lt;/code&gt; parameter, setting it to &lt;code&gt;0&lt;/code&gt;. Then, at 1 second, add another keyframe setting it to &lt;code&gt;100&lt;/code&gt;. As you play this back, you'll see the letters of your text layer animate, one by one, raising back to their default position.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkn5615lhnkzxklr3m829.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkn5615lhnkzxklr3m829.gif" alt="Text Animator start Position GIF" width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As the name suggests, the start and end parameters of the range selector control how much of the text is affected by all the properties listed inside Animator 1 (in this case, position). With the start set to &lt;code&gt;0&lt;/code&gt;, and the end set to &lt;code&gt;100&lt;/code&gt;, the entire range is selected, so all characters will be affected. However, as we change the start from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;100&lt;/code&gt; over time, it stops affecting the text gradually as the range gets smaller, resulting in none of the text being affected once it reaches &lt;code&gt;100&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can also create a similar animation using the offset parameter, instead of the start or end. This allows you to keep your range fixed, but offsets the values from &lt;code&gt;-100&lt;/code&gt; to &lt;code&gt;100&lt;/code&gt;. This allows for a bit more flexibility, and to easily reverse the animation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsh6kzwjgy1r8tkzkbax.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsh6kzwjgy1r8tkzkbax.gif" alt="Text Animator offset Position GIF" width="1302" height="966"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, if you explore the advanced options of the range selector, you can find some options to help fine tune the animation. You can change whether or not the range selects the text by &lt;code&gt;character&lt;/code&gt;, &lt;code&gt;word&lt;/code&gt; or &lt;code&gt;line&lt;/code&gt;. Perfect if you have a long sentence or heading. You can also affect the easing of the animation, randomise the order characters are affected, and change the shape of the animation. This will alter how the text transitions, and is worth playing with to achieve different results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5kqo76usznsykd17y7g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5kqo76usznsykd17y7g.png" alt="screen shot of the based on parameter" width="528" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also apply multiple Animators to the same text layer with slightly offset keyframes, to achieve some fun effects.&lt;/p&gt;

&lt;p&gt;To try this, remove all keyframes currently on your timeline. Set start to &lt;code&gt;0&lt;/code&gt;, end to &lt;code&gt;100&lt;/code&gt;, and animate offset over 1 second, from &lt;code&gt;-100&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt;. Then, duplicate Animator 1. This will create an &lt;code&gt;Animator 2&lt;/code&gt; property. Open this up, and set the position inside Animator 2 to &lt;code&gt;-50&lt;/code&gt;. Select the keyframes for Animator 2's range selector, and move them forward 4 frames. You'll notice this creates a small bounce back for your text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37gbj026hmm1xrd3rxv6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37gbj026hmm1xrd3rxv6.gif" alt="Text Animator 2 Animators GIF" width="1302" height="852"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to add additional properties to the same Animator, you can do so by clicking on the &lt;code&gt;add&lt;/code&gt; button next to the Animator in the timeline, selecting &lt;code&gt;property&lt;/code&gt;, then whatever property you would like to add.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgmmuo78fbkds0fy3brz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgmmuo78fbkds0fy3brz.png" alt="add button" width="448" height="23"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that covers the range selector. For some projects, this is enough. Stop here for a bit and have a play with this selector. Try using different properties within the same Animator, or create multiple Animators with different effects. Once you're happy you understand how this control works, you can move onto the next step: working with the expression selector.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Using The Expression Selector &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The range selector is not perfect. It does not allow us to control the delay between each letter as it animates, but instead limits us to changing the shape of the animation, the offset, and ease. This means that animations can sometimes be hard to time, and often, does not allow for a smooth transition of letters. What if you want to start the animation of the second letter &lt;em&gt;before&lt;/em&gt; the first has completely finished?&lt;/p&gt;

&lt;p&gt;If you need to be more precise, you will need to use the expression selector.&lt;/p&gt;

&lt;p&gt;It took me some time to work out how to use this selector. But now that I have, I much prefer it to the range selector.&lt;/p&gt;

&lt;p&gt;First, create a new text layer, add an Animator with a position property like before, and set the y coordinate to &lt;code&gt;200&lt;/code&gt;. This time however, &lt;strong&gt;delete&lt;/strong&gt; the range selector. Then from your timeline, click the &lt;code&gt;add&lt;/code&gt; button next to Animator 1. Select &lt;code&gt;Selector&lt;/code&gt;/&lt;code&gt;Expression&lt;/code&gt;. Opening the property up, you will see it has a lot less options than the range selector. The &lt;code&gt;Based On&lt;/code&gt; option allows you to change whether the selector affects characters, words, or lines. Other than this, there is simply an &lt;code&gt;Amount&lt;/code&gt; property, which changes how much the Animator applies to the text. By default, there is an expression written in the amount parameter: &lt;code&gt;selectorValue * textIndex/textTotal&lt;/code&gt;, and your text layer will look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frh8ogxle83alj3wlf61b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frh8ogxle83alj3wlf61b.png" alt="Screenshot Expression selector 01" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This immediately throws 3 variables at us without explanation. Allow me to go through these now, and why they are affecting the text in this way.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;selectorValue&lt;/code&gt; is referencing the &lt;code&gt;Amount&lt;/code&gt; value of this property. By default, it is set to &lt;code&gt;100, 100, 100&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;textIndex&lt;/code&gt; assigns an index number to each character, word or line in your text layer (depending on what your &lt;code&gt;based on&lt;/code&gt; property is set to), so that each may be affected individually.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;textTotal&lt;/code&gt; is the &lt;em&gt;total&lt;/em&gt; number of characters, words, or lines in your text layer (depending on what your &lt;code&gt;based on&lt;/code&gt; property is set to).&lt;/p&gt;

&lt;p&gt;With my &lt;code&gt;based on&lt;/code&gt; set to &lt;code&gt;Characters&lt;/code&gt;, we can now make sense of the expression written in the amount property.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;textIndex/textTotal&lt;/code&gt; will produce a number between 0 and 1. This in turn is multiplied against the &lt;code&gt;selectorValue&lt;/code&gt;. Therefore, the first character in the text layer with an index of 1, will be affected by the Animator only a small fraction. However, as &lt;code&gt;textIndex&lt;/code&gt; increases in value, the amount the Animator affects the text also increases. This goes on until we reach the last character of the text layer, which is 100% affected by the effects of the Animator.&lt;/p&gt;

&lt;p&gt;Another way to understand it, is to see each letter affected in sequence.&lt;/p&gt;

&lt;p&gt;Let's replace the default expression with this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (textIndex == Math.floor(time)) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an &lt;code&gt;if&lt;/code&gt; statement. If the &lt;code&gt;textIndex&lt;/code&gt; is the same as &lt;code&gt;Math.floor(time)&lt;/code&gt;, the Animator will be applied to it 100% (&lt;code&gt;Math.floor&lt;/code&gt; is applied to ensure the statement only returns integers). Else, it will be applied at a value of 0 (meaning it will have no effect whatsoever).&lt;/p&gt;

&lt;p&gt;As you scrub through the timeline, you will see one by one the letters of your text move.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqltk0nsew6wtbykb121.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzqltk0nsew6wtbykb121.gif" alt="Screenshot expression selector 02" width="1290" height="879"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how we select our range in the expression selector. This may be a bit confusing at first. But hopefully, I can help demystify the process of writing expressions for dynamic text movement a little bit.&lt;/p&gt;

&lt;p&gt;So, we now know &lt;code&gt;textIndex&lt;/code&gt; is one of the values we need to consider when writing the expression. Let's now consider the other values we will need.&lt;/p&gt;

&lt;p&gt;Go ahead and change the y position property within Animator 1 to &lt;code&gt;-200&lt;/code&gt;. This will help us write the expression to come.&lt;/p&gt;

&lt;p&gt;Delete the expression from your expression selector, and create the following variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var frames = 1 / thisComp.frameDuration;    //comp frame rate
var delay = 1;  //frame delay between each character, in frames
var dur = 6;    //duration of animation per character, in frames
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;frames&lt;/code&gt; is simply the framerate of the current composition. We will need this in order to convert our other values into frames rather than seconds, since working in seconds can be confusing when needing such small increments. This is easily set by dividing &lt;code&gt;1&lt;/code&gt; by &lt;code&gt;thisComp.frameDuration&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;delay&lt;/code&gt; is the time between each character in our animation. The lower the number, the less of a delay, therefore the quicker the animation. Setting it to &lt;code&gt;1&lt;/code&gt; will result in a 1 frame delay between each character - meaning the second letter will start to animate 1 frame after the first letter starts to move, and so on.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dur&lt;/code&gt; is the duration of the animation, per character. I've set mine to 6 for a medium speed animation.&lt;/p&gt;

&lt;p&gt;Now we have established our variables, we can create an &lt;code&gt;ease&lt;/code&gt; function to animate the text. This will provide a gradual change in value to our characters, vs our previous &lt;code&gt;if&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;Let's take a look at this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var frames = 1 / thisComp.frameDuration;
var delay = 1;
var dur = 6;

var indexDelay = textIndex*(delay/frames);
var frameDur = dur/frames;

ease(time, indexDelay, indexDelay + frameDur, 100, 0);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, we start off again with our variables &lt;code&gt;frames&lt;/code&gt;, &lt;code&gt;delay&lt;/code&gt;, and &lt;code&gt;dur&lt;/code&gt;. But there are two more variables we need to create in order to connect them with the &lt;code&gt;textIndex&lt;/code&gt; value, and keep the &lt;code&gt;ease&lt;/code&gt; function easy to read.&lt;/p&gt;

&lt;p&gt;First is &lt;code&gt;indexDelay&lt;/code&gt;. This simply multiplies the &lt;code&gt;textIndex&lt;/code&gt; with our &lt;code&gt;delay&lt;/code&gt; variable (divided by &lt;code&gt;frames&lt;/code&gt;, to convert the delay into the correct time increments). This will dictate when each letter begins its animation.&lt;/p&gt;

&lt;p&gt;The second is &lt;code&gt;frameDur&lt;/code&gt;. This simply divides &lt;code&gt;dur&lt;/code&gt; by &lt;code&gt;frames&lt;/code&gt;, to convert the animation duration into the correct time increment.&lt;/p&gt;

&lt;p&gt;Finally, the &lt;code&gt;ease&lt;/code&gt; function. If you are unfamiliar with the &lt;code&gt;ease&lt;/code&gt; function, I go into it in depth in &lt;a href="https://dev.to/kocreative/after-effects-favourite-expressions-part-1-linear-and-ease-14p1"&gt;this&lt;/a&gt; article. I highly recommend making yourself familiar with how this function works before continuing.&lt;/p&gt;

&lt;p&gt;I set the first argument to &lt;code&gt;time&lt;/code&gt;, so the selector value is remapped to &lt;code&gt;time&lt;/code&gt; and will change as the seconds tick by. Next, I set my in and out points to &lt;code&gt;indexDelay&lt;/code&gt;, and &lt;code&gt;indexDelay + frameDur&lt;/code&gt;. This means each character will animate in turn from its own index specific start point, for as long as our &lt;code&gt;dur&lt;/code&gt; value, in frames. Finally, the last 2 arguments will set what values the selector property animates between. These are set to &lt;code&gt;100&lt;/code&gt; and &lt;code&gt;0&lt;/code&gt; respectively, meaning the Animator will go from 100% affecting the text, to 0% affecting it, returning the text to its default position.&lt;/p&gt;

&lt;p&gt;The result of this expression is this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zu76iwgnvwup1jqbpp8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0zu76iwgnvwup1jqbpp8.gif" alt="Apricot GIF 01" width="1080" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Already, we can see a big difference in the timings here vs the range selector. Here, we are able to play with the timings in a much more precise way. Feel free to adjust the numbers of the &lt;code&gt;delay&lt;/code&gt; and &lt;code&gt;dur&lt;/code&gt; variables, to see what can be achieved.&lt;/p&gt;

&lt;p&gt;Like the range selector, you can duplicate Animator 1 to create an &lt;code&gt;Animator 2&lt;/code&gt;. By simply adjusting &lt;code&gt;time&lt;/code&gt; in Animator 2's &lt;code&gt;ease&lt;/code&gt; function, you can create a similar bounce effect as we did for the range selector before:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Animator 2 expression&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
var frames = 1 / thisComp.frameDuration;
var delay = 1;
var dur = 6;

var indexDelay = textIndex*(delay/frames);
var frameDur = dur/frames;
var inTime = (thisComp.frameDuration * 4);

ease(time - inTime, indexDelay, indexDelay + frameDur, 100, 0);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result with 2 expression selector animators:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2e9fuvdoyblec4cs9yq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx2e9fuvdoyblec4cs9yq.gif" alt="Apricot GIF 02" width="1080" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I created the variable &lt;code&gt;inTime&lt;/code&gt; to store my 4 frame calculation, to keep the &lt;code&gt;ease&lt;/code&gt; function tidy.&lt;/p&gt;

&lt;p&gt;For the cherry on top, try adding an opacity property to Animator 1, so the text also fades in as it drops down (remember: you can do this by clicking the &lt;code&gt;add&lt;/code&gt; button next to Animator 1, selecting &lt;code&gt;Property&lt;/code&gt;/&lt;code&gt;Opacity&lt;/code&gt;, and setting it to &lt;code&gt;0&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwf6atwev1g7bo6d5yabz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwf6atwev1g7bo6d5yabz.gif" alt="Apricot GIF 03" width="1080" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, if you want the text to animate on &lt;em&gt;and&lt;/em&gt; off, you need only use an &lt;code&gt;if&lt;/code&gt; statement to toggle between 2 different &lt;code&gt;ease&lt;/code&gt; functions on both your Animators:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Animator 1 expression&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var frames = 1 / thisComp.frameDuration;
var delay = 1;
var dur = 6;

var indexDelay = textIndex*(delay/frames);
var frameDur = dur/frames;

var timeOut = linear(time, 2, 4, 0, 2);

var aniIn = ease(time, indexDelay, indexDelay + frameDur, 100, 0);
var aniOut = ease(time - 2, indexDelay, indexDelay + frameDur, 0, 100);

if (time &amp;lt; 2) aniIn
    else aniOut
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Animator 2 expression&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var frames = 1 / thisComp.frameDuration;
var delay = 1;
var dur = 6;

var indexDelay = textIndex*(delay/frames);
var frameDur = dur/frames;
var inTime = (thisComp.frameDuration * 4);

var aniIn = ease(time - inTime, indexDelay, indexDelay + frameDur, 100, 0);
var aniOut = ease(time - inTime - 2, indexDelay, indexDelay + frameDur, 0, 100);

if (time &amp;lt; 2 - (thisComp.frameDuration * 4)) aniIn
    else aniOut
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzr2pkvv3pie477xqtm4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmzr2pkvv3pie477xqtm4.gif" alt="Apricot 04" width="720" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I advise you to stop and have a play with this now, to see what you can make before moving onto the advanced techniques.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Advanced Techniques &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;So, the range selector lets us fine tune our movements, but not control the delay between our letters. Whereas using the expression selector, we &lt;strong&gt;can&lt;/strong&gt; control the delay, but the default &lt;code&gt;linear&lt;/code&gt; and &lt;code&gt;ease&lt;/code&gt; functions leave us with only two types of interpolation for our animation.&lt;/p&gt;

&lt;p&gt;Or, does it?&lt;/p&gt;

&lt;p&gt;What if we want more dynamic animation?&lt;/p&gt;

&lt;p&gt;Then we need to create our own custom easing functions. Or, we can use functions others have already created, such as &lt;a href="http://robertpenner.com/easing/" rel="noopener noreferrer"&gt;Penner's Easing functions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have gone into depth about these functions in &lt;a href="https://dev.to/kocreative/after-effects-favourite-expressions-part-2-penner-easing-functions-1kb"&gt;this&lt;/a&gt; article before. In short, they are a collection of equations which, in Penner's own terms, add "flavour to motion." And, thanks to these functions being &lt;a href="http://robertpenner.com/easing_terms_of_use.html" rel="noopener noreferrer"&gt;open source&lt;/a&gt;, we can use them in our After Effects templates, or make our own .jsx files to store them in free of charge (click on the previous article for a full rundown of how to do this). This saves us &lt;em&gt;massive&lt;/em&gt; amounts of time - so thank you very much once again, Robert Penner.&lt;/p&gt;

&lt;p&gt;Importing my .jsx file to my project, I make a new text layer and add an expression selector to it like before. Then, I start by referencing my .jsx file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const penner = footage("Penner Easing Functions.jsx").sourceData.getFunctions();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are not looking to make a .jsx file, but instead are adding custom functions manually to your project each time, I will post the details of the functions I will be using here. My first example will be using the &lt;code&gt;easeInOutSine&lt;/code&gt; function, which you can create by adding...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; function easeInOutSine (t, b, c, d) {
            return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
        };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...to the start of your expression, instead of calling a .jsx file.&lt;/p&gt;

&lt;p&gt;By using the &lt;code&gt;easeInOutSine&lt;/code&gt; function, we can make a constantly moving text layer, bobbing up and down following a sine curve.&lt;/p&gt;

&lt;p&gt;After calling our .jsx file, or establishing the function, we set up our variables, the same as we did before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var frames = 1 / thisComp.frameDuration;
var delay = 2;
var dur = 12;

var indexDelay = textIndex * (delay/frames);
var frameDur = dur/frames;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, once we are happy, we can write our final expression. Since the Penner Easing functions work slightly differently to the default &lt;code&gt;ease&lt;/code&gt; and &lt;code&gt;linear&lt;/code&gt; expressions, our variables will be inputted in a slightly different order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;easeInOutSine(time - indexDelay, 100, -100, frameDur)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;easeInOutSine&lt;/code&gt;, the &lt;code&gt;indexDelay&lt;/code&gt; is subtracted from time, our first argument, since this function does not establish an in and out point for value remapping. The second argument is the starting value of our selector, which is set to &lt;code&gt;100&lt;/code&gt;. The third argument is the change in value, so this is set to &lt;code&gt;-100&lt;/code&gt;, to bring the selector value to 0. The last argument is the duration of the animation. This is simply set to &lt;code&gt;frameDur&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4y9l6x81kmxbi8xkoh1v.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4y9l6x81kmxbi8xkoh1v.gif" alt="Orange GIF 01" width="720" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wow - what a cool trick! Even though we specified a duration of &lt;code&gt;frameDur&lt;/code&gt;, the text will move continuously, as we have not set any boundaries for the custom function to stop its calculations (i'll be getting to this shortly).&lt;/p&gt;

&lt;p&gt;By adjusting the delay, we can control the space between each letter like so:&lt;br&gt;
&lt;strong&gt;Delay of 3&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fto4ab4iexjwa64zrknch.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fto4ab4iexjwa64zrknch.gif" alt="Orange GIF 02" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delay of 4&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhi4auuz5sbsbbxv1hk88.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhi4auuz5sbsbbxv1hk88.gif" alt="Orange GIF 03" width="720" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delay of 5&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvr96ulgfnalauoqyok5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwvr96ulgfnalauoqyok5.gif" alt="Orange GIF 04" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Already, the effect can be varied substantially, just by changing the &lt;code&gt;delay&lt;/code&gt; value. But the variations don't have to stop there. What if instead of subtracting the &lt;code&gt;textIndex&lt;/code&gt; from &lt;code&gt;time&lt;/code&gt;, we &lt;em&gt;added&lt;/em&gt; it to our &lt;code&gt;dur&lt;/code&gt; value? This would create an animation, where every letter of our text moved at a slightly different speed, depending on its &lt;code&gt;textIndex&lt;/code&gt; value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var frames = 1 / thisComp.frameDuration;
var dur = 12;

var durAlt = (textIndex + dur) / frames;

//Return
easeInOutSine(time, 100, -100, durAlt)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we are no longer referencing delay, we can remove this variable and marvel at the results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsoo58dm29kp0rk307ujn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsoo58dm29kp0rk307ujn.gif" alt="Orange GIF 06" width="1080" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This animation was actually the result of a mistake I made testing these expressions. But I really like it! I think it's a great way to add randomness to your work, as it is forever evolving. It is also a testament to the importance of experimentation. I never would have found this possibility if I didn't give myself room to explore and make mistakes.&lt;/p&gt;

&lt;p&gt;So, to recap the full advanced expression we've worked with so far:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;easeInOutSine&lt;/code&gt; full expression with .jsx file&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const penner = footage("Penner Easing Functions.jsx").sourceData.getFunctions();

var frames = 1 / thisComp.frameDuration;
var delay = 2;
var dur = 12;

var indexDelay = textIndex * (delay/frames);
var frameDur = dur/frames;
var durAlt = (textIndex + dur) / frames;

//Return
penner.easeInOutSine(time - indexDelay, 100, -100, frameDur) //Evenly timed expression

//or

penner.easeInOutSine(time, 100, -100, durAlt) //Each character animating at different speeds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;easeInOutSine&lt;/code&gt; full expression without .jsx file&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; function easeInOutSine (t, b, c, d) {
            return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
        };

var frames = 1 / thisComp.frameDuration;
var delay = 2;
var dur = 12;

var indexDelay = textIndex * (delay/frames);
var frameDur = dur/frames;
var durAlt = (textIndex + dur) / frames;

//Return
easeInOutSine(time - indexDelay, 100, -100, frameDur) //Evenly timed expression

//or

easeInOutSine(time, 100, -100, durAlt) //Each character animating at different speeds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is all well and good for animating with a sine, but having values go out of bounds may not be as useful for other custom functions. Let's take the &lt;code&gt;easeInOutBack&lt;/code&gt; function for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function easeInOutBack (t, b, c, d, s) {
            if (s == undefined) s = 1.70158;
            if ((t/=d/2) &amp;lt; 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
            return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
        };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we use the previously devised expression for this function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;easeInOutBack(time - indexDelay, 100, -100, frameDur)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82c3gwpvarbvxgcjdciq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F82c3gwpvarbvxgcjdciq.gif" alt="Cherry GIF 01" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we have a problem. The animation is only supposed to drop down once. But, because the function is remapping to time, it continues moving after the specified duration and drops down &lt;em&gt;twice&lt;/em&gt; - moving the selector value and going out of bounds. This is because we haven't set any boundaries on our &lt;code&gt;time&lt;/code&gt;. Even after our specified duration, time continues to tick, giving the function space to continue its calculations.&lt;/p&gt;

&lt;p&gt;In order to fix this, we need to &lt;code&gt;clamp&lt;/code&gt; the time parameter. Let's make another variable to store this new value in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var inTime = clamp(time - indexDelay, 0, frameDur);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we &lt;code&gt;clamp&lt;/code&gt; &lt;code&gt;time&lt;/code&gt;, preventing it from counting higher than the &lt;code&gt;frameDur&lt;/code&gt; variable. However, because we are subtracting the &lt;code&gt;indexDelay&lt;/code&gt; from &lt;code&gt;time&lt;/code&gt; first, each character will hit this limitation &lt;em&gt;in turn&lt;/em&gt;, allowing each letter to complete its animation before coming to a halt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;easeInOutBack&lt;/code&gt; full expression with .jsx file&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const penner = footage("Penner Easing Functions.jsx").sourceData.getFunctions();

var frames = 1 / thisComp.frameDuration;
var delay = 2;
var dur = 12;

var indexDelay = textIndex * (delay/frames);
var frameDur = dur/frames;
var inTime = clamp(time - indexDelay, 0, frameDur);

//Return
penner.easeInOutBack(inTime, 100, -100, frameDur)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;easeInOutBack&lt;/code&gt; full expression without .jsx file&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function easeInOutBack (t, b, c, d, s) {
            if (s == undefined) s = 1.70158;
            if ((t/=d/2) &amp;lt; 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
            return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
        };

var frames = 1 / thisComp.frameDuration;
var delay = 2;
var dur = 12;

var indexDelay = textIndex * (delay/frames);
var frameDur = dur/frames;
var inTime = clamp(time - indexDelay, 0, frameDur);

//Return
easeInOutBack(inTime, 100, -100, frameDur)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fap2vw6pzqqqqpb1viclq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fap2vw6pzqqqqpb1viclq.gif" alt="Cherry GIF 02" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice! Using the &lt;code&gt;easeInOutBack&lt;/code&gt; function, we can make a text animation with a bounce, using only one 1 Animator. Much more simple than creating 2 and offsetting the time like before!&lt;/p&gt;

&lt;p&gt;You can also control how "bouncy" this expression is by adding the 5th argument to this custom function: &lt;code&gt;s&lt;/code&gt;. According to the expression, while undefined it is &lt;code&gt;1.70158&lt;/code&gt;. So if we change it to a much larger value (and increase our &lt;code&gt;dur&lt;/code&gt; value to account for the extra animation time), we can make the movement back more pronounced:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;easeInOutBack(inTime, 100, -100, frameDur, 5)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwd08aou550lv9emtndi1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwd08aou550lv9emtndi1.gif" alt="Cherry GIF 03" width="1080" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's one last thing to fix with the &lt;code&gt;easeInOutBack&lt;/code&gt; function before we continue. The expression is &lt;em&gt;supposed&lt;/em&gt; to ease back at the start and the end of the movement (hence ease &lt;em&gt;in&lt;/em&gt; and &lt;em&gt;out&lt;/em&gt;) - however currently it only moves back at the end. This is because at the start, our selector is already at max value: &lt;code&gt;100&lt;/code&gt;. If we want to see the move back at the start, we need to leave room for the selector to &lt;em&gt;move back to&lt;/em&gt;. Therefore, I should set my starting value to &lt;code&gt;90&lt;/code&gt; instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;easeInOutBack(inTime, 90, -90, frameDur)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftaxxc207fzzpwfflj493.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftaxxc207fzzpwfflj493.gif" alt="Cherry GIF 04" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perfect! Now that I have fixed everything for the function to run correctly, I can use it to animate in and out by creating a new &lt;code&gt;aniOut&lt;/code&gt; variable, and an &lt;code&gt;if&lt;/code&gt; statement like before:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;easeInOutBack&lt;/code&gt; full expression with .jsx file&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const penner = footage("Penner Easing Functions.jsx").sourceData.getFunctions();

var frames = 1 / thisComp.frameDuration;
var delay = 2;
var dur = 10;

var indexDelay = textIndex * (delay/frames);
var frameDur = dur/frames;
var inTime = clamp(time - indexDelay, 0, frameDur);
var outTime = clamp(time - 1.5 - indexDelay, 0, frameDur);

//Functions
var aniIn = penner.easeInOutBack(inTime, 90, -90, frameDur)
var aniOut = penner.easeInOutBack(outTime, 0, 90, frameDur)

if (time &amp;lt; 1.5) aniIn
    else aniOut
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;easeInOutBack&lt;/code&gt; full expression without .jsx file&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function easeInOutBack (t, b, c, d, s) {
            if (s == undefined) s = 1.70158;
            if ((t/=d/2) &amp;lt; 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
            return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
        };

var frames = 1 / thisComp.frameDuration;
var delay = 2;
var dur = 10;

var indexDelay = textIndex * (delay/frames);
var frameDur = dur/frames;
var inTime = clamp(time - indexDelay, 0, frameDur);
var outTime = clamp(time - 1.5 - indexDelay, 0, frameDur);

//Functions
var aniIn = easeInOutBack(inTime, 90, -90, frameDur)
var aniOut = easeInOutBack(outTime, 0, 90, frameDur)

if (time &amp;lt; 1.5) aniIn
    else aniOut
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8mgr0f4ei4xv14bb9cxm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8mgr0f4ei4xv14bb9cxm.gif" alt="Cherry GIF 05" width="1080" height="1080"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And with that, we have a good foundation to begin experimenting with Penner's other custom easing functions.&lt;/p&gt;

&lt;p&gt;Try using other functions, other properties, and seeing how the &lt;code&gt;delay&lt;/code&gt; and &lt;code&gt;dur&lt;/code&gt; values can alter the effects. Get creative - and if you've made it this far, congratulations! You are now a master of the After Effects text animator.&lt;br&gt;
&lt;/p&gt;
&lt;br&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;All examples covered here in this tutorial are achieved using predominantly 1 property: position (with a little helping hand from opacity), only 2 of Penner's Easing functions, and the range or expression selectors. There is still a lot of room for experimentation - for instance, &lt;em&gt;combining&lt;/em&gt; the use of range selectors and expression selectors.&lt;/p&gt;

&lt;p&gt;As I said before, the text animator tool is &lt;strong&gt;powerful&lt;/strong&gt;. It can create so many unique and incredible animations, and the best part about using expression selectors is that they leave the text layers keyframe-less, and editable - perfect for creating dynamic templates.&lt;/p&gt;

&lt;p&gt;I hope this article has been inspiring, and given you the tools to play with your own animations.&lt;/p&gt;

&lt;p&gt;As always, do you have any questions? Perhaps you know of a better way to achieve this? Let me know in the comments. I'd also love to see what you create!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahcq6slx68scuny1chii.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahcq6slx68scuny1chii.gif" alt="Image description" width="1280" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>design</category>
      <category>aftereffects</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>After Effects: For Loops</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Wed, 07 Aug 2024 10:52:09 +0000</pubDate>
      <link>https://dev.to/kocreative/after-effects-favourite-expressions-part-4-for-loops-18cp</link>
      <guid>https://dev.to/kocreative/after-effects-favourite-expressions-part-4-for-loops-18cp</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Have you ever been building an After Effects template, wanting to use expressions to automate your motion, but found it tricky to write your script accounting for the ever changing values your template may contain? For instance, a constantly changing minimum and maximum slider value. Or an array, or a .csv file with a distinctly different number of entries/rows.&lt;/p&gt;

&lt;p&gt;One of the hardest things about building template projects is accounting for these mystery parameters. The more limited the template, the shorter its lifespan and functionality. So it's always in our best interests to make it as efficient as possible.&lt;/p&gt;

&lt;p&gt;For this reason, I find myself using &lt;code&gt;for&lt;/code&gt; loops.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are &lt;code&gt;for&lt;/code&gt; Loops?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;for&lt;/code&gt; loops allow After Effects to run the same code a set amount of times, each time using a different value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (arg 1, arg 2, arg3){
    //execution
    };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first argument runs before the loop begins, the second argument sets the condition when the loop should run, and the third argument runs every time after the loop is executed. For a more in depth analysis of the function itself, you can read more &lt;a href="https://www.w3schools.com/js/js_loop_for.asp" rel="noopener noreferrer"&gt;here where w3school&lt;/a&gt; breaks down the function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example Of &lt;code&gt;for&lt;/code&gt; Loop In Action
&lt;/h2&gt;

&lt;p&gt;So, what can &lt;code&gt;for&lt;/code&gt; loops be used for in After Effects? They can be used effectively to cycle through array values, but on top of that they can be used to cycle through keyframes, and other such data we may want to access.&lt;/p&gt;

&lt;p&gt;Take this example from my &lt;a href="https://dev.to/kocreative/project-using-after-effects-expressions-to-generate-character-lip-sync-16ia"&gt;character lip sync&lt;/a&gt; project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var audio = thisComp.layer("Audio Amplitude").effect("Both Channels")("Slider");
let maxV = 0;
let minV = 0;
for (let i = 1; i &amp;lt;= audio.numKeys; i++){
    maxV = Math.max(maxV, audio.key(i).value);
    minV = Math.min(minV, audio.key(i).value);
    };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, I list my first argument establishing the variable &lt;code&gt;i&lt;/code&gt;, and set it to 1. My second argument, which defines when the loop runs, is set to &lt;code&gt;i &amp;lt;= audio.numKeys&lt;/code&gt;. This means the loop will only run when &lt;code&gt;i&lt;/code&gt; is less than or equal to the number of keyframes present on my audio slider. Finally, the third argument is set to &lt;code&gt;i++&lt;/code&gt;, adding 1 to &lt;code&gt;i&lt;/code&gt; at the end of every loop.&lt;/p&gt;

&lt;p&gt;While the loop is active, it affects the value of my &lt;code&gt;maxV&lt;/code&gt; and &lt;code&gt;minV&lt;/code&gt; variables. As &lt;code&gt;i&lt;/code&gt; increases, the loop checks the value of each keyframe in turn, and adjusts the variables depending on whether or not the value is higher or lower than the current &lt;code&gt;maxV&lt;/code&gt; or &lt;code&gt;minV&lt;/code&gt; values. Then, once &lt;code&gt;i&lt;/code&gt; is more than the number of keyframes present on my audio slider, the loop stops. This leaves my &lt;code&gt;maxV&lt;/code&gt; and &lt;code&gt;minV&lt;/code&gt; variables equal to the highest and lowest keyframe values on the audio slider respectively.&lt;/p&gt;

&lt;p&gt;No matter how many keyframes or how long the timeline is, this loop is able to work out the maximum and minimum values inputted into the audio slider without any errors. This is ideal for this template, as it is designed to be used for many different audio tracks, with varying volume and track lengths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;for&lt;/code&gt; Loops With &lt;code&gt;time&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;for&lt;/code&gt; loops can also be used in timers. Take this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var copy = ["apple", "banana", "pear", "peach", "melon", "grapes"];
var copyLength = copy.length;
var dur = time;

let num = 0;
for (i = 0; i &amp;lt;= dur; i++){
    if (true) num++;
}

copy[clamp(num, 0, copyLength-1)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When inputted into the source text parameter, this example will cycle through the text in the &lt;code&gt;copy&lt;/code&gt; array, displaying a new entry every second, and stopping on the last entry. Let's go through how it does this.&lt;/p&gt;

&lt;p&gt;First, I set up the array. Create the array and input the text to be shown on screen. Make sure the text is inside &lt;code&gt;""&lt;/code&gt; so that After Effects knows it is text, and not a variable. I then create another variable, &lt;code&gt;copyLength&lt;/code&gt;, to determine how long the array is. Doing this instead of using a static integer will allow the expression to update, no matter the number of entries in the &lt;code&gt;copy&lt;/code&gt; array. I created the variable &lt;code&gt;dur&lt;/code&gt;, aka my line duration. Set to &lt;code&gt;time&lt;/code&gt;, the text will change every second. Finally, I set my last variable, &lt;code&gt;num&lt;/code&gt;, to 0, because array index numbers start at 0.&lt;/p&gt;

&lt;p&gt;Now that the variables are all set up, I can create the &lt;code&gt;for&lt;/code&gt; loop. First, I create the variable &lt;code&gt;i&lt;/code&gt; and set it to 0 (to match the &lt;code&gt;num&lt;/code&gt; variable). For the second argument, I specify &lt;code&gt;i &amp;lt;= dur&lt;/code&gt;, meaning the loop will be active only when &lt;code&gt;i&lt;/code&gt; is less than the specified duration time. My third argument is once again &lt;code&gt;i++&lt;/code&gt;, adding 1 to the &lt;code&gt;i&lt;/code&gt; variable at the end of every loop.&lt;/p&gt;

&lt;p&gt;While the loop is active, it will add 1 to the &lt;code&gt;num&lt;/code&gt; variable. Effectively, creating a counter which will increase in value by 1, every second of the composition timeline.&lt;/p&gt;

&lt;p&gt;Now that I have my counting variable, I can finish the expression.&lt;/p&gt;

&lt;p&gt;However, if I left the expression at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;copy[num]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it could cause an error once the &lt;code&gt;num&lt;/code&gt; variable counts higher than the number of values in the &lt;code&gt;copy&lt;/code&gt; array. To avoid this, I &lt;code&gt;clamp()&lt;/code&gt; the value. &lt;code&gt;clamp()&lt;/code&gt; allows me to set the minimum and maximum values of a variable. So, by setting the &lt;code&gt;num&lt;/code&gt; variable like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;copy[clamp(num, 0, copyLength-1)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it cannot go lower than 0, or higher than &lt;code&gt;copyLength-1&lt;/code&gt;. Because array index's start at 0, and the length of a variable is measured starting at 1, I need to -1 from the &lt;code&gt;copyLength&lt;/code&gt; to match the last index value of the array.&lt;/p&gt;

&lt;p&gt;And there you have it: a way for After Effects to cycle through text (completely without keyframes or sliders).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uhf0koreno5nnjn89xs.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5uhf0koreno5nnjn89xs.gif" alt="GIF of above expression working in After Effects" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By changing the &lt;code&gt;dur&lt;/code&gt; variable you can adjust the amount of time each array entry is on screen. For instance, setting &lt;code&gt;dur = time / 2&lt;/code&gt; will double the time each entry is on screen, so that each entry is on for 2 seconds.&lt;/p&gt;

&lt;p&gt;You can also connect this to a .csv file instead of manually typing an array. However this is slightly more complex, and a topic worthy of its own article.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;for&lt;/code&gt; loops can be tricky, but are worth understanding when writing your own After Effects expressions. They can enable powerful flexibility when creating templates, and allow us to work around ever changing variables.&lt;/p&gt;

&lt;p&gt;Find this article helpful? Have any questions? Know a better way to use &lt;code&gt;for&lt;/code&gt; loops in After Effects that I missed? Let me know in the comments.&lt;/p&gt;

</description>
      <category>design</category>
      <category>aftereffects</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>After Effects: The If Statement</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Sat, 03 Aug 2024 08:54:50 +0000</pubDate>
      <link>https://dev.to/kocreative/after-effects-favourite-expressions-part-3-the-if-statement-58nl</link>
      <guid>https://dev.to/kocreative/after-effects-favourite-expressions-part-3-the-if-statement-58nl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Last time I covered a pretty complex topic, introducing custom functions into After Effects with Penner's easing functions. I'll like to take a &lt;em&gt;big&lt;/em&gt; step back now, and talk about &lt;code&gt;if&lt;/code&gt; statements.&lt;/p&gt;

&lt;p&gt;So, what are &lt;code&gt;if&lt;/code&gt; statements?&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic &lt;code&gt;if&lt;/code&gt; Statements
&lt;/h2&gt;

&lt;p&gt;First - if you dabble in After Effects expressions or have any sort of coding experience, you more than likely are familiar with &lt;code&gt;if&lt;/code&gt; statements. &lt;code&gt;if&lt;/code&gt; statements allow you to tell After Effects to check for certain criteria within the project, and execute a command based on that criteria. An example of this could be linking a layer's opacity to a checkbox control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (effect("Checkbox Control")("Checkbox") == 1) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this statement in place, when the checkbox control is turned &lt;strong&gt;on&lt;/strong&gt;, the layer will be seen at 100% opacity. However, if the control is &lt;strong&gt;off&lt;/strong&gt;, the opacity will be set to 0%, and the layer will be completely transparent. This can be a useful control when making .mogrt files, or, connecting multiple layers to a single, keyframe-able parameter.&lt;/p&gt;

&lt;p&gt;The ability to check for criteria is what makes &lt;code&gt;if&lt;/code&gt; statements a powerful tool in your arsenal, even at a basic level. But they can also help enable more sophisticated code for more complex situations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking For Additional Criteria
&lt;/h2&gt;

&lt;p&gt;Sometimes, there may be cause for checking more than one argument in order to return a certain value. For instance, what if I only want a drop shadow layer in a template to be visible when a) there is text inputted into a text layer, and b) the drop shadow checkbox control switch is engaged?&lt;/p&gt;

&lt;p&gt;This is possible with the &lt;em&gt;and&lt;/em&gt; operator: &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt;. Adding this between the &lt;code&gt;if&lt;/code&gt; statement arguments tells After Effects we want both of these things to be true, in order to return a specific result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (thisComp.layer("test").text.sourceText != "" &amp;amp;&amp;amp; effect("Checkbox Control")("Checkbox") == 1) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;if&lt;/code&gt; statement checks both arguments, making sure the "test" text layer is &lt;strong&gt;not&lt;/strong&gt; empty, and that the checkbox control is &lt;strong&gt;on&lt;/strong&gt;, before returning an opacity value. In this case, if both of those arguments are true, it will return 100, but if &lt;strong&gt;neither or both are false&lt;/strong&gt;, it will return 0.&lt;/p&gt;

&lt;p&gt;Another helpful operator to consider is the &lt;em&gt;or&lt;/em&gt; operator: &lt;code&gt;||&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (thisComp.layer("test").text.sourceText != "" || effect("Checkbox Control")("Checkbox") == 1) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this operator, After Effects will return the first value in the statement if &lt;strong&gt;one or both of the statements are true&lt;/strong&gt;. Only when both are false, will it return the &lt;code&gt;else&lt;/code&gt; value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Nesting &lt;code&gt;if&lt;/code&gt; Statements
&lt;/h2&gt;

&lt;p&gt;But what if there is a more complex situation, where there are more than 2 potential answers to an argument? If you need to check for an array of criteria, each with its own potential solution, you may need to create a nested &lt;code&gt;if&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;For example, I have a text layer which should contain a number between 0 and 4. Depending on what number is displayed, the solid layer behind it will change opacity (0 = 0%, 1 = 25%, 2 = 50%, 3 = 75%, 4 = 100%). However, the solid layer should also check to see whether or not a checkbox control is on. If it is not, the layer shouldn't be visible at all, regardless of the text layer's number.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;copy = thisComp.layer("text").text.sourceText;
ctrl = effect("Checkbox Control")("Checkbox");

if (ctrl == 1)
    if (copy == "0") 0
            else if (copy == "1") 25
            else if (copy == "2") 50
            else if (copy == "3") 75
            else if (copy == "4") 100
            else value
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break this down.&lt;/p&gt;

&lt;p&gt;First, I make a variable for my text layer, and my checkbox control. Then, I begin my nested &lt;code&gt;if&lt;/code&gt; statement. Because there are more than 2 possible outcomes for this scenario, using &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; in this case would be encumbersome, forcing After Effects to check the checkbox over and over again unnecessarily. Instead, only the first statement checks to see if the checkbox control is on. Then, I create a second &lt;code&gt;if&lt;/code&gt; statement inside the first, which checks what number the text layer is displaying. From there, I create enough &lt;code&gt;else&lt;/code&gt; &lt;code&gt;if&lt;/code&gt; arguments to cover all possible outcomes. Once all text layer outcomes are accounted for, I finish the statement by writing the &lt;code&gt;else&lt;/code&gt; argument for the checkbox control.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fblhxmvaxrl69objk01oa.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fblhxmvaxrl69objk01oa.gif" alt="GIF showing how the opacity changes cycling through numbers 0 to 4, and also turning the checkbox switch on and off" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Scripting decision making possibilities with After Effects &lt;code&gt;if&lt;/code&gt; statements allows for unique and tailored templates. Whether these expressions are written to streamline keyframe animation, to create a .mogrt file, or are there to react to data from an external source, they provide a level of dynamic adaptability to projects otherwise not possible.&lt;/p&gt;

&lt;p&gt;Was this useful? Any questions? Feel free to ask in the comments.&lt;/p&gt;

</description>
      <category>aftereffects</category>
      <category>beginners</category>
      <category>design</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Project: Using After Effects Expressions To Generate Character Lip Sync</title>
      <dc:creator>Kat</dc:creator>
      <pubDate>Fri, 26 Jul 2024 11:17:03 +0000</pubDate>
      <link>https://dev.to/kocreative/project-using-after-effects-expressions-to-generate-character-lip-sync-16ia</link>
      <guid>https://dev.to/kocreative/project-using-after-effects-expressions-to-generate-character-lip-sync-16ia</guid>
      <description>&lt;h2&gt;
  
  
  Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Create Your Lip Flaps&lt;/li&gt;
&lt;li&gt;Importing Into After Effects&lt;/li&gt;
&lt;li&gt;Convert Audio To Keyframes&lt;/li&gt;
&lt;li&gt;Creating The Expression&lt;/li&gt;
&lt;li&gt;Working Out The Audio Range&lt;/li&gt;
&lt;li&gt;Final &lt;code&gt;if&lt;/code&gt; Statements&lt;/li&gt;
&lt;li&gt;Accounting For Audio Peaks&lt;/li&gt;
&lt;li&gt;Final Step&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Lip sync can be one of the most painful parts of character animation. But if you're looking for a simple solution to sell in a character speaking, you may find that using expressions in After Effects can help automate the process.&lt;/p&gt;

&lt;p&gt;Here's how I do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Your Lip Flaps&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;First, create your lip flaps. This can be as simple as a line and 2 stretched out circles, one horizontally and the other vertically. Feel free to add more shape and detail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1liy68vo26t6vcgvi4hd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1liy68vo26t6vcgvi4hd.png" alt="Screenshot of lip flap layers in illustrator" width="800" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing Into After Effects&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Start a new After Effects project, and import your lip flaps and the audio you want to create lip sync for.&lt;/p&gt;

&lt;p&gt;Create a new composition and drag all of these assets into your timeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Convert Audio To Keyframes&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;We have one last thing to do before we can start writing our expression.&lt;/p&gt;

&lt;p&gt;Right click on your audio layer, and select &lt;code&gt;Keyframe Assistant&lt;/code&gt; &amp;gt; &lt;code&gt;Convert Audio To Keyframes&lt;/code&gt;. This will create a new layer called Audio Amplitude. You'll see this layer comes with 3 sliders: &lt;code&gt;Left Channel&lt;/code&gt;, &lt;code&gt;Right Channel&lt;/code&gt;, and &lt;code&gt;Both Channels&lt;/code&gt;. We only need the &lt;code&gt;Both Channels&lt;/code&gt; effect, so feel free to delete the other 2. If you open up the effect, you will see that there is a new keyframe for every frame of the composition.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating The Expression&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now the fun bit! Writing the expression. It's important to understand &lt;em&gt;what&lt;/em&gt; we need to tell After Effects, in order to know &lt;em&gt;how&lt;/em&gt; we write our code.&lt;/p&gt;

&lt;p&gt;Essentially, we want to work out the range of our audio, from lowest point to highest. We can then use this range to control the opacity of our lip flaps. When the audio is mute, the closed mouth option should be visible. If the audio is in the first half of the audio range, the horizontal mouth option should be visible. And if the audio is in the second half of the audio range, the vertical mouth option should be visible. This will give the illusion of a lip sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working Out The Audio Range&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;An easy way to work out the range of our audio is to do a quick check. We can run our cursor through our timeline and check the value of all the keyframes in the &lt;code&gt;Both Channels&lt;/code&gt; slider manually. However if your audio track is long, or you're working at a high framerate, you can easily miss values. So it would be better in this circumstance if we got After Effects to do the checking for us.&lt;/p&gt;

&lt;p&gt;We can do this using some &lt;code&gt;for&lt;/code&gt; loops from within the opacity parameter of all of our lip flap layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;audio = thisComp.layer("Audio Amplitude").effect("Both Channels")("Slider");

maxV = 0;
minV = 0;
for (i = 1; i&amp;lt;=audio.numKeys; i++){
    maxV = Math.max(maxV, audio.key(i).value);
    minV = Math.min(minV, audio.key(i).value);
    };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we reference the &lt;code&gt;Both Channels&lt;/code&gt; slider. Then, create a variable to store your max and min values in: &lt;code&gt;maxV&lt;/code&gt; and &lt;code&gt;minV&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The arguments in our &lt;code&gt;for&lt;/code&gt; loop are set to &lt;code&gt;i = 1; i&amp;lt;=audio.numKeys; i++&lt;/code&gt;. This creates the variable &lt;code&gt;i&lt;/code&gt; for our loop, and uses it to check every keyframe on the &lt;code&gt;Both Channels&lt;/code&gt; slider. The loop then stops when it reaches the last keyframe.&lt;/p&gt;

&lt;p&gt;Let's start with the &lt;code&gt;maxV&lt;/code&gt; value. While looping, the &lt;code&gt;for&lt;/code&gt; loop checks &lt;code&gt;maxV&lt;/code&gt;. It uses the &lt;code&gt;Math.max()&lt;/code&gt; function to compare our current &lt;code&gt;maxV&lt;/code&gt; value to the current keyframe value. If the keyframe value is bigger than &lt;code&gt;maxV&lt;/code&gt;, it updates &lt;code&gt;maxV&lt;/code&gt; to be that value.&lt;/p&gt;

&lt;p&gt;We then do the same to work out our minimum value, using &lt;code&gt;Math.min()&lt;/code&gt; on the next line in the loop. In most cases this value will be 0, but running this line can account for background noise, or negative values in the audio track.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final &lt;code&gt;if&lt;/code&gt; Statements&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;We now have the values we need to work out our audio range. In order to return the correct opacity value, we need to provide an &lt;code&gt;if&lt;/code&gt; statement for our 3 lip flap layers.&lt;/p&gt;

&lt;p&gt;First, the statement for our closed mouth layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (audio &amp;lt; .1) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, I have specified that if the value of the &lt;code&gt;audio&lt;/code&gt; variable is under &lt;code&gt;0.1&lt;/code&gt; the audio of this comp should be considered mute. Therefore the closed mouth opacity is set to 100. If the variable's value is higher than &lt;code&gt;0.1&lt;/code&gt;, the opacity will be set to 0. Because our final &lt;code&gt;if&lt;/code&gt; statement for this layer doesn't require working out the audio range, the &lt;code&gt;for&lt;/code&gt; loops can be deleted from this layer to help with the project's efficiency. Make sure to substitute &lt;code&gt;0.1&lt;/code&gt; for a value which makes sense for your project, if necessary.&lt;/p&gt;

&lt;p&gt;Next, our vertical mouth layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (audio &amp;lt; (maxV - minV)/2) 0
    else 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This layer will only be visible in the top range of our audio. If the &lt;code&gt;audio&lt;/code&gt; variable is less than half of our audio range, this layer's opacity will be set to 0. This accounts for when our audio is mute, and in its first half of the audio range. However, when the &lt;code&gt;audio&lt;/code&gt; variable is equal to or higher than half of our audio range, the opacity is set to 100.&lt;/p&gt;

&lt;p&gt;Lastly, our horizontal mouth layer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (audio &amp;lt; .1) 0
    else
        if (audio &amp;lt; (maxV - minV)/2) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The horizontal mouth layer needs to account for when the vertical mouth layer and the closed mouth layer is on. Therefore, the &lt;code&gt;if&lt;/code&gt; statement needs to check for both of these possibilities. Combining our last 2 &lt;code&gt;if&lt;/code&gt; statements into 1 achieves this, making sure we set the correct argument to 100. Since we want the layer to be visible in the first half of our audio range, we want to set the argument &lt;code&gt;if (audio &amp;lt; (maxV - minV)/2)&lt;/code&gt; to 100, and all others to 0.&lt;/p&gt;

&lt;h1&gt;
  
  
  Accounting For Audio Peaks&lt;a&gt;&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;You might at this point there is less variation in the horizontal and vertical mouth flaps than you would like. This may mean there are points in the &lt;code&gt;Both Channels&lt;/code&gt; slider which are skewing the range, since we take the maximum and minimum values of the entire audio track. Because of this, we might want to finesse the scale a little bit to account for peaks.&lt;/p&gt;

&lt;p&gt;I did this by adding a slider to my expression. I made a null layer and added the slider there, then named it &lt;code&gt;Max Value Adjust&lt;/code&gt;. I made a variable to store the slider in both the horizontal and vertical mouth layers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;endV = thisComp.layer("SLIDERS").effect("Max Value Adjust")("Slider");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And added it to their &lt;code&gt;if&lt;/code&gt; statements like so:&lt;br&gt;
&lt;strong&gt;Horizontal if statement&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (audio &amp;lt; .1) 0
    else
        if (audio &amp;lt; ((maxV + endV) - minV)/2) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Vertical if statement&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (audio &amp;lt; ((maxV + endV) - minV)/2) 0
    else 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the slider is in place, it can be used to adjust the &lt;code&gt;maxV&lt;/code&gt; of our audio range. You can do it by eye, dragging the slider into minus values until you're happy with the look of your lip flaps.&lt;/p&gt;

&lt;p&gt;You can also follow the same process to add a slider to the &lt;code&gt;minV&lt;/code&gt; value of the audio range, if you need that flexibility. To do this, create another slider and add it to the &lt;code&gt;if&lt;/code&gt; statements of your closed mouth and horizontal mouth layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Closed Mouth If Statement&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (audio &amp;lt; .1 + startV) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Horizontal If Statement&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (audio &amp;lt; .1 + startV) 0
    else
        if (audio &amp;lt; ((maxV + endV) - minV)/2) 100
    else 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Step&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Now, you should have your lip flaps moving in time to your audio. However, you may find that there are some flash frames, given that the &lt;code&gt;Both Channels&lt;/code&gt; slider has a keyframe on every frame of the composition. To mitigate this, create an adjustment layer and add the &lt;code&gt;Posterize Time&lt;/code&gt; effect to it. Set the value to half of your composition's frame rate to start. Then, if you still feel there is some flashing, gradually take the number down until you're happy with the rate the lip flaps change. In my composition set to 25fps, I tend to set &lt;code&gt;Posterize Time&lt;/code&gt; to 10.&lt;/p&gt;

&lt;p&gt;And that's it! Generated, simple lip sync for when you need to spend your time elsewhere in your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomvh2jr536vwhopxgrlf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomvh2jr536vwhopxgrlf.gif" alt="GIF of lip sync animation" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Please leave a comment if you have any questions, or know of a more efficient way of achieving this.&lt;/p&gt;

</description>
      <category>aftereffects</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>design</category>
    </item>
  </channel>
</rss>
