← Tools

Expresso

Beta v1.0.0 coming soon

Store, organize, copy, and apply After Effects expressions.

v0.3.1 · updated Jul 13, 2026 ·

All 90 expressions
Basic Wiggle
freq = 2; …
Random organic motion around the current value. First number = wiggles/sec, second = amount.
Customize
freq = 2;
amount = 30;
wiggle(freq, amount)
Wiggle X Only
freq = 2; …
Wiggle horizontally while keeping the Y value locked. Apply to Position.
freq = 2;
amount = 30;
x = wiggle(freq, amount)[0];
[x, value[1]]
Wiggle Scale (uniform)
freq = 1.5; …
Wiggle scale evenly on both axes so it doesn't distort. Apply to Scale.
freq = 1.5;
amount = 12;
s = wiggle(freq, amount)[0];
[s, s]
Frozen Random per Layer
min = 0; …
One stable random value per layer that doesn't flicker every frame. Great for offsets.
min = 0;
max = 100;
seedRandom(index, true);
random(min, max)
Seamless Looping Wiggle
freq = 1; …
A wiggle that loops perfectly every loopTime seconds by cross-blending the wiggle with a time-shifted copy of itself. Apply to any property. freq = wiggles/sec, amp = amount, loopTime = seconds per seamless loop. Uses wiggle's optional 5th time argument.
freq = 1;
amp = 60;
loopTime = 3;
t = time % loopTime;
w1 = wiggle(freq, amp, 1, 0.5, t);
w2 = wiggle(freq, amp, 1, 0.5, t - loopTime);
linear(t, 0, loopTime, w1, w2)
Handheld Camera Shake
freq = 3; …
Multi-octave wiggle that feels like a handheld camera (fine detail layered on broad motion). Apply to Position (or a camera/null's Position). freq = base frequency, amp = amount, octaves = layers of detail (3 gives a natural jitter).
freq = 3;
amp = 25;
octaves = 3;
wiggle(freq, amp, octaves, 0.6)
Wiggle That Ramps Up
freq = 3; …
Starts calm and intensifies: the wiggle amplitude eases from 0 to maxAmp over the first rampTime seconds of the layer. Apply to any property. freq = wiggles/sec, maxAmp = final amount, rampTime = seconds to reach full intensity.
freq = 3;
maxAmp = 80;
rampTime = 2;
amp = linear(time, inPoint, inPoint + rampTime, 0, maxAmp);
wiggle(freq, amp)
Stepped Random (glitch hold)
rate = 4; …
Jumps to a new random value rate times per second and holds it in between, for a digital/glitch feel. Works on any 1D property (Opacity, Rotation, a Slider). rate = jumps/sec, minVal/maxVal = value range. Reseeding by time-step keeps each value steady until the next step.
rate = 4;
minVal = 0;
maxVal = 100;
seedRandom(Math.floor(time * rate), true);
random(minVal, maxVal)
Smooth Noise Drift
freq = 1; …
Smooth, flowing wander using Perlin noise() instead of wiggle — softer and more organic, never jerky. Apply to Position. freq = speed of drift, amp = pixels of travel. Two separate noise fields drive X and Y so they move independently.
freq = 1;
amp = 60;
value + [noise([time * freq, 0]), noise([time * freq, 100])] * amp
Smooth Out Jitter
width = 0.08; …
Averages a property's keyframed values over a short window to remove shake — great for cleaning up jittery imported tracking data or hand-keyed noise. Apply to a keyframed property. width = seconds averaged, samples = how many samples to blend.
width = 0.08;
samples = 5;
smooth(width, samples)
Continuous Rotation
speed = 60; …
Spin forever at a constant speed. The number is degrees per second. Apply to Rotation.
speed = 60;
time * speed
Frame Number
Math.floor(time * thisComp.frameRate)
The current frame as a number — handy as a base for counters.
Math.floor(time * thisComp.frameRate)
Posterize Time (stepped / stop-motion)
steps = 6; …
Forces any time-based expression to update in discrete steps for a choppy stop-motion look. Prefix any expression with posterizeTime(fps); here 6 = updates per second, followed by a wiggle. Apply to any property.
steps = 6;
freq = 8;
amount = 40;
posterizeTime(steps);
wiggle(freq, amount)
Timecode / Time Display
timeToCurrentFormat()
Shows the current time as a timecode string using the project's display format (respects frame rate and drop-frame). Apply to Source Text. Swap in timeToTimecode() to force a fixed HH:MM:SS:FF format.
timeToCurrentFormat()
Countdown Timer (MM:SS)
start = 60; …
Counts down from a number of seconds and displays MM:SS, stopping at 0. Apply to Source Text. start = seconds to count down from.
start = 60;
function pad(n){ n = Math.floor(n).toString(); while (n.length < 2) n = "0" + n; return n; }
t = Math.max(start - time, 0);
pad(Math.floor(t / 60)) + ":" + pad(t % 60)
Strobe / Blink
rate = 8; …
Hard on/off blink at a fixed rate. Apply to Opacity. rate = blinks per second (each cycle is one on and one off). Change the 100/0 to blink between other values.
rate = 8;
Math.floor(time * rate) % 2 == 0 ? 100 : 0
Freeze Value After a Time
freezeTime = 2; …
Lets an animated property play normally, then freezes it on the value it had at freezeTime. Apply to any keyframed/animated property (including Time Remap). freezeTime = seconds (comp time) at which to lock the value.
freezeTime = 2;
time > freezeTime ? valueAtTime(freezeTime) : value
Layer Speed (px/sec)
thisLayer.transform.position.speed
Returns how fast this layer is moving in pixels per second (magnitude of Position velocity). Apply to a Slider Control to read it, or use it to drive motion blur, stretch, etc. The layer needs animated Position for a non-zero result.
thisLayer.transform.position.speed
Loop (Cycle)
loopOut()
Repeat the keyframed animation forever. Needs at least 2 keyframes.
loopOut()
Loop (Ping-Pong)
loopOut("pingpong")
Play the keyframes forward, then backward, forever.
loopOut("pingpong")
Loop (Continue)
loopOut("continue")
Keep going past the last keyframe at its final speed (no repeat).
loopOut("continue")
Loop (Offset)
loopOut("offset")
Repeat the keyframes but keep advancing each cycle — endless forward progress.
loopOut("offset")
Loop In (Cycle)
loopIn()
Loop the animation backward before the first keyframe.
loopIn()
Loop Last Keyframes Only
loopOut("cycle", 1)
Loops only the final segment of a keyframed animation instead of the whole thing. The number counts keyframes back from the last one: 1 = loop the last two keyframes; 0 (or blank) = all keyframes. Needs at least 3 keyframes to differ from a normal loop.
loopOut("cycle", 1)
Loop by Duration
duration = 2; …
Loops the keyframes contained in the last N seconds rather than a keyframe count. Here 2 = loop the final 2 seconds of keyframes; 0 = all. Needs at least 2 keyframes.
duration = 2;
loopOutDuration("cycle", duration)
Loop Video (Time Remap)
loopOut("cycle")
Loops a video or precomp forever. Setup: select the layer, Layer > Time > Enable Time Remapping, keep the two auto-created Time Remap keyframes (or set your own), then apply this to the Time Remap property.
loopOut("cycle")
Repeating 0→1 Ramp (sawtooth)
period = 2; …
A value that ramps 0 to 1 and instantly resets, repeating forever — no keyframes. Perfect for driving looping gradients, offsets, or Trim Paths. period = seconds per cycle. Multiply the result to scale it to any range.
period = 2;
(time % period) / period
Keyframeless Ping-Pong
period = 2; …
A triangle wave that ramps up then down between lo and hi forever, with no keyframes. Apply to any 1D property (Opacity, Rotation, a Slider). period = seconds for a full up-and-down cycle.
period = 2;
lo = 0;
hi = 100;
half = period / 2;
t = time % period;
t < half ? linear(t, 0, half, lo, hi) : linear(t, half, period, hi, lo)
Inertial Bounce
amp = 0.08; …
Adds a natural springy overshoot after the last keyframe. Tune amp / freq / decay.
amp = 0.08;
freq = 2.5;
decay = 3.0;
n = 0;
if (numKeys > 0){
  n = nearestKey(time).index;
  if (key(n).time > time) n--;
}
if (n > 0){
  t = time - key(n).time;
  v = velocityAtTime(key(n).time - 0.001);
  value + v * amp * Math.sin(freq * t * 2 * Math.PI) / Math.exp(decay * t);
} else {
  value;
}
Sine Oscillation
amp = 30; …
Smooth back-and-forth wave added to a 1D value (e.g. Rotation). amp = size, freq = speed.
amp = 30;
freq = 1;
value + amp * Math.sin(time * freq * 2 * Math.PI)
Realistic Gravity Bounce
e = 0.7; …
Physically simulated bouncing after the last keyframe: the incoming velocity is reflected and decays through a series of parabolic hops, like a ball hitting the floor. Apply to Position (works 1D or 2D/3D). e = elasticity (0-1), g = gravity (px/sec^2), nMax = max bounces. Needs at least one keyframe that establishes the impact velocity.
e = 0.7;
g = 5000;
nMax = 9;
n = 0;
if (numKeys > 0){
  n = nearestKey(time).index;
  if (key(n).time > time) n--;
}
if (n > 0){
  t = time - key(n).time;
  v = -velocityAtTime(key(n).time - 0.001) * e;
  vl = length(v);
  if (value instanceof Array){
    vu = (vl > 0) ? normalize(v) : [0, 0, 0];
  } else {
    vu = (v < 0) ? -1 : 1;
  }
  tCur = 0;
  segDur = 2 * vl / g;
  tNext = segDur;
  nb = 1;
  while (tNext < t && nb <= nMax){
    vl *= e;
    segDur *= e;
    tCur = tNext;
    tNext += segDur;
    nb++;
  }
  if (nb <= nMax){
    delta = t - tCur;
    value + vu * delta * (vl - g * delta / 2);
  } else {
    value;
  }
} else {
  value;
}
Damped Pendulum Swing
amp = 40; …
A decaying back-and-forth swing that settles to rest, added on top of the current value. Apply to Rotation (great for hanging signs, arms, tails). amp = starting swing in degrees, freq = swings/sec, decay = how quickly it settles. No keyframes needed; starts at the layer's in-point.
amp = 40;
freq = 1;
decay = 0.8;
t = time - inPoint;
value + amp * Math.sin(freq * t * 2 * Math.PI) / Math.exp(decay * t)
Elastic Scale Pop-In
freq = 18; …
Self-contained elastic entrance: scale springs from 0 up past the target and wobbles into place at the layer's start — no keyframes. Apply to Scale. target = final scale %, freq = wobble speed (radians/sec), decay = settle rate (higher settles faster).
freq = 18;
decay = 6;
target = 100;
t = Math.max(time - inPoint, 0);
s = target * (1 - Math.exp(-decay * t) * Math.cos(freq * t));
[s, s]
Orbit Around Center
radius = 200; …
Circle the composition center. r = radius, speed = rotations/sec. Apply to Position.
radius = 200;
speed = 1;
a = time * speed * 2 * Math.PI;
[thisComp.width / 2 + Math.cos(a) * radius, thisComp.height / 2 + Math.sin(a) * radius]
Center Anchor Point
r = sourceRectAtTime(time, false); …
Keep the anchor point at the center of the layer's content. Apply to Anchor Point.
r = sourceRectAtTime(time, false);
[r.left + r.width / 2, r.top + r.height / 2]
Follow Layer Above (Delay)
delay = 0.1; …
Trail the position of the layer directly above with a time delay. Needs a layer above.
delay = 0.1;
thisComp.layer(index - 1).transform.position.valueAtTime(time - delay)
Point at Another Layer
target = thisComp.layer("Target").transform.position; …
Rotate to face a layer named "Target". Apply to Rotation.
target = thisComp.layer("Target").transform.position;
d = target - transform.position;
radiansToDegrees(Math.atan2(d[1], d[0]))
World Position of Another Layer (toComp)
L = thisComp.layer("Target"); …
Pins this 2D layer to the anchor point of a layer named "Target" in composition space, working even if Target is parented, nested, or 3D. Apply to Position.
L = thisComp.layer("Target");
p = L.toComp(L.transform.anchorPoint);
[p[0], p[1]]
Scale to Fit Comp
sx = thisComp.width / width * 100; …
Scales a footage/solid layer uniformly so it fits inside the composition (contain, keeping aspect ratio). Apply to Scale. width/height are the layer's source pixel dimensions; use Math.max instead of Math.min to fill/cover instead of fit.
sx = thisComp.width / width * 100;
sy = thisComp.height / height * 100;
f = Math.min(sx, sy);
[f, f]
Stagger by Layer Index
delay = 0.1; …
Plays this layer's own keyframed animation delayed by its position in the layer stack, so duplicated layers cascade. Apply to any keyframed property. d = seconds of delay per layer; layer 1 plays with no delay. Needs at least 2 keyframes.
delay = 0.1;
valueAtTime(time - (index - 1) * delay)
Motion Trail (Index Delay)
delay = 0.08; …
Each copy of this layer follows an animated layer named "Master" with a delay that grows by its index, creating a trailing streak. Duplicate this layer several times and apply to Position. d = seconds of delay per copy.
delay = 0.08;
m = thisComp.layer("Master");
m.transform.position.valueAtTime(time - index * delay)
Velocity Squash & Stretch
maxStretch = 30; …
Automatically stretches along the direction of travel and squashes across it, based on vertical speed — the classic cartoon effect. Apply to Scale of a layer with animated Position. maxStretch = max % deformation, soft = speed (px/sec) that produces full stretch.
maxStretch = 30;
soft = 3000;
v = thisLayer.transform.position.velocity[1];
s = clamp(v / soft, -1, 1) * maxStretch;
[100 - s, 100 + s]
Auto-Orient to Motion
v = thisLayer.transform.position.velocity; …
Rotates the layer to point in the direction it is moving (an expression-based Auto-Orient along path). Apply to Rotation of a layer with animated Position. If the artwork points up instead of right by default, add 90 to the result.
v = thisLayer.transform.position.velocity;
radiansToDegrees(Math.atan2(v[1], v[0]))
3D Orbit Around Center
radius = 300; …
Circles the comp center on the horizontal (X-Z) plane so the layer swings toward and away from camera. Requires the layer's 3D switch ON. Apply to Position. radius = orbit size in px, speed = revolutions/sec.
radius = 300;
speed = 0.4;
a = time * speed * 2 * Math.PI;
c = [thisComp.width / 2, thisComp.height / 2, 0];
c + [Math.cos(a) * radius, 0, Math.sin(a) * radius]
Snap Position to Grid
grid = 50; …
Rounds the keyframed Position to the nearest grid cell, giving a stepped, pixel-art / UI feel to otherwise smooth motion. Apply to Position. grid = cell size in pixels.
grid = 50;
[Math.round(value[0] / grid) * grid, Math.round(value[1] / grid) * grid]
Auto Grid Layout by Index
cols = 5; …
Arranges duplicated layers into a tidy grid based on each layer's index — duplicate a layer many times and they fill rows and columns automatically. Apply to Position. cols = columns, cellW/cellH = spacing in px.
cols = 5;
cellW = 200;
cellH = 200;
i = index - 1;
[(i % cols) * cellW + cellW / 2, Math.floor(i / cols) * cellH + cellH / 2]
Breathing / Pulsing Scale
amp = 6; …
Gentle, continuous scale pulse so a static element feels alive. Apply to Scale. amp = pulse size in % (e.g. 6 means 94%-106%), freq = pulses per second.
amp = 6;
freq = 0.4;
s = 100 + amp * Math.sin(time * freq * 2 * Math.PI);
[s, s]
Composition Center
[thisComp.width / 2, thisComp.height / 2]
Snap to the exact middle of the composition. Apply to Position.
[thisComp.width / 2, thisComp.height / 2]
Link to a Slider Control
thisComp.layer("Controls").effect("Speed")("Slider")
Drive this value from a Slider named "Speed" on a "Controls" layer. Adjust the names.
thisComp.layer("Controls").effect("Speed")("Slider")
Distance Between Two Layers
a = thisComp.layer("A").transform.position; …
Returns the pixel distance between the positions of two layers named "A" and "B". Apply to a Slider Control (or to Source Text by adding + ""). Uses length()'s two-point form.
a = thisComp.layer("A").transform.position;
b = thisComp.layer("B").transform.position;
length(a, b)
Number of Layers
thisComp.numLayers
Returns how many layers are in the composition. Apply to a Slider Control, or to Source Text by adding + "".
thisComp.numLayers
Lock to Comp Position Despite Parent
p = [thisComp.width / 2, thisComp.height / 2]; …
Keeps a layer visually locked to a fixed composition point even when it is parented to a moving/rotating/scaling layer. Apply to Position. Here it holds comp center; change the point as needed. Works whether or not the layer currently has a parent.
p = [thisComp.width / 2, thisComp.height / 2];
hasParent ? parent.fromComp(p) : p
Distribute Layers Across Width
first = 100; …
Spreads all layers evenly across the comp horizontally by their stacking index, keeping their own Y. Apply to Position. first/last = left and right x positions to span between. Adapts automatically as you add or remove layers.
first = 100;
last = thisComp.width - 100;
n = thisComp.numLayers;
x = n > 1 ? linear(index, 1, n, first, last) : (first + last) / 2;
[x, value[1]]
Midpoint Between Two Layers
a = thisComp.layer("A").transform.position; …
Sits exactly halfway between two layers named "A" and "B", following them live. Apply to Position (works for 2D or 3D). Great for a label pinned between two moving objects.
a = thisComp.layer("A").transform.position;
b = thisComp.layer("B").transform.position;
(a + b) / 2
Mirror Another Layer's Text
thisComp.layer("Master").text.sourceText
Show the same text as a layer named "Master". Apply to Source Text.
thisComp.layer("Master").text.sourceText
Frame Counter (text)
Math.floor(time * thisComp.frameRate) + ""
Display the current frame number as text. Apply to Source Text.
Math.floor(time * thisComp.frameRate) + ""
Auto-Size Background Box
pad = 20; …
Sizes a shape-layer rectangle to wrap a text layer named "My Text" with padding. Apply to a Rectangle path's Size property. pad = extra pixels around the text. Pair with a Position expression to keep the box centered on the text.
pad = 20;
src = thisComp.layer("My Text");
r = src.sourceRectAtTime(time, false);
[r.width + pad, r.height + pad]
Composition Name (text)
thisComp.name
Displays the current composition's name. Handy for shot slates and versioning. Apply to Source Text.
thisComp.name
Layer Name (text)
name
Displays this layer's own name as its text, so renaming the layer updates the text. Apply to Source Text.
name
Number with Commas
function addCommas(n){ …
Formats a number with thousands separators (e.g. 1,250,000). Setup: add a Slider Control named "Amount" to this layer and animate it. Apply to Source Text.
function addCommas(n){
  var s = Math.round(n).toString();
  return s.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
addCommas(effect("Amount")("Slider"))
Padded Number (leading zeros)
digits = 3; …
Shows a number with a fixed digit count, padding with leading zeros (e.g. 007). Setup: add a Slider Control named "Count" to this layer. Apply to Source Text. digits = total width.
digits = 3;
n = Math.round(effect("Count")("Slider")).toString();
while (n.length < digits) n = "0" + n;
n
Typewriter Reveal
speed = 12; …
Types the text out one character at a time starting at the layer's in-point, no keyframes. Apply to Source Text. speed = characters per second. Just type your text normally and the expression handles the reveal.
speed = 12;
n = Math.floor((time - inPoint) * speed);
value.substring(0, n)
Scramble / Decode Text
speed = 12; …
Reveals text left-to-right while un-revealed letters flicker through random characters, like a decoding effect. Apply to Source Text. speed = characters locked per second. Spaces are preserved; edit chars to change the flicker alphabet.
speed = 12;
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
src = value;
n = (time - inPoint) * speed;
out = "";
for (i = 0; i < src.length; i++){
  if (i < n){
    out += src.charAt(i);
  } else if (src.charAt(i) == " "){
    out += " ";
  } else {
    seedRandom(i + Math.floor(time * 30), true);
    out += chars.charAt(Math.floor(random(0, chars.length)));
  }
}
out
Progress Percentage (text)
Math.round(linear(time, inPoint, outPoint, 0, 100)) + "%"
Displays 0% to 100% as text over the layer's lifetime — instant loader/progress readout with no keyframes. Apply to Source Text. To tie it to a specific span, replace inPoint/outPoint with your own start/end times.
Math.round(linear(time, inPoint, outPoint, 0, 100)) + "%"
Random Word per Layer
words = ["Ready", "Set", "Go"]; …
Picks one word from a list, stable per layer (duplicates show different words that don't flicker). Apply to Source Text. Edit the words array to your own options.
words = ["Ready", "Set", "Go"];
seedRandom(index, true);
words[Math.floor(random(words.length))]
Eased Number Counter
startVal = 0; …
Counts from startVal to endVal with a smooth ease (fast then slowing), no keyframes. Apply to Source Text. dur = seconds to reach the end value. Wrap with a comma formatter if you want thousands separators.
startVal = 0;
endVal = 1000;
dur = 3;
n = ease(time, inPoint, inPoint + dur, startVal, endVal);
Math.round(n) + ""
Hex to Color
hex = "FF9900"; …
Use a hex code as an After Effects color. Apply to a Color property.
hex = "FF9900";
r = parseInt(hex.substr(0, 2), 16) / 255;
g = parseInt(hex.substr(2, 2), 16) / 255;
b = parseInt(hex.substr(4, 2), 16) / 255;
[r, g, b, 1]
Rainbow Hue Cycle
speed = 0.2; …
Continuously cycles through the color spectrum over time using HSL. Apply to a Color property. speed = hue turns per second; 1 = full saturation, 0.5 = mid lightness.
speed = 0.2;
h = (time * speed) % 1;
hslToRgb([h, 1, 0.5, 1])
Shift a Color's Hue Over Time
speed = 0.1; …
Takes a base hex color, converts it to HSL, rotates only its hue over time, and converts back to RGB (saturation and lightness stay put). Apply to a Color property. The 0.1 controls hue-shift speed.
speed = 0.1;
base = hexToRgb("E03535");
hsl = rgbToHsl(base);
hsl[0] = (hsl[0] + time * speed) % 1;
hslToRgb(hsl)
Random Color per Layer
seedRandom(index, true); …
Gives each layer one stable random color that doesn't flicker. Apply to a Color property. seedRandom(index, true) fixes the color per layer; duplicate layers get different colors.
seedRandom(index, true);
[random(), random(), random(), 1]
Hex to Color (built-in)
hexToRgb("FF9900")
Uses After Effects' built-in hexToRgb() (AE 16.0+) to turn a hex code into a color, no manual parsing needed. Accepts "FF9900", "#FF9900" or an 8-digit RGBA hex. Apply to a Color property.
hexToRgb("FF9900")
Blend Two Colors Over Time
c1 = hexToRgb("FF3366"); …
Smoothly oscillates between two hex colors. Apply to a Color property. c1/c2 = the two colors (hex), speed = full-cycle speed in Hz. Uses built-in hexToRgb() and linear() interpolation across the RGBA arrays.
c1 = hexToRgb("FF3366");
c2 = hexToRgb("33CCFF");
speed = 0.5;
t = (Math.sin(time * speed * 2 * Math.PI) + 1) / 2;
linear(t, 0, 1, c1, c2)
Random Color from Palette
seedRandom(index, true); …
Assigns each layer a stable random color chosen from a fixed palette (no ugly fully-random hues, no flicker). Apply to a Color property. Edit the pal array to your brand colors; duplicate layers spread across the palette.
seedRandom(index, true);
pal = [hexToRgb("E63946"), hexToRgb("F4A261"), hexToRgb("2A9D8F"), hexToRgb("264653")];
pal[Math.floor(random(pal.length))]
Rainbow Spread by Index
step = 0.08; …
Gives each layer a hue stepped by its index, so a stack or grid of duplicates forms a rainbow. Apply to a Color property. step = hue increment per layer (0-1), sat/light = saturation and lightness held constant.
step = 0.08;
sat = 0.7;
light = 0.55;
h = (index * step) % 1;
hslToRgb([h, sat, light, 1])
Clamp / Constrain
min = 0; …
Keep a value between a minimum and maximum.
min = 0;
max = 100;
clamp(value, min, max)
Remap Range (linear)
inMin = 0; …
Map a value from one range to another. Here: 0–100 becomes 0–500.
inMin = 0;
inMax = 100;
outMin = 0;
outMax = 500;
linear(value, inMin, inMax, outMin, outMax)
Nudge Toward Another Layer (normalize)
dist = 150; …
Moves this layer a fixed pixel distance toward a layer named "Target". normalize() builds a length-1 direction vector; 150 = pixels to move. Apply to a 2D Position.
dist = 150;
target = thisComp.layer("Target").transform.position;
dir = normalize(target - transform.position);
value + dir * dist
Move Along an Angle
ang = effect("Angle")("Slider"); …
Offsets Position along an angle by a distance, both driven by sliders. Setup: add two Slider Controls named "Angle" (degrees) and "Distance" (pixels) to this layer. Apply to Position. degreesToRadians converts the angle for the trig functions.
ang = effect("Angle")("Slider");
dist = effect("Distance")("Slider");
r = degreesToRadians(ang);
value + [Math.cos(r), Math.sin(r)] * dist
Drive Opacity by X Position
x = transform.position[0]; …
Links one property to another with linear(): fades this layer from 0 to 100% opacity as it moves from the left edge to the right edge of the comp. Apply to Opacity. No keyframes or setup required.
x = transform.position[0];
linear(x, 0, thisComp.width, 0, 100)
Snap Value to Steps
step = 45; …
Rounds a 1D property to the nearest increment for a mechanical, ratcheting feel (e.g. Rotation snapping in 45 degree clicks). Apply to any 1D property. step = increment size.
step = 45;
Math.round(value / step) * step
Oscillate Between Two Values
lo = 0; …
Smoothly sweeps a 1D property between lo and hi (absolute values, not added to the property). Apply to Opacity, Rotation, a Slider, etc. freq = full sweeps per second. Unlike a plain sine, output stays exactly within lo..hi.
lo = 0;
hi = 100;
freq = 1;
lo + (hi - lo) * (Math.sin(time * freq * 2 * Math.PI) + 1) / 2
Ease Across Layer Life
from = 0; …
Smoothly animate 0 → 100 over the layer's duration, no keyframes needed.
from = 0;
to = 100;
ease(time, inPoint, outPoint, from, to)
Auto Fade In / Out
fadeIn = 0.5; …
Eases opacity up at the layer's start and down at its end with no keyframes. Apply to Opacity. fadeIn / fadeOut are the durations in seconds.
fadeIn = 0.5;
fadeOut = 0.5;
a = ease(time, inPoint, inPoint + fadeIn, 0, 100);
b = ease(time, outPoint - fadeOut, outPoint, 100, 0);
Math.min(a, b)
Ease Between Layer Markers
m1 = marker.key(1).time; …
Eases a value from 0 to 100 between the first two layer markers. Setup: add at least two markers to this layer (tap * on the numpad while previewing). Apply to any 1D property. Change 0/100 to your target range.
m1 = marker.key(1).time;
m2 = marker.key(2).time;
ease(time, m1, m2, 0, 100)
Bounce-Out Easing
dur = 1; …
Animates a 1D property from `from` to `to` with a classic bouncing landing (Penner easeOutBounce), no keyframes. Apply to any 1D property such as Position[1] via a Slider, Scale, or Opacity. dur = seconds of the move.
dur = 1;
from = 0;
to = 100;
function bounceOut(x){
  var n = 7.5625;
  var d = 2.75;
  if (x < 1 / d){ return n * x * x; }
  else if (x < 2 / d){ x -= 1.5 / d; return n * x * x + 0.75; }
  else if (x < 2.5 / d){ x -= 2.25 / d; return n * x * x + 0.9375; }
  else { x -= 2.625 / d; return n * x * x + 0.984375; }
}
t = clamp((time - inPoint) / dur, 0, 1);
from + (to - from) * bounceOut(t)
Overshoot (Back) Easing
dur = 0.8; …
Eases a 1D property from `from` to `to`, overshooting slightly past the target then settling back (Penner easeOutBack), no keyframes. Apply to any 1D property. dur = seconds of the move, s = overshoot strength (1.70158 is the standard amount).
dur = 0.8;
from = 0;
to = 100;
s = 1.70158;
t = clamp((time - inPoint) / dur, 0, 1) - 1;
p = t * t * ((s + 1) * t + s) + 1;
from + (to - from) * p
Audio-Driven Scale
base = 100; …
Pulses a layer's scale with the music. Setup: select your audio layer, then Animation > Keyframe Assistant > Convert Audio to Keyframes to create the "Audio Amplitude" layer. Apply to Scale. 100 = base size; the slider value adds on top.
base = 100;
a = thisComp.layer("Audio Amplitude").effect("Both Channels")("Slider");
s = base + a;
[s, s]
Audio-Driven Opacity
boost = 4; …
Flashes a layer's opacity in time with the audio. Setup: run Animation > Keyframe Assistant > Convert Audio to Keyframes first (creates the "Audio Amplitude" layer). Apply to Opacity. The 4 multiplier boosts the low slider values; clamp keeps it within 0–100.
boost = 4;
a = thisComp.layer("Audio Amplitude").effect("Both Channels")("Slider");
clamp(a * boost, 0, 100)
Audio-Reactive Wiggle
freq = 2; …
Wiggle whose amount is driven by the music, so it shakes harder on loud beats. Setup: select your audio layer, Animation > Keyframe Assistant > Convert Audio to Keyframes (creates the "Audio Amplitude" layer). Apply to Position or Scale. freq = wiggle speed, mult = sensitivity.
freq = 2;
mult = 1;
a = thisComp.layer("Audio Amplitude").effect("Both Channels")("Slider");
wiggle(freq, a * mult)
Audio-Reactive Bounce
mult = 2; …
Pushes the layer upward on each beat and settles back. Setup: run Animation > Keyframe Assistant > Convert Audio to Keyframes first (creates the "Audio Amplitude" layer). Apply to Position. mult = how many pixels of lift per unit of amplitude.
mult = 2;
a = thisComp.layer("Audio Amplitude").effect("Both Channels")("Slider");
value - [0, a * mult]

The real Expresso panel — pick a category, expand a row, or hit ✨ Generate.

Built with

  • After Effects
  • CEP / Bolt CEP
  • React
  • TypeScript

A dockable After Effects panel for everything expressions: a 90-expression built-in library, your own saved collection, and one-click copy or apply onto whatever you have selected.

Since v0.2 it also has a built-in AI assistant — describe the motion you want and it writes the expression, or point it at an existing one to explain, improve, or riff on it. Bring your own key (Anthropic, OpenAI, or Gemini); it's stored on your machine and goes nowhere else.

What you get

  • 90 expressions, ready to go

    A built-in library across 11 categories — wiggle rigs, loops, text counters, physics — each with a plain-English description. One click to copy or apply.

  • Your own library

    Save the expressions you actually use, organize them into categories, and carry them between machines with import/export.

  • AI that speaks expressions

    Describe what you want and get a working expression — or have one explained, improved, or varied. Works with Anthropic, OpenAI, or Gemini.

  • Your key, your machine

    Bring your own AI key. It's stored locally and only ever sent to the provider you chose — nothing passes through anyone else's server.

  • Updates that come to you

    The panel checks for new versions and shows what changed, right inside After Effects. Bugs and ideas go straight to the developer from Settings.