PhilipAng
← All writing

May 20262 min read

Springs beat easing curves for interface motion

A duration and a bezier curve describe how an animation looks. A spring describes how an object behaves. That difference shows up everywhere once you notice it.

  • Design
  • Engineering

Most interface animation is still written as a duration plus an easing curve. Three hundred milliseconds, ease-out, done. It works, it is predictable, and it is why so much motion on the web feels like a slideshow.

Springs are better, and the reason is not that they look bouncier.

Duration is the wrong parameter

When you specify 300ms ease-out, you are describing the appearance of a transition. That is fine until the transition is interrupted.

Say a card is animating from left to right and the user grabs it halfway. With a duration-based tween you have a decision to make, and every option is bad: restart the animation from the current position with a fresh 300ms, and short movements feel sluggish; keep the original timeline, and the card lurches; cancel and snap, and it feels broken.

The awkwardness comes from the model. Duration assumes the animation runs start to finish undisturbed. Interfaces are interrupted constantly.

Springs model the object, not the transition

A spring has no duration. It has stiffness, damping and mass, and a current velocity. You do not tell it how long to take — you tell it where to go, and it gets there from wherever it currently is, at whatever speed it is currently moving.

Interruption stops being a special case. The target changes, the velocity carries through, the motion stays continuous. There is no seam because there was never a timeline to cut.

export const SPRING = {
  soft: { type: "spring", stiffness: 120, damping: 20, mass: 0.9 },
  bouncy: { type: "spring", stiffness: 300, damping: 14, mass: 0.8 },
  snappy: { type: "spring", stiffness: 400, damping: 30, mass: 1 },
} as const;

Three presets. Everything on this site uses one of them.

Why so few

This is the part people skip. The value is not in having springs available; it is in having few of them.

When every animation in a product draws from the same three, the whole interface starts to feel like one physical object with consistent material properties. Cards, chips, menus and page transitions all decelerate the same way. Nobody notices this consciously. They notice that it feels expensive.

Give yourself twelve springs and you have thrown that away. You now have twelve materials.

Where easing still wins

Springs are wrong for anything with a fixed, meaningful duration:

  • A progress bar tied to real progress.
  • A carousel on a timer.
  • Anything synchronised to audio or video.

Those have a genuine duration, so use one.

The accessibility part

None of this matters if it makes someone ill. Every spring on this site is gated on prefers-reduced-motion. When it is set, transforms drop out and only opacity remains — nothing moves, nothing disappears, no content becomes unreachable.

That last clause matters. The common failure is to disable the animation and accidentally disable the state change with it, leaving elements stuck invisible. Test the reduced-motion path like any other path.