Charts · 12

Easing versus springs

A transition is a duration and a curve: it will arrive in exactly 400 ms whatever you do. A spring is a force: stiffness pulls it toward the target, damping bleeds off speed, and it arrives when the physics says so. The difference is invisible on a single clean move and obvious the moment you interrupt one. Click the button, then click it again before the cards land. Both lines on the graph are recorded from the screen, not drawn from the formula.

Spring

ζ = 1.00 (critically damped, fastest with no bounce) · settles in ~725 ms

CSS transition

cubic-bezier(0.25, 0.1, 0.25, 1) · always 400 ms, whatever happens

Click again before it arrives. The spring carries its velocity into the new journey; the transition starts a fresh curve from wherever it was.

transition · ease
spring · default
leftrightnow−2.4 s

transition · spring · where it was told to go. Both lines are sampled from the screen every frame, the transition’s position read back from its computed transform.

Notes

The spring is a damped harmonic oscillator, F = −k(x − target) − cv, integrated with semi-implicit Euler in 1 ms substeps on every animation frame. The damping ratio ζ = c / (2√(km)) says the shape: under 1 it overshoots (Bouncy, Wobbly), over 1 it creeps in without crossing (Heavy), exactly 1 is the fastest arrival with no overshoot. Settle time is when the card is within 0.1% of the target and nearly still, from rest; it is the number a spring has instead of a duration. The transition’s position is read back each frame from the element’s computed transform matrix, which is why an interruption shows the fresh curve starting from the mid-flight position with zero velocity, the small hitch you feel in most transition-driven interfaces.

Presets are in the spirit of Framer Motion and react-spring defaults (stiffness 170, damping 26 is the classic react-spring default). CSS has no spring timing function as of 2026, though linear() with many stops can bake one; Apple’s UIKit has used springs by default since iOS 7, which is a large part of why iOS feels the way it does. Sources: react-spring and Framer Motion documentation; Apple WWDC 2023, Animate with springs; CSS Easing Functions Level 2.