Shots in this scene
Interpolation: the In-Between
In one line
Interpolation means working out the values between a start and an end. Every animation on the web is one value, such as a position, an opacity or a colour, moving from one number to another as a progress number goes from 0 to 1.
Think of it like this
A map says the café is at 0 km and the station is at 10 km. A friend asks where you are when you are three tenths of the way there. You say "3 km". You interpolated: you took the start, the end, and how far along you are, and worked out the place in between.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- interpolation
- Working out the values between a start and an end.
- progress
- A number from 0 (the start) to 1 (the end) saying how far through the animation we are.
- lerp
- Short for linear interpolation:
a + (b - a) * p. - keyframe
- A value the animation must have at a given point. The browser fills in the gaps.
- extrapolation
- Going past the ends: progress below 0 or above 1 continues the same straight line.
- clamp
- Cut a value to a range. A colour channel of 361 is clamped to 255.
Step by step
01Drag the progress slider
In "The lerp slider" drag the slider from 0 to 1. The box slides from 0 to 160 px and turns from orange to blue. The readout shows the numbers lerp produced. At 0.50 the position is 80.0 px and the colour is rgb(149, 132, 144).
02Go past the ends
Drag to -0.50. The box moves 80 px to the left of its start (x = -80.0 px). Drag to 1.50: it sits 240 px along. Look at the colour readout: at 1.50 the red is -64 and the blue is 344, but the box shows a plain bright blue. The numbers are impossible, so the browser clamps each one into 0 to 255.
03Read the console of "Lerp, edge by edge"
The second example prints lerp for the normal range, for progress outside it, for a start and end that are the same (nothing to travel, so the answer is always 80) and for "auto". parseFloat("auto") is NaN ("not a number"), and any sum with NaN stays NaN.
04Find the colour half way
The last line prints 149, 132, 144: the average of orange (255, 107, 44) and blue (42, 157, 244), channel by channel. It is a greyish pink, not a purple. A browser's transition between the same two colours passes through exactly this colour (measured: half way through a 1-second transition the element was rgb(149, 132, 144)). Topic 3.2 covers which colour space transitions use.
05Try to animate to auto
"Height to auto" starts two boxes closed, then opens them: one to height: auto, one to height: 80px. The console prints 0 animations for the first and 1 for the second. Nothing happens to the first box over time: it appears at once.
Try it yourself
- 1
Check the arithmetic
In "The lerp slider" drag to 0.25. Do the sum yourself:
0 + (160 - 0) * 0.25is 40, and the readout saysx = 40.0 px. Now changelerp(0, 160, p)tolerp(160, 0, p): the same slider now sends the box the other way, because only the order ofaandbchanged. - 2
Look at the clamping
Drag the slider to -0.50. Measured: the box is at
translateX(-80px)and its computed background isrgb(255, 82, 0), but the readout says the red is 362 and the blue is -56. The page asked for values outside 0 to 255 and the browser clamped each one. - 3
Let the browser animate to auto
In "Height to auto" add this line at the top of the CSS:
:root { interpolate-size: allow-keywords; }. Reload the example. Measured: the first line now reads0 -> auto : 1 animations. The property tells the browser it may treatautoas a size it can measure. Phase 10 covers it, with its limits and what older browsers do.
Code & diagrams
One progress number drives a position and a colour. The range goes from -0.50 to 1.50 so you can see what happens outside 0 to 1.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Plain console output: the formula at its normal, extreme and awkward inputs.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
lerp(0, 200, 0) = 0
lerp(0, 200, 0.25) = 50
lerp(0, 200, 1) = 200
lerp(0, 200, -0.5) = -100
lerp(0, 200, 1.5) = 300
lerp(80, 80, 0.7) = 80
parseFloat('auto') = NaN
lerp(auto, 200, 0.5) = NaN
halfway orange to blue = 149, 132, 144The same transition on two boxes. Only the one that goes to a number gets a track on the timeline. The first read of `a.offsetHeight` makes the browser see both boxes closed before the class is added.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
0 -> auto : 0 animations
0 -> 80px : 1 animationBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Pass a number as text
In "Lerp, edge by edge" change lerp(0, 200, 0.25) to lerp("0", 200, 0.5) and read the console.
Myth vs fact
Myth
A transition to height: auto is only a bit rough.
Fact
It does not run at all. With no numbers on both sides there is nothing to interpolate, so the browser skips the animation and changes the height at once, with no warning. Measured above: 0 animations.
Myth
Progress always stays between 0 and 1.
Fact
Not in the formula. Overshooting easings (Phase 5) and springs (Phase 9) send it past 1 on purpose. Clamp it only when the value has a limit, such as a colour channel or an opacity.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Browsers interpolate each property by its own rules, not one global
lerp. Lengths and numbers use the formula directly. Transforms are interpolated function by function, and only when both lists match:translate(...) rotate(...)totranslate(...) rotate(...). If the lists do not match, the browser works with the matrices instead, which can take a different path (a rotation of 360° can seem to do nothing). - ▸
Discrete values (a keyword such as
display: none) have no in-between. A transition skips them and the change is instant, unlesstransition-behavior: allow-discreteis set: then most flip at the half-way point, anddisplayandvisibilityget special rules. Topic 3.2 covers it.
Remember this
- 1
The problem this solves: you know where a thing starts and where it ends, but a screen needs its position in every frame between. Something must work out all those in-between values. This topic shows that it is a single small formula, so you can read any animation as a value travelling from A to B.
- 2
Progress is a number from 0 to 1 that says how far through the animation we are. 0 is the start, 0.5 is half way, 1 is the end. Time becomes progress: for a 500 ms animation, 250 ms in gives progress 0.5. Hold on to this idea. The next topics only change how progress is worked out from time (Topic 1.3) and how it is shaped by an easing curve (Phase 5).
- 3
**
lerp(a, b, p)** (short for linear interpolation) gives the value at progresspbetweenaandb:a + (b - a) * p. Witha = 0,b = 200andp = 0.25it gives 50. To use it, draw the slider below: the box's position and each colour channel arelerpvalues of the samep. A colour is three numbers (red, green, blue), so you lerp each one on its own. - 4
Keyframes are known values; the browser fills the gaps. A keyframe says "at this point the value must be this". With two keyframes (a transition's start and end), there is one stretch to fill. With
@keyframesat 0%, 40% and 100%, there are two stretches, and each gets its ownlerpfrom one keyframe's value to the next, with its own progress from 0 to 1. The browser does this for every property that can be interpolated: numbers, lengths, colours and transforms. - 5
Progress outside 0 to 1. Nothing in the formula stops
pfrom being -0.5 or 1.5. The result carries on in a straight line:lerp(0, 200, 1.5)is 300. Easing curves that overshoot (Phase 5) use this on purpose, so the element goes past its end and comes back. But a value such as a colour channel has hard limits: the browser clamps a red of 361 to 255. - 6
Not everything can be interpolated. The browser can only work out in-between values when both ends are numbers of the same kind.
height: 0toheight: 80pxworks.height: 0toheight: autodoes not:autois an instruction ("work out the size from the content"), not a number, so there is nothing to lerp. The browser does not animate it. It also shows no error, which is why this is the most common surprise.
Explain it without notes
Write the formula for the value at progress p between a and b, and say what progress means.
Why does a CSS transition from height: 0 to height: auto not animate, and what do you see?
What happens if progress is 1.5? What if it is -0.5?
Practice
Write a function mix(from, to, p) that takes two colours as arrays [r, g, b] and returns a CSS string such as rgb(149, 132, 144). Clamp each channel to 0 to 255 and round it.
A @keyframes rule has 0% { left: 0 }, 40% { left: 100px }, 100% { left: 160px }. What is left at 20% of the time? At 70%?
Done when you can
You can write
lerpand say what each part meansYou can read an animation as a value moving from keyframe to keyframe
You can say what happens at progress below 0 and above 1
You can say why
autodoes not animate and name a way around it