Smoothstep & Easing

A plain lerp moves at constant speed from start to finish — and then slams to a halt. Played on a UI panel or a camera it reads as robotic: things in the real world ease into motion and ease out of it, they don't teleport up to full velocity and back. The fix is not to change the lerp at all, but to reshape t before you feed it in.

Reshaping t, line by line

Step 1 — see the problem. Lerp advances by the same amount for every equal slice of t: its rate is dead flat, since \frac{d}{dt}\!\left[(1-t)a + tb\right] = b - a is a constant. The motion never accelerates and never slows — that abrupt start and stop is what looks mechanical.

Step 2 — the idea of easing. Keep the lerp; pass it an eased parameter instead of the raw one. An easing function e maps [0, 1] \to [0, 1] with e(0) = 0 and e(1) = 1 (same endpoints, so the animation still starts at a and ends at b), but bends the journey in between:

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

Step 3 — ease-in (start slow). The simplest bend is to square t:

e_{\text{in}}(t) = t^2.

Near t = 0, t^2 is tiny, so the value barely moves at first and then races to catch up — it accelerates in. Good for things gathering momentum (a falling weight, a charging attack).

Step 4 — ease-out (end slow). Mirror ease-in by flipping the input and output:

e_{\text{out}}(t) = 1 - (1 - t)^2.

This screams off the line and decelerates into the finish — the natural feel for a UI element settling into place. (Ease-in-out simply uses e_{\text{in}} for the first half and e_{\text{out}} for the second.)

Step 5 — smoothstep: slow at both ends in one formula. The animator's favourite is a single cubic that eases in and out:

s(t) = 3t^2 - 2t^3.

Check the endpoints: s(0) = 0 and s(1) = 3 - 2 = 1. Good.

Step 6 — why it feels smooth: zero slope at the ends. Differentiate:

s'(t) = 6t - 6t^2 = 6t(1 - t).

At the boundaries this slope vanishes: s'(0) = 0 and s'(1) = 6(1)(0) = 0. Slope is speed, so the motion starts from a dead stop and glides to a dead stop — no velocity jump at either end. That is precisely the "slow-in / slow-out" the eye reads as natural. In the middle s'\big(\tfrac12\big) = 6 \cdot \tfrac12 \cdot \tfrac12 = \tfrac32, a touch faster than a lerp's constant 1 — it makes up the time it spent easing.

Step 7 — wire it in. Nothing about lerp changes; you just feed it s(t):

\text{value} = \operatorname{lerp}\big(a,\, b,\, s(t)\big) = a + (b - a)\,(3t^2 - 2t^3).

One line turns a robotic slide into a polished, weighty move.

Easing reshapes the interpolation parameter before the blend:

Disney's animators codified the "12 basic principles of animation" in the 1930s, and one of them is slow in and slow out: real objects take time to accelerate and decelerate, so drawings should crowd their frames near the extremes of a move and spread them out through the middle. Smoothstep 3t^2 - 2t^3 is exactly that principle written as a formula — its vanishing end-slopes are the bunched frames at the start and finish, its steeper middle is the spread-out frames in between. Decades of hand-drawn craft, captured in a one-line cubic that every UI toolkit now ships under names like ease-in-out. Need an even silkier stop? The quintic smootherstep 6t^5 - 15t^4 + 10t^3 zeroes the second derivative at the ends too, killing the jerk as well as the velocity jump.

Compare the curves over [0, 1]

Three reshapings of t on one chart: the faint straight line is plain linear t, then ease-in t^2, then smoothstep 3t^2 - 2t^3. Slide the t marker and watch where each curve sits: smoothstep hugs the floor near 0 and the ceiling near 1 (flat ends), while sprinting through the middle. The moving vertical marker tracks your t, so you can read off how far each reshaping has progressed and feel the slow-in / slow-out directly.