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
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
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);