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.
- Endpoints: \operatorname{lerp}(a,b,0)=a and
\operatorname{lerp}(a,b,1)=b — it hits both keys exactly.
- Constant rate: the derivative with respect to t is
the constant b-a, so equal steps in t give
equal steps in value — even spacing.
- Affine, per-component: for vectors it runs independently on each coordinate,
\operatorname{lerp}(\mathbf{a},\mathbf{b},t)=(1-t)\mathbf{a}+t\mathbf{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.
- It leaves the rotation set. The average of two rotation matrices is generally
not a rotation — its columns lose unit length and orthogonality, so it stretches or shears
as well as turns.
- Non-constant angular speed. Even after re-normalising, blending the entries does
not sweep the angle at a constant rate; the object speeds up and slows down mid-turn for no
reason the animator asked for.
- Euler angles bring gimbal issues. Lerping three Euler angles independently can
pass through
gimbal lock
and take a wild, non-shortest path between two orientations.
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:
| Channel | Lives in | Blend with |
| Positions / translations | flat Euclidean space | plain lerp |
| Rotations / orientations | the rotation group (a sphere) | slerp |
| Scales | multiplicative — better in log space | lerp the logarithm, then exp |
| Colours | a perceptual space, not raw RGB | lerp 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.