Command Palette

Search for a command to run...

Hectal
Shots in this scene
SHOT 5.4Intermediate ~21 min · shot 4 of 6

linear() and Custom Curves

In one line

linear() takes a list of points and draws straight lines between them. With enough points it can copy any curve, including a bounce or a spring, which cubic-bezier() cannot. It works in every current browser, but older ones ignore it, so give it a fallback.

0/6 · 0%

Think of it like this

Connect-the-dots. With four dots you draw a rough bend. With forty dots, joined by short straight lines, the picture looks smooth and can show a wiggle that four dots never could. linear() is connect-the-dots for an easing curve.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

linear()
A CSS easing made of a list of points joined by straight lines. Not to be confused with the keyword linear.
sampling
Reading a function's value at evenly spaced moments to turn it into a list of numbers.
bounce
A curve that reaches the end value, goes back, and reaches it again, several times.
@supports
A CSS rule that applies its block only if the browser understands the property and value in its test.
fallback
A simpler declaration that an older browser uses when it cannot understand the new one.

Step by step

01Press Drop

Run "Bounce: 4 points, 24 points, a fallback" and press Drop. Three balls fall 78 px. The first (4 points) drops with a smooth slowdown and no bounce. The second and third (24 points) bounce.

02Step to 338 ms

Pause, press Drop, step to 338 ms. The 24-point balls are at 75.6 px, just touching the floor. The 4-point ball is at 48.4 px. At 563 ms the 24-point balls are bouncing back at 62.3 px while the 4-point ball is at 67.9 px and still going down.

03Read the console

The console says linear() supported: true and shows what the browser refuses: one point, an empty list. The 4-point ball's computed value is linear(0 0%, 0.473 25%, 0.766 50%, 0.973 75%, 1 100%).

04Build one from a function

Run "A spring, sampled into linear()". It samples a damped spring into 30 points: the console says it overshoots to 1.284 and the string is 214 characters. Press Rebuild with 8 points: now the peak is 1.209 and the string is 61 characters.

Try it yourself

  1. 1

    Pretend to be an old browser

    Change @supports (transition-timing-function: linear(0, 1)) to linear(0) (which is invalid). The block is skipped, as in an old browser. Press Drop: the third ball no longer bounces; it lands with a soft ease-out (70.3 px at 338 ms, 77.0 px at 563 ms, 78 px at the end).

  2. 2

    Fewer points

    In "A spring" press Go, step to 150 ms: the box is at 229.1 px (the exact curve gives 230.8). Rebuild with 8 points and compare: the whole curve is rougher and peaks lower, at 1.209 instead of 1.284 (217.6 px instead of 231 px).

Code & diagrams

Try it: interactive

Springs as linear()

Pick the spring curve, change its stiffness and damping, and press Play. Read the linear() text the lab prints. Make a spring that overshoots, then one that does not.

Bounce: 4 points, 24 points, a fallback

Three balls with the same 900 ms drop. The left one has a 4-point list, the others a 24-point bounce, the last wrapped in @supports with a soft cubic-bezier before it. The console prints what CSS.supports says about a few lists.

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

linear() supported: true
one point: false
empty: false
stops with %: true
two stops on one value: true
few: linear(0 0%, 0.473 25%, 0.766 50%, 0.973 75%, 1 100%)
A spring, sampled into linear()

The easing string is built by JavaScript from a damped-spring formula. Press Go, step to 150 ms, then rebuild with 8 points and compare.

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

30 points: overshoot to 1.284, string is 214 characters

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

Too few points

In "Bounce" replace the 24-point list on the second ball with linear(0, 0.473, 0.766, 0.973, 1), or just look at the first ball.

terminal
$ Press Drop, step to 338 ms and 563 ms
── what you'll see ──
The 4-point ball is at 48.4 px at 338 ms and 67.9 px at 563 ms: it only goes down, never bounces.
Measured: 4 points taken from the bounce at 25%, 50% and 75% hide the bounce between them.

Break #2

The list does not end at 1

Replace the second ball's list with linear(0, 0.5, 0.9).

terminal
$ Press Drop, step to 899 ms, then 901 ms
── what you'll see ──
70.1 px at 899 ms, then 78 px at 901 ms: the ball jumps 7.9 px in the last frame.
Measured at 0.9 of 78 px.

Myth vs fact

Myth

linear() is the same as the keyword linear.

Fact

The keyword is a straight line from 0 to 1. The function takes your points and can draw any shape; a list linear(0, 1) is the same as the keyword.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    linear() is accepted wherever a CSS easing is: animation-timing-function, and the easing option of the Web Animations API. One string serves both.

Remember this

  1. 1

    The problem this solves: cubic-bezier() draws one smooth bend with at most one overshoot. It cannot bounce three times, and it cannot be a spring that wobbles and settles. For those you need to say the exact progress at many moments.

  2. 2

    The syntax. linear(0, 0.25, 1) lists output values. With no positions they are spaced evenly: here at 0%, 50% and 100%. You may give a position, linear(0, 0.5 30%, 1), and a value may hold for a stretch with two positions, 1 20% 60%. The browser draws straight lines between the points. Its computed value always lists a percent for every point, as the console shows.

  3. 3

    More points, truer curve. The straight lines are only an approximation. In the first example the same bounce is drawn with 4 points and with 24: at 338 ms the 24-point ball has hit the floor (75.6 px of 78) and the 4-point ball is still falling (48.4 px). A typical generated bounce or spring has 30 to 60 points; each costs a few characters.

  4. 4

    Sample a function. You rarely type the numbers. You write the maths of the motion (a bounce, a damped spring 1 − e^(−6t)·cos(14t)), take its value at evenly spaced moments, round to three decimals and join them into a string. The second example does this in 6 lines.

  5. 5

    A spring in linear() has a fixed shape. The curve overshoots to 1.284 (231 px on a 180 px move: 51 px past the target) and settles within the duration you give it. It does not know about velocity: if the user interrupts it, the next animation starts from rest. True springs that keep their speed come in Phase 9.

  6. 6

    Start at 0 and end at 1. If the last point is not 1, the animation ends there, and then the element jumps to its real end value. In the break-it below, a list ending at 0.9 stops at 70.1 px and snaps to 78 px in the last frame. linear(0) (one point) and linear() (none) are invalid.

  7. 7

    Support and fallback. linear() arrived in Chrome 113, Firefox 112 and Safari 17.2, so a few percent of visitors (check caniuse for today's figure) still use a browser without it. Such a browser that does not understand it throws the declaration away. Write a plain declaration first (a cubic-bezier with a similar feeling), then override inside @supports (transition-timing-function: linear(0, 1)). In an old browser the ball lands softly instead of bouncing, and nothing breaks. A bounce is decoration: leave it out for reduced motion.

Explain it without notes

01

Why can linear() make a bounce when cubic-bezier() cannot?

02

How would you give an old browser a good result when you use linear()?

03

What happens if the last point of a linear() list is 0.9?

Practice

01

Write a linear() list for a single overshoot of 1.1 that settles at 1 with 6 points, and say what the first and last points must be.

02

Change the spring example so the sampling uses 12 points and a decay of 4 instead of 6. What happens to the overshoot?

Done when you can

  • You can write a linear() list with and without positions

  • You can sample a function into one and say how many points it needs

  • You can wrap it in @supports with a fallback

  • You know it must start at 0 and end at 1