Interpolation Refresher

Animation is, underneath all the artistry, one relentless question asked sixty times a second: given where a thing was at one keyframe and where it will be at the next, where is it right now? Answering it is interpolation — manufacturing the in-between values that live between two knowns. Every character that walks, every camera that dollies, every colour that fades to black is an interpolation running under the hood.

You already met the workhorse — linear interpolation — in the maths track. This page is a deliberate consolidation for animation: we recap lerp and easing, and then deliver the one warning that separates a working animation system from a subtly broken one — different quantities must be interpolated in different spaces. A position blends one way; a colour, a scale, and above all a rotation blend in others. Lerp everything and your character's joints will visibly shrink as they swing.

Lerp: the atom of animation

Linear interpolation walks a straight line from a to b as a parameter t slides from 0 to 1:

\operatorname{lerp}(a, b, t) = (1 - t)\,a + t\,b.

Read the formula as a weighted average: the weights (1-t) and t always sum to 1, so the result is a valid blend that stays on the segment between the two values. That "weights sum to one" property is exactly what will fail for us later — hold onto it.

Reshaping t: easing is spacing

Raw lerp moves at a dead-constant rate, which reads as mechanical. Animators fix this not by changing the endpoints but by reshaping the parameter before it enters the lerp. Feed t through an easing function e(t) with e(0)=0 and e(1)=1, then lerp with e(t) instead of t:

\text{value}(t) = \operatorname{lerp}\bigl(a,\,b,\;e(t)\bigr).

The classic choice, met as smoothstep, is e(t) = 3t^2 - 2t^3, whose slope e'(t) = 6t(1-t) vanishes at both ends — the motion eases out of the first key and into the second (slow-in / slow-out). The endpoints are untouched; only the spacing of the in-betweens changes. The chart below lets you feel the difference: drag t and watch how far the eased marker lags or leaps versus the straight line.

Linear vs eased, side by side

The faint straight line is plain t (constant rate). The bold curve is smoothstep 3t^2-2t^3. At t=0.25 the eased value is still near the floor — the move has barely started; through the middle it sprints; near t=1 it settles gently. Same endpoints, utterly different life.

This is the whole trick of easing: it is a lens on t, not a change to a or b. Which means everything on this page about what space to interpolate in is orthogonal to easing — you pick the space first, then reshape t however you like inside it.

Worked example: lerping two positions

Positions are the friendly case — they live in flat Euclidean space, where the straight line the lerp draws is the shortest, most natural path. Take a ball at \mathbf{a}=(0, 0) and a target at \mathbf{b}=(8, 4). Halfway through:

\operatorname{lerp}(\mathbf{a},\mathbf{b},\tfrac12) = \tfrac12(0,0) + \tfrac12(8,4) = (4, 2).

Exactly the midpoint, as expected. At t=0.25 we get (2, 1); at t=0.75, (6, 3) — evenly spaced points marching up a straight line. Nothing surprising, and that is the point: for positions, component-wise lerp is correct. Now watch it break.

The trap: lerping a rotation

Rotations do not live in flat space. A 2-D rotation by angle \theta is the matrix

