Resources

Practical tools and references for building Styles

After Effects Expressions

Almost every property in After Effects can be extended with expressions. Hold ALT/OPTION and click on the stop-watch to add expressions to a property.

Below you’ll find a series of powerful expressions to make your Styles more dynamic.

Link to controller preserving animation shape
// Paste it on a property with two keyframes, to link it to a controller on the comp.
// You can add an slider called time_modifier to stretch or reduce the animation; if the slider is at 100 it will play at 100% the speed of the controller.
// --- SETUP ---
// Main controller that drives the animation
var controller = thisComp.layer("controller").effect("enter")("Slider");

// Optional "time_modifier" slider on this layer.
// Defaults to 100 (100%) if the effect doesn't exist.
var timeModifierValue = 100; // Default value
try {
  // Try to get the value from the slider effect on this layer
  timeModifierValue = thisLayer.effect("time_modifier")("Slider").value;
} catch (e) {
  // If the effect doesn't exist, we'll just use the default 100. No error will be thrown.
  timeModifierValue = 100;
}


// --- CALCULATIONS ---
// Convert the percentage slider (e.g., 200) to a multiplier (e.g., 2.0)
var durationMultiplier = timeModifierValue / 100;

// Get the original start and end times from the controller's keyframes
var controllerStart = controller.key(1).time;
var controllerEnd = controller.key(2).time;
var controllerDuration = controllerEnd - controllerStart;

// Calculate the new, modified end time for our animation
// This takes the original duration, scales it, and adds it to the start time.
var newAnimationEnd = controllerStart + (controllerDuration * durationMultiplier);

// Calculate the progress (a value from 0 to 1) based on the *new* time range
var progress = linear(time, controllerStart, newAnimationEnd, 0, 1);

// Get the start and end times of the original keyframes on this property
var animStart = thisProperty.key(1).time;
var animEnd = thisProperty.key(thisProperty.numKeys).time;

// --- OUTPUT ---
// Map the 0-1 progress to the time span of the original animation
var newTime = linear(progress, 0, 1, animStart, animEnd);

// Return the value of the original animation at this remapped time
valueAtTime(newTime);
Link textBlock to shot-controller
// Paste it on a property with two keyframes, to link it to a controller on the comp.
// You can add an slider called time_modifier to stretch or reduce the animation; if the slider is at 100 it will play at 100% the speed of the controller.
// --- SETUP ---
// Main controller that drives the animation
var shotName = thisComp.name.split("-text")[0]
var controller = comp(shotName).layer("controller").effect("enter")("Slider");

// Optional "time_modifier" slider on this layer.
// Defaults to 100 (100%) if the effect doesn't exist.
var timeModifierValue = 100; // Default value
try {
  // Try to get the value from the slider effect on this layer
  timeModifierValue = thisLayer.effect("time_modifier")("Slider").value;
} catch (e) {
  // If the effect doesn't exist, we'll just use the default 100. No error will be thrown.
  timeModifierValue = 100;
}


// --- CALCULATIONS ---
// Convert the percentage slider (e.g., 200) to a multiplier (e.g., 2.0)
var durationMultiplier = timeModifierValue / 100;

// Get the original start and end times from the controller's keyframes
var controllerStart = controller.key(1).time;
var controllerEnd = controller.key(2).time;
var controllerDuration = controllerEnd - controllerStart;

// Calculate the new, modified end time for our animation
// This takes the original duration, scales it, and adds it to the start time.
var newAnimationEnd = controllerStart + (controllerDuration * durationMultiplier);

// Calculate the progress (a value from 0 to 1) based on the *new* time range
var progress = linear(time, controllerStart, newAnimationEnd, 0, 1);

// Get the start and end times of the original keyframes on this property
var animStart = thisProperty.key(1).time;
var animEnd = thisProperty.key(thisProperty.numKeys).time;

// --- OUTPUT ---
// Map the 0-1 progress to the time span of the original animation
var newTime = linear(progress, 0, 1, animStart, animEnd);

// Return the value of the original animation at this remapped time
valueAtTime(newTime);

textBlock

Word Reveal (cumulative)
// Place on Range Selector's START property.
// Animator: Opacity 0 | Based On: Words | Units: Percentage
// Comp markers carry percentage values as comments (e.g. "0", "10", "20"… "100").
// Words reveal cumulatively as the playhead crosses each marker.
var m = thisComp.marker;
var n = m.numKeys;
if (n < 1) {
    0;
} else {
    var t = time;
    if (t <= m.key(1).time) {
        parseFloat(m.key(1).comment);
    } else if (t >= m.key(n).time) {
        parseFloat(m.key(n).comment);
    } else {
        var idx = m.nearestKey(t).index;
        if (m.key(idx).time > t) idx--;
        var t1 = m.key(idx).time;
        var t2 = m.key(idx + 1).time;
        var v1 = parseFloat(m.key(idx).comment);
        var v2 = parseFloat(m.key(idx + 1).comment);
        linear(t - t1, 0, t2 - t1, v1, v2);
    }
}
Word Spotlight (one word at a time)
// Requires TWO Range Selectors on one Animator (Opacity: 0, Based On: Words, Percentage).
// Range Selector 1: Start=0, End=100, Mode=Add (hides all words).
// Range Selector 2: Mode=Subtract (reveals the current word).
// Comp markers carry percentage values as comments (e.g. "0", "10", "20"… "100").

// ---- Place on Range Selector 2 → START ----
var m = thisComp.marker;
var n = m.numKeys;
if (n < 1) { 0; }
else {
    var t = time;
    if (t <= m.key(1).time) parseFloat(m.key(1).comment);
    else if (t >= m.key(n).time) parseFloat(m.key(n).comment);
    else {
        var idx = m.nearestKey(t).index;
        if (m.key(idx).time > t) idx--;
        parseFloat(m.key(idx).comment);
    }
}

// ---- Place on Range Selector 2 → END ----
var m = thisComp.marker;
var n = m.numKeys;
if (n < 2) { 100; }
else {
    var t = time;
    if (t >= m.key(n).time) parseFloat(m.key(n).comment);
    else if (t <= m.key(1).time) parseFloat(m.key(Math.min(2, n)).comment);
    else {
        var idx = m.nearestKey(t).index;
        if (m.key(idx).time > t) idx--;
        parseFloat(m.key(Math.min(idx + 1, n)).comment);
    }
}
Link to Trinity using two keyframes
let aKey = thisProperty.key(1);
let bKey = thisProperty.key(2);
let flow = comp("mainComp").layer("$").effect("FLOW")("Slider");
let form = comp("mainComp").layer("$").effect("FORM")("Slider");
linear(flow,0,100,aKey,bKey)
Modify a color using FORM
// Put this expression on a color link it's lightness,hue and/or saturation to FORM'
color = comp("mainComp").layer("$").effect("1")("Color");
form = comp("mainComp").layer("$").effect("FORM")("Slider")
hsl = rgbToHsl(color);
lightFactor = linear(form,0,100,1,0)
hsl[2] = Math.min(1, hsl[2] + lightFactor);
hslToRgb(hsl);