Easing & spring
Easing scripts a move over a fixed time; a spring simulates physics and settles on its own. Knowing which to reach for is half of good motion.
In one line: reach for an easing curve when you want a move to take a known amount of time, and a spring when you want it to feel like a physical object responding to force.
What it is
Easing describes how an animation's speed changes across a fixed duration. You pick a curve — ease-out, ease-in-out, a custom cubic-bezier() — and a time, say 300 ms, and the browser interpolates between start and end along that curve. Nothing overshoots; the move ends exactly when the clock says so.
A spring is different in kind: instead of a duration, you describe a little physics model — stiffness (how hard it pulls toward the target), damping (how much it resists motion), and mass — and let it run until it settles. A spring can overshoot the target and oscillate before coming to rest, which is exactly what makes motion feel physical.
Why it matters
The choice changes how an interface feels. Easing is predictable and calm: good for things that should feel deliberate — a panel sliding open, a page transition, anything where a consistent, knowable duration matters. A spring is reactive and alive: good for direct manipulation — dragging, pressing, snapping — where the motion should echo a real force the user applied.
The common mistake is using a long ease where a spring belongs (the interface feels sluggish and scripted) or a bouncy spring where calm easing belongs (everything wobbles and reads as toy-like). Match the curve to the cause.
See it
Tweak it3
How it works
Two mental models, side by side:
- Easing = a path through time. The curve below maps elapsed time (left to right) to progress (bottom to top).
ease-outstarts fast and decelerates — it feels responsive because most of the distance is covered early.
- A spring = a simulation. There's no fixed end time; stiffness and damping decide how quickly it settles and whether it overshoots. Lower damping means more wobble.
Tune a spring by feel: raise stiffness for a snappier arrival, lower damping for more bounce, and keep the settle time short enough that it never adds perceived latency.
Build it
.card {
transform: translateX(0);
transition: transform 300ms cubic-bezier(0, 0, 0.58, 1); /* ease-out */
}
.card.is-moved {
transform: translateX(180px);
}
/* CSS has no real spring, but a tuned cubic-bezier can fake a small overshoot. */
.card.springy {
transition-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); /* slight overshoot */
}
@media (prefers-reduced-motion: reduce) {
.card,
.card.springy {
transition: none;
}
}import { motion, useReducedMotion } from "motion/react";
export function MoveDemo({ moved }) {
const reduce = useReducedMotion();
const to = { x: moved ? 180 : 0 };
return (
<>
{/* Eased: predictable, ends in exactly 300ms. Good for deliberate moves. */}
<motion.div
className="box"
animate={to}
transition={reduce ? { duration: 0 } : { duration: 0.3, ease: [0, 0, 0.58, 1] }}
/>
{/* Spring: reactive, settles on its own. Good for direct manipulation. */}
<motion.div
className="box"
animate={to}
transition={reduce ? { duration: 0 } : { type: "spring", stiffness: 600, damping: 12, mass: 0.6 }}
/>
</>
);
}Make it yours
Use the controls beside the demo above to change stiffness, damping, and mass — each change updates the example live.
Experiment in the playgroundReproduce it with an LLM
Reproduce it with an LLM
You are a senior front-end engineer. Build two versions of the same 'move a card 240px to the right on click' interaction in React using the Motion library: one with a fixed-duration ease-out (about 300 ms), and one with a spring. For the spring, expose stiffness, damping, and mass as props and pick defaults that settle in roughly 400 ms with a small, tasteful overshoot. Requirements: animate only transform; fully disable animation under prefers-reduced-motion: reduce; keep the control a real button with a visible focus-visible ring. Add a one-line code comment on each that says when to prefer it. Return only the component code with imports.
Pitfalls & accessibility
Related
Jelly button: squash & stretch on press
Make a control feel physical: it squashes when pressed and springs back with a wobble.
Motion principles
Borrowed from classic animation: timing, anticipation, follow-through, and staging turn a mechanical move into one that reads as intentional.
Further reading
- Your animation library's spring documentation, for how stiffness, damping, and mass map to feel.
- A primer on
cubic-bezier()timing functions and how to read an easing curve.