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.

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.