R(\theta) = \begin{pmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{pmatrix},

and its columns are unit vectors — that is what makes it a rotation (it turns things without stretching them). Suppose we want to blend from 0^\circ (R(0)=I, the identity) to 90^\circ. Naively, we lerp the two matrices entry by entry at t=0.5:

R(0)=\begin{pmatrix}1&0\\0&1\end{pmatrix},\qquad R(90^\circ)=\begin{pmatrix}0&-1\\1&0\end{pmatrix}, M = \tfrac12 R(0) + \tfrac12 R(90^\circ) = \begin{pmatrix} 0.5 & -0.5 \\ 0.5 & 0.5 \end{pmatrix}.

Is M a rotation? Check a column length: \sqrt{0.5^2 + 0.5^2} = \sqrt{0.5} \approx 0.707. The columns have shrunk to about 71\% of unit length. Applying M to your character's forearm doesn't just rotate it to 45^\circ — it also scales it down by ~29%. The arm visibly shrinks at the middle of the swing and pops back at the end. The averaged matrix is a rotation-plus-shrink, not a rotation.

The fix: interpolate the angle, or slerp

The right blend keeps the result on the set of rotations the whole way. In 2-D that is trivial — interpolate the angle and rebuild the matrix: R\bigl(\operatorname{lerp}(0^\circ, 90^\circ, t)\bigr). At t=0.5 that is honestly R(45^\circ), whose columns are exact unit length. No shrink, constant angular speed.

In 3-D, orientations are represented by unit quaternions, which live on the surface of a 4-D sphere. The correct interpolation walks the great-circle arc across that sphere at constant speed — spherical linear interpolation, or slerp:

\operatorname{slerp}(q_0, q_1, t) = \frac{\sin\bigl((1-t)\Omega\bigr)}{\sin\Omega}\,q_0 + \frac{\sin\bigl(t\,\Omega\bigr)}{\sin\Omega}\,q_1,\qquad \cos\Omega = q_0 \cdot q_1.

Notice the shape: it is still a weighted blend of the two endpoints, but the weights are \sin ratios rather than (1-t) and t. Those weights are chosen precisely so the result stays a unit quaternion — a genuine rotation — for every t, and so the angle sweeps uniformly. Slerp is "lerp, but on a sphere."

Interpolating a whole pose

A character pose is not one value — it is a bundle of channels: root position, dozens of joint rotations, maybe blend-shape weights and a scale or two. Interpolating a pose means interpolating each channel in its own proper space, all sharing the same t:

ChannelLives inBlend with
Positions / translationsflat Euclidean spaceplain lerp
Rotations / orientationsthe rotation group (a sphere)slerp
Scalesmultiplicative — better in log spacelerp the logarithm, then exp
Coloursa perceptual space, not raw RGBlerp in linear-light or a perceptual space

Scales are multiplicative: to go from 1\times to 4\times, the perceptually even midpoint is 2\times (a factor of two each half), not the arithmetic 2.5\times. Interpolating \log(\text{scale}) and exponentiating gives that even multiplicative march; plain lerp does not. Colours have their own trap — lerping raw sRGB values darkens and desaturates the mid-tones, which is why renderers blend in linear light. The unifying rule: pick the space where a straight line means the right thing, blend there, then map back.

Lerp joins two keys with a straight segment, so a curve through many keys is a chain of segments with sharp corners at each key — the velocity jumps instantly, which reads as a visible hitch. Higher-order interpolation smooths this. Cubic Hermite interpolation blends not just the two endpoint values p_0, p_1 but also endpoint tangents m_0, m_1:

p(t) = (2t^3 - 3t^2 + 1)\,p_0 + (t^3 - 2t^2 + t)\,m_0 + (-2t^3 + 3t^2)\,p_1 + (t^3 - t^2)\,m_1.

By matching the tangent at each key, neighbouring segments meet with a continuous velocity — no corner, no hitch. This is exactly what a keyframe editor's tangent handles control, and it is the seed of the animation splines (Catmull–Rom, Bézier, TCB) you will meet next. Slerp has a curved cousin too — squad — for smoothing chains of rotations.

The seductive shortcut is to store every animated quantity as a bag of floats and lerp the whole bag component-wise. It looks right for positions and blend weights, so the bug hides — until a joint rotates. Component-wise lerp of a rotation matrix produces a matrix that is no longer a rotation (shrunken, sheared columns), so limbs pinch and shrink through the middle of a swing; component-wise lerp of Euler angles takes non-shortest paths and can graze gimbal lock; naive lerp of a scale marches unevenly; naive lerp of sRGB colour muddies mid-tones. The rule is not "lerp everything," it is "lerp in the right space for each quantity" — plain lerp for translations, slerp for rotations, log-space for scales, linear-light for colour. When something animates with a mysterious pulse or shrink, suspect a quantity being blended in the wrong space.