SLERP: Blending Orientations

You now have two orientations stored as unit quaternions, q_0 and q_1 — say a camera's start and end pose, or two keyframes of an animation. The animator's question is: how do you glide smoothly from one to the other? The answer is SLERP, spherical linear interpolation, and it is the reason quaternion-stored orientations blend so cleanly. We build it from the naive attempt up.

From naive blend to the great-circle arc

Step 1 — the naive try: lerp the components. The obvious idea is straight-line interpolation, treating the four numbers as a vector:

\operatorname{lerp}(q_0, q_1, t) = (1 - t)\,q_0 + t\,q_1, \qquad t \in [0, 1].

At t=0 it is q_0, at t=1 it is q_1 — good endpoints. But a unit quaternion must lie on the 4-D unit sphere, and the straight chord between two points on a sphere cuts through it, so the blend is no longer unit-length.

Step 2 — patch it: NLERP (normalised lerp). Just renormalise each step, \operatorname{nlerp}(q_0, q_1, t) = \frac{(1-t)\,q_0 + t\,q_1}{\,|(1-t)\,q_0 + t\,q_1|\,}. This stays on the sphere and is cheap. But because the chord is being projected back out to the sphere, equal steps in t do not give equal steps in angle: the motion speeds up in the middle and lingers near the ends. Fine for a quick blend, wrong for a camera that should pan at constant rate.

Step 3 — do it right: walk the great-circle arc. The honest path between two points on a sphere is the shortest great-circle arc joining them, traversed at constant angular velocity. Call the angle between q_0 and q_1 on the 4-D sphere \Omega; we want a point that sweeps from q_0 to q_1 along that arc, covering an equal slice of \Omega for each equal slice of t.

Step 4 — find the angle from the dot product. Both quaternions are unit vectors in \mathbb{R}^4, so their dot product is the cosine of the angle between them:

\cos\Omega = q_0 \cdot q_1 = w_0 w_1 + x_0 x_1 + y_0 y_1 + z_0 z_1.

Step 5 — the SLERP formula. The constant-speed point on the arc is the sine-weighted combination

\operatorname{slerp}(q_0, q_1, t) = \frac{\sin\big((1-t)\,\Omega\big)}{\sin\Omega}\,q_0 + \frac{\sin\big(t\,\Omega\big)}{\sin\Omega}\,q_1.

Check the endpoints. At t=0: the first weight is \sin\Omega/\sin\Omega = 1 and the second is \sin 0/\sin\Omega = 0, giving exactly q_0. At t=1: the weights flip, giving exactly q_1. In between, because the weights are sines of angles linear in t, the result traces the arc at a steady angular rate — constant speed, by construction. (The two scalar weights are exactly the coefficients that decompose the arc point in the \{q_0, q_1\} plane; they are the spherical analogue of (1-t) and t, and as \Omega \to 0 they reduce to them.)

Step 6 — take the short way (the double-cover sign flip). Here is the one quaternion gotcha. A quaternion and its negative represent the same rotation: q and -q rotate identically (the unit quaternions double-cover the rotations). So q_1 and -q_1 are the same destination but sit on opposite sides of the sphere. If q_0 \cdot q_1 < 0 the arc to q_1 goes the long way round (more than 90° through orientation space); flipping the sign,

\text{if } q_0 \cdot q_1 < 0: \quad q_1 \leftarrow -q_1,

picks the nearer representative so SLERP takes the genuinely shortest path. Skip this and your camera occasionally takes the scenic 270° route to a pose 90° away — a classic engine bug.

For unit quaternions q_0, q_1 and t \in [0,1], spherical linear interpolation is \operatorname{slerp}(q_0, q_1, t) = \frac{\sin\big((1-t)\Omega\big)}{\sin\Omega}\,q_0 + \frac{\sin\big(t\Omega\big)}{\sin\Omega}\,q_1.

NLERP and SLERP reach the same endpoints and trace the same arc — the difference is purely the timing along it.

Rule of thumb engines actually follow: NLERP for cheap, high-volume animation blends with small angles; SLERP when the path is long or the constant rate is visible (cinematic camera moves). A common hybrid even SLERPs only when q_0 \cdot q_1 shows a wide angle and NLERPs otherwise.

It's tempting to think "lerp the four quaternion numbers, then renormalise" (that's NLERP) is SLERP — after all, both start exactly at q_0, end exactly at q_1, and, as above, trace the very same great-circle arc. The catch is speed, not path. Plain lerp draws a straight chord through the inside of the 4-D sphere; snapping that chord back onto the sphere by dividing by its length bunches points up near the ends and stretches them apart in the middle, because the chord passes closer to the sphere's surface near t=0,1 than at t=0.5. So a rotation that should sweep at one constant rate visibly speeds up through the middle of the turn — a wobble that's easy to miss in a code review (both formulas "work" and land on the right endpoints) and easy to spot on screen the moment a camera or a limb has to swing through a wide angle at an even pace. True SLERP's \sin-weighted formula, not a lerp-and-renormalise shortcut, is what actually holds the angular speed constant end to end.

Sweep the arc with the t slider

A circle stands in for the unit sphere of orientations. q_0 (blue) and q_1 (green) are two orientations separated by the angle \Omega; the orange arrow is \operatorname{slerp}(q_0, q_1, t). Drag t from 0 to 1 and watch it sweep the short arc at a steady rate — landing exactly on q_0 at t=0 and q_1 at t=1. Change \Omega to set the two poses farther apart; the readout shows \cos\Omega = q_0\cdot q_1 and the swept angle t\,\Omega.