You’ve been keyframing your character’s every move. Wrist rotate, elbow bend, knee lift — frame by frame, adjusting, tweaking, losing your mind. And then you need to change the timing by 3 frames and suddenly you’re redoing everything.
That’s the old way. Expressions change the game.
Expressions in After Effects let you drive properties with math, link animations across layers, and create rigs that respond to each other intelligently. For character animation specifically, they unlock a level of polish and flexibility that keyframes alone can’t touch.
Today I’m walking you through the expressions I reach for on every character animation project — the ones that turn stiff rigs into characters that actually feel alive.
What Expressions Actually Are (The Short Version)
Think of an expression as a tiny script attached to any property in After Effects. Instead of setting a static value, you write a rule that calculates the value dynamically — based on time, other properties, or random variation.
You add an expression by holding Option (Mac) or Alt (Windows) and clicking the stopwatch next to any property. The expression editor opens, you paste your code, and hit enter. That’s it.
Let’s get into the ones that matter for character work.
1. The Wiggle Expression — Organic Movement Without Frame-By-Frame Work
Wiggle is the expression I use on almost every character — even static poses feel more alive with subtle movement.
wiggle(3, 30)
First number = frequency (times per second). Second number = amplitude (how many pixels the movement covers). So wiggle(3, 30) creates 3 random movements per second, each up to 30 pixels away from the base value.
For a character’s idle breathing, I’ll often use a much tighter value:
wiggle(0.5, 8)
This gives you half a wiggle per second — slow, subtle, natural breathing. Apply it to the body layer’s position. Your character now has a pulse without you touching a single keyframe.
You can also target only one axis:
temp = wiggle(1.5, 20);
[temp[0], value[1]]
This wiggles only the X axis while leaving Y untouched — useful for a head that’s gently swaying side-to-side but not bobbing vertically.
2. Time Expression — Automatic Cycled Movement
The time expression returns the current time in seconds. Multiply it by a speed factor and you get automatic, continuous movement.
time * 100
Applied to rotation, this spins the layer continuously — useful for wheels, propellers, or any looping mechanical element. Set the rotation keyframes you want, then add the time expression to add continuous motion on top.
For a more controlled cycle — say, a character’s head slowly tracking to follow something — you can clamp the value to keep it within a range:
Math.sin(time * 2) * 15
This oscillates between -15 and +15 degrees. At 2 radians per second, it gives you a slow, pendulum-like head movement. Perfect for a character that’s idly looking around.
3. loopOut — Seamless Repeating Animations
Once you’ve keyframed a walk cycle, a bounce, or any repeating motion, loopOut extends it infinitely without duplicating keyframes.
loopOut(type = "cycle", numKeyframes = 0)
The cycle type repeats your animation seamlessly. numKeyframes = 0 means use all keyframes. Change it to 2 and only the last 2 keyframes repeat — useful for creating a lead-in.
Other loop types worth knowing:
loopOut(type = "pingpong", numKeyframes = 0)
Pingpong bounces between your first and last keyframe — great for a breathing chest that expands and contracts.
loopOut(type = "offset", numKeyframes = 0)
Offset accumulates the motion each cycle, so the layer keeps drifting in the direction of your animation — useful for walk cycles that need to travel across the screen.
4. Connecting Body Parts with the Parent Property
The most powerful expression-free technique in character animation is parenting — and it becomes even more powerful when you add expressions on top.
Parent a forearm to an upper arm, and when the upper arm rotates, the forearm follows. But what if you want the forearm to rotate with the upper arm, plus a little extra bend during fast movement?
thisComp.layer("Upper Arm").rotation + 0.6
This adds the upper arm’s rotation to the forearm’s base rotation, plus a 0.6-degree offset. Now your forearm lags slightly behind the upper arm during quick movements — creating natural follow-through without any manual keyframing.
You can chain this across an entire arm: shoulder → upper arm → forearm → hand. Each layer adds its own proportional response. Instant procedural follow-through.
5. Puppet Pin Rigging with Expressions
The Puppet tool is great for basic deformation, but with expressions you can make it genuinely responsive.
Pin the torso and add this to the Pin 2’s position:
x = thisComp.layer("Controller").transform.position[0];
y = thisComp.layer("Controller").transform.position[1] * 0.7;
[x, y]
Now the torso squashes slightly on the Y axis as the controller moves — add a Math.abs to keep it always compressing in the right direction:
x = thisComp.layer("Controller").transform.position[0];
y = thisComp.layer("Controller").transform.position[1];
squash = 1 + (1 - Math.abs(y - value[1]) / 50) * 0.3;
[x, value[1] * (2 - squash)]
This creates a squash-and-stretch effect driven by the controller’s Y position. Animate the controller, and the entire torso responds with natural squash-and-stretch. That’s an expression-based character rig in under 10 lines of code.
6. Random Expression — Variable Timing for Natural Feel
Every character animation principle says your keyframes shouldn’t be perfectly uniform — slight variations in timing make movement feel human. random() gives you that automatically.
value + random(-5, 5)
This adds a random value between -5 and +5 to whatever the keyframed value is. Apply this to rotation or position on a walk cycle and each step gets slightly different timing. Apply it to the position of multiple fingers and each one moves at a slightly different pace.
For a more controlled Gaussian distribution (more values clustering near the center):
value + gaussRandom(5)
This creates subtle variations — most movements are close to the keyframed value, with occasional bigger deviations — which is exactly how real movement works.
7. Linear Expression — Converting One Range to Another
Linear interpolation is one of the most useful expressions for character rigs. It converts a value from one range to another.
linear(inputValue, inputMin, inputMax, outputMin, outputMax)
Imagine you have a slider on a null object controlling how much a character is leaning forward. You want that lean to control both the torso rotation and the leg positions.
lean = thisComp.layer("Control").effect("Slider")("Slider");
linear(lean, 0, 100, 0, 25)
A slider from 0-100 maps to 0-25 degrees of rotation. Now you can drive multiple layers from the same control — lean the torso, shift the leg positions, tilt the head — all from one slider. One control, whole body response.
8. Value at Time — Delayed Ripples Through a Rig
valueAtTime reads what a property’s value was at a specified time. This lets you create delayed, cascading movements through a rig.
thisProperty.valueAtTime(time - 0.08)
Parent this to a spine segment’s rotation. The segment now follows the rotation 0.08 seconds behind the segment above it — a delayed cascade that creates natural wave-like motion through the spine when the torso moves. No extra keyframes needed.
This is the secret behind those “liquid” character rigs where the whole body flows into a movement rather than moving as a rigid block.
Putting It Together: A Simple Two-Expression Character Rig
Here’s a quick rig you can build in 10 minutes that demonstrates the power of expressions:
- Create a character with a body layer and a head layer parented to the body.
- On the body layer, add
wiggle(0.8, 6)to position — subtle idle breathing. - On the head layer’s rotation, add
thisComp.layer("Body").transform.rotation * 0.4— the head responds to body movement at 40% of the body rotation. - On the head layer’s position, add
thisComp.layer("Body").transform.positionplus a vertical offset based on the body’s Y position usinglinear().
Now when you animate the body moving up and down, the head follows — with its own independent breathing animation layered on top, and rotation that echoes but doesn’t exactly match the body.
That’s three expressions. No additional keyframes on the head. And it feels alive.
The Mental Model Shift
The biggest trap motion designers fall into is treating expressions as a technical replacement for keyframe work. They’re not. They’re a different tool.
Keyframes handle intentional, specific poses — the ones that define your character’s attitude and personality. Expressions handle the connective tissue — the ambient motion, the secondary reactions, the adjustments that would take forever to keyframe manually.
Think of it as building a character rig rather than animating a character. You set up the system, define the relationships, and then you have a responsive character that moves naturally with minimal additional input.
The expressions in this article — wiggle, time, loopOut, parent references, linear, valueAtTime, random — are the foundation. Master these and you’ll start seeing rigging opportunities everywhere. That’s when character animation stops being tedious and starts being genuinely fun.
And if you want project files where I’ve already built these rigs out, with full annotations explaining how each expression works — check out our motion design courses. Each project file has the AE file plus a video walkthrough breaking down the expression logic. It’s the fastest way to learn by reverse-engineering rigs that actually work.
Now go add some expressions to your next character. Your future self — who doesn’t have to re-keyframe the whole walk cycle because the timing changed — will thank you.