Why Your Animations Feel Wrong
Most of the time when an animation feels "off," it's not broken. It does exactly what it was asked to do — it starts, it moves, it stops. The problem is what it was asked to do.
The default web animation is the cubic Bézier curve. When you write transition: transform 0.3s ease-in-out, you're describing one: a mathematical function that maps time to progress. The curve's shape controls whether the animation starts fast or slow, decelerates out, or both.
Cubic Bézier curves are predictable and efficient. They're also entirely unlike anything that exists in the physical world.
Real objects don't follow curves. They have mass. When a spring releases, it overshoots its target — momentum carries it past — then oscillates and settles. It never stops at exactly the destination on the first pass.
This matters because we've spent our entire lives watching things move. We've built an instinctive expectation: things with momentum should overshoot. An animation that lands precisely at its destination feels smooth, but also slightly empty.
Interactive demo
Watch where the yellow ball goes relative to the target line.
ease-in-out — cubic-bezier(0.4, 0, 0.2, 1)
elastic.out — spring physics (overshoots the line)
The grey dot is easing. The yellow dot has mass. Notice how the yellow one crosses the line before coming back. That overshoot is not a mistake — it's what makes the motion feel consequential.
The physics, briefly
Spring physics comes from Hooke's Law: the force exerted by a spring is proportional to how far it's displaced from its resting position. When you release a spring, three things happen in sequence:
- The restoring force accelerates the mass toward the target
- Momentum carries it past the target (overshoot)
- The spring pulls it back — repeating until energy dissipates
Three parameters control the feel:
- Mass — heavier objects have more momentum and overshoot further
- Stiffness — a stiffer spring pulls back faster, reducing overshoot
- Damping — friction that absorbs energy and reduces oscillation
Designers don't need to think in these terms directly. But understanding that there are physical parameters explains why "add more spring" is not a single dial — it's a negotiation between three forces.
When spring matters and when it doesn't
Spring animations are not universally better. They're better for interactions — hover states, click feedback, drawer opens, tooltip appearances. Moments where the interface is responding to something a human did.
Easing curves are better for ambient or cinematic motion — page transitions, scroll-driven storytelling, loading sequences. Moments where the interface is telling something rather than responding to it.
Tip
If a human triggered it, use spring. If it's ambient or sequential, use ease.
The reason is expectation. When you click a button, you expect a physical consequence — mass, inertia, rebound. When a hero image fades in on load, you're watching a sequence; precise timing matters more than physical believability.
Using spring in GSAP
GSAP's elastic.out is a reasonable spring approximation:
gsap.to(element, {
x: 200,
ease: 'elastic.out(1, 0.35)',
duration: 0.9,
})
The two parameters:
- Amplitude (first): how much it overshoots.
1is full overshoot;0.5is subtle. Start around0.6–0.8for UI interactions. - Period (second): how quickly it settles. Lower = fewer oscillations.
0.3–0.4is usually right.
For hover effects and micro-interactions I use elastic.out(0.6, 0.4) as a baseline — noticeable spring without feeling toy-like.
Without GSAP: the CSS linear() function
Since Chrome 117, you can approximate spring physics in pure CSS:
.spring {
transition: transform 0.6s linear(
0, 0.009, 0.035 2.1%, 0.141, 0.281 6.7%,
0.723 12.9%, 0.938 16.7%, 1.017, 1.077, 1.121,
1.149 24.3%, 1.163, 1.161, 1.154 29.9%,
1.129 32.8%, 1.051 39.6%, 1.017 43.1%,
0.991, 0.977 51%, 1 100%
);
}
That function is a real spring — generated from spring.computer by Sam Selikoff. You input mass, stiffness, and damping, and it exports the CSS linear() curve.
Note
linear() is not yet supported in Safari (as of early 2026). For production, either use GSAP or include a @supports fallback with a standard ease.
The underlying principle
This is not about complexity for its own sake. It's about matching what users unconsciously expect from motion.
The interface that feels inevitable is one where every action has a proportionate physical consequence. Where clicking a button feels like pressing something that has weight. Where a panel opening feels like it was pushed rather than switched.
Easing curves are a tool. Spring physics is closer to truth. The gap between them is small but perceptible — and in interfaces, small perceptible things compound.
Most users will never notice the spring. They'll just feel that something is right.