Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 1.2Beginner ~22 min · shot 2 of 6

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.

0/6 · 0%

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.

Find the colour half waydiagram
Rendering diagram…

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. 1

    Check the arithmetic

    In "The lerp slider" drag to 0.25. Do the sum yourself: 0 + (160 - 0) * 0.25 is 40, and the readout says x = 40.0 px. Now change lerp(0, 160, p) to lerp(160, 0, p): the same slider now sends the box the other way, because only the order of a and b changed.

  2. 2

    Look at the clamping

    Drag the slider to -0.50. Measured: the box is at translateX(-80px) and its computed background is rgb(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. 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 reads 0 -> auto : 1 animations. The property tells the browser it may treat auto as a size it can measure. Phase 10 covers it, with its limits and what older browsers do.

Code & diagrams

The lerp slider

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.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.
Lerp, edge by edge

Plain console output: the formula at its normal, extreme and awkward inputs.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

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, 144
Height to auto

The 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.

Press Run to see the page.
00:00.000
animations
0.00s0.10s0.20s0.30s0.40s0.50s0.60s0.70s0.80s0.90s

Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.

frames

Console

Nothing logged yet.

Expected console output

0 -> auto : 0 animations
0 -> 80px : 1 animation

Break 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.

terminal
$ console.log(lerp("0", 200, 0.5))
── what you'll see ──
0100
Real output from Node and the browser.

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(...) to translate(...) 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, unless transition-behavior: allow-discrete is set: then most flip at the half-way point, and display and visibility get special rules. Topic 3.2 covers it.

Remember this

  1. 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. 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. 3

    **lerp(a, b, p)** (short for linear interpolation) gives the value at progress p between a and b: a + (b - a) * p. With a = 0, b = 200 and p = 0.25 it gives 50. To use it, draw the slider below: the box's position and each colour channel are lerp values of the same p. A colour is three numbers (red, green, blue), so you lerp each one on its own.

  4. 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 @keyframes at 0%, 40% and 100%, there are two stretches, and each gets its own lerp from 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. 5

    Progress outside 0 to 1. Nothing in the formula stops p from 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. 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: 0 to height: 80px works. height: 0 to height: auto does not: auto is 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

01

Write the formula for the value at progress p between a and b, and say what progress means.

02

Why does a CSS transition from height: 0 to height: auto not animate, and what do you see?

03

What happens if progress is 1.5? What if it is -0.5?

Practice

01

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.

02

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 lerp and say what each part means

  • You 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 auto does not animate and name a way around it