Shots in this scene
Duration-Based Springs
In one line
Designers think in "about a third of a second, a little bounce", not in stiffness and damping. Motion's visualDuration and bounce convert that into real spring physics.
Think of it like this
You book a taxi by saying when you must arrive, not by naming the engine's horsepower. visualDuration and bounce are the arrival time and a taste for how lively the ride is. The horsepower, stiffness and damping, is worked out for you.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- bounce
- Motion's dial for liveliness. 0 is critical damping, larger is bouncier, negative is overdamped.
- visualDuration
- In seconds, the time the movement looks done. The bounce tail can run longer.
- duration-based spring
- A spring solved to end at a given time, built from
durationandbounce. - design token
- A named value shared by designers and code, such as a spring called "fast spatial".
Step by step
01Read the three balls
All three springs have visualDuration 0.4 s. Bounce 0, 0.25 and 0.5 overshoot 0.0%, 2.8% and 16.3%.
02Press Go, pause at 400 ms
The balls are at 154.7, 164.1 and 168.7 of 160 px. The timeline shows linear() tracks of 750, 700 and 1050 ms: Motion sampled each spring into a CSS easing.
03Read the option table
Line 4 shows precedence, line 3 the end at exactly the duration, line 5 the negative bounce, line 6 the clamp.
04Take the taste test
Play both drawers of the "Feel it" pair and pick one before the answer opens.
Try it yourself
- 1
Turn the dial to 1
In "Bounce balls" change the third
0.5incasesto1. The console saysζ 0, overshoot 85.4%, at rest after 8159 ms: our1 - bouncesays ζ 0 but Motion clamps it to 0.05, so the ball bobs for 8 s. - 2
Shorten the duration
In "What Motion makes of the options" change
duration: 500to200:overshoot 1.5%, at rest after 200 ms. A duration-based spring ends exactly on time with the same overshoot, only faster.
Code & diagrams
Motion's `spring()` is a generator: `next(t)` gives the position at t milliseconds. The console loops over it. The balls use `animate()` with the same options.
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
bounce 0: ζ 1, overshoot 0.0%, at rest after 720 ms
bounce 0.25: ζ 0.75, overshoot 2.8%, at rest after 679 ms
bounce 0.5: ζ 0.5, overshoot 16.3%, at rest after 819 msSeven option sets run through the generator.
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
no options (stiffness 100, damping 10) → overshoot 16.3%, at rest after 1068 ms
visualDuration 0.4, bounce 0.2 → overshoot 1.5%, at rest after 701 ms
duration 500, bounce 0.2 → overshoot 1.5%, at rest after 500 ms
stiffness 170, damping 26 + bounce 0.9 → overshoot 0.0%, at rest after 718 ms
bounce -0.5, visualDuration 0.4 → overshoot 0.0%, at rest after 787 ms
bounce 1, visualDuration 0.4 → overshoot 85.4%, at rest after 8159 ms
stiffness 170, damping 0 → overshoot 100.0%, never restsOpen the cart drawer on both sides. Which one would you want on the launch page?
A Bounce 0.5, 0.5 s
B Bounce 0.1, 0.35 s
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
Mix physics and time
Read line 4 of "What Motion makes of the options": stiffness and damping together with bounce 0.9 and duration 300.
Myth vs fact
Myth
Bounce 0 is the fastest way to settle.
Fact
In the example bounce 0.25 was at rest after 679 ms and bounce 0 after 720 ms: a critically damped spring creeps in.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
For properties the browser can animate on its own, Motion builds a
linear()easing from the spring (the tracks above are WAAPI animations withlinear()and a generator-measured duration). That is why such a spring has a fixed length and cannot take over an earlier speed (9.4, 9.5).
Remember this
- 1
The problem this solves: stiffness 170 and damping 26 mean nothing to a designer, and Mei's spec says "0.35 s, a light bounce". Motion (14.3 here) takes both descriptions and turns the friendly one into physics.
- 2
**
bounceis the damping ratio in disguise.** For bounce 0 to 1, ζ = 1 - bounce: 0 is critical damping, 0.25 is ζ 0.75, 0.5 is ζ 0.5. A negative bounce gives an overdamped spring, ζ = 1 ÷ (1 + bounce). ζ never falls below 0.05, so bounce 1 is not endless: it overshoots 85.4% and rests after 8159 ms. - 3
**
visualDurationis the time the move looks done.** For bounce 0 and up, Motion setsstiffness = (2π ÷ (1.2 × visualDuration))²anddamping = 2 × ζ × √stiffness(a negative bounce also rescales the frequency so the move still arrives on time). Its "at rest" thresholds depend on the move: a 0 to 1 value uses 0.005 and 0.01, a move over 5 units uses 0.5 and 2, so the rest times above are for a 0 to 1 move. For 0.4 s and bounce 0.2 that is stiffness 171.3 and damping 20.94. The bounce tail runs past it: rest after 701 ms. At 400 ms a bounce-0 spring is at 96.7%. - 4
A little bounce settles sooner than none. In the first example bounce 0.25 is at rest after 679 ms, bounce 0 after 720 ms: a critically damped spring creeps into its last pixels. Bounce 0.5 takes 819 ms.
- 5
**The
durationoption ends on time, with a catch.**duration: 500rests at exactly 500 ms, but duration-based springs ignore the speed of an interrupted animation. Motion's source says why: inherited velocity "causes massive oscillation on small-range animations". For gestures (9.4, 9.6) use stiffness and damping. - 6
Precedence and defaults. Give
stiffness,dampingormassand the time options are ignored:{ stiffness: 170, damping: 26, bounce: 0.9, duration: 300 }behaves as 170 and 26 (rest after 718 ms). With no options Motion 14.3 uses stiffness 100, damping 10, mass 1 (ζ 0.5, 16.3% overshoot, rest after 1068 ms). Read defaults from the installed package: other tables can be older. - 7
Tokens. Material 3 (since May 2025) names springs by role, such as
md.sys.motion.spring.fast.spatial: spatial springs for position and size, effects springs for things like colour and opacity, each fast, default or slow, in an expressive and a standard scheme. Read its spec page for the values. Lumen will use a few named springs, not numbers scattered in the code.
Explain it without notes
How do bounce and visualDuration relate to stiffness and damping?
When would you use stiffness and damping instead of duration and bounce?
Practice
Mei asks for "0.3 s, a hint of bounce". Write the Motion call for an element sliding in 200 px.
What damping gives a spring with stiffness 171.3 a ζ of 0.8?
Done when you can
You can turn bounce into a damping ratio
You can say which option wins when physics and time are mixed
You can say why duration-based springs do not suit gestures