Sam Joyce

Spring to linear()

Simulate a damped spring and compile it into a CSS linear() easing, with a visible error budget.

Spring to linear()

k 380 · c 34 · m 1.0

linear()

--ease-out

Both run for 449 ms.

Preset
380
34
1.0
0.0040
Damping ratio
0.872 under-damped
Settles in
449 ms
Overshoot
0.37%
Points
14
Max error
0.0066
CSS
173 B
linear(0, 0.0056 1.3%, 0.0251 2.8%, 0.0944 5.8%, 0.5035 18%, 0.6844 24.5%, 0.8104 30.8%, 0.9024 37.8%, 0.9367 41.8%, 0.9636 46.3%, 0.9826 51.2%, 0.9946 56.8%, 1.0035 70%, 1)
Describe this figure

Position against time for a spring with stiffness 380, damping 34 and mass 1.0: damping ratio 0.872, under-damped. It rises from 0 to rest at 1 in 449 ms, overshooting by 0.37% on the way.

The linear() easing keeps 14 of the 401 samples. The largest gap between it and the spring is 0.0066, against a tolerance of 0.0040. Of the two squares, the top one plays the generated easing and the bottom one the site’s ease-out over the same duration.

Things to try

  • Choose detent and press Play. The filled square runs 4.53% past its mark and comes back; the outlined --ease-out square never passes it.
  • Double the mass on the settle preset and the damping ratio falls from 0.872 to 0.617. That change takes the overshoot from 0.37% to 8.54% and stretches the settle time from 449 to 743 ms.
  • Turn on Show the trick, then move the tolerance between its ends. At 0.0005 the settle spring needs 38 points and 483 bytes; at 0.02 it needs 6 points and 67 bytes, and the whiskers show what that saving costs.

How it works

The figure solves the spring exactly: a mass released from 0 with no velocity, pulled towards 1. Stiffness is how hard it pulls and damping is how quickly the motion dies away; mass slows everything down. The shape comes down to one number, the damping ratio c / (2 * sqrt(k * m)). Below 1 the spring overshoots and rings. Above 1 it creeps in.

The settle time is the last moment the spring is more than 0.1% away from rest. The curve is sampled at 401 evenly spaced points across that window, and Ramer–Douglas–Peucker thins them out. It keeps the sample furthest from the straight line between the ends, splits there, and repeats until every dropped sample lies within the tolerance of its chord. What survives becomes the stops of linear(): a value and a percentage of the duration each.

The tolerance is measured perpendicular to the chord, but the error you see is vertical. Where the curve is steep the two differ, so the settle preset’s largest gap is 0.0066 against a tolerance of 0.004. The readout reports the vertical figure because that is what the eye notices.

Play hands the string to element.animate() as the easing, with the settle time as the duration. The outlined square runs the site’s --ease-out over the same time.

This site’s --spring-settle, --spring-detent and --spring-soft were generated with this code.

Keyboard

Key Action
Tab Move between the presets, sliders and buttons
Arrow keys Change the focused slider or preset
Home, End Jump to a slider’s ends
P Play or stop, while focus is inside the figure

Published

More from the lab

Nº 005Next

Optical centre

Geometric versus optical centring, with the centroid drawn.

lab · interface