Understanding CSS Easing: Why the Same Animation Can Feel Different
Two CSS transitions can move an element the same distance over the same duration and feel completely different in character, because the easing curve controls how speed changes across that duration, not just how long it takes.
Quick answer: CSS easing controls how an animation's speed changes over its duration, not the duration itself. The same 300ms transition can feel snappy, sluggish, or natural depending only on the easing curve: linear moves at a constant rate throughout, while ease-in, ease-out, ease-in-out, and custom cubic-bezier() curves each describe a different, non-constant relationship between elapsed time and covered distance.
Set the same element moving the same distance over the same 300 milliseconds, and swap only the easing function, and the animation can feel snappy, sluggish, mechanical, or natural - without changing a single other value. Easing controls something duration alone doesn't touch: how speed itself changes across that time.
What an easing curve actually describes
A CSS easing function is a curve mapping elapsed time to progress - at 50% of the duration, has the animation covered 50% of the distance, or 20%, or 80%? linear covers distance at a perfectly constant rate throughout - 50% of the time always means exactly 50% of the distance. Every other easing keyword and custom curve describes some non-constant relationship between time and progress instead, which is what produces the feeling of acceleration or deceleration.
How a bezier curve actually encodes that relationship
Every easing curve in CSS, including the named keywords, is a cubic bezier under the hood: a curve anchored at two fixed points, (0, 0) at the start and (1, 1) at the end, shaped by two movable control points in between. The horizontal axis is time, the vertical axis is progress. Where those control points sit determines how quickly the curve climbs from 0 to 1: pull them toward the bottom-left and the curve stays flat (slow progress) early on before racing to catch up; pull them toward the top-right and it's the opposite. That's the entire mechanism behind every named easing keyword, they're just pre-chosen control point positions with a memorable label attached.
Why the named keywords feel different from each other
- ease-in: starts slow, accelerates toward the end. Feels like something building up momentum - often used for elements leaving the screen.
- ease-out: starts fast, decelerates toward the end. Feels like something settling into place - often used for elements arriving.
- ease-in-out: slow at both ends, faster in the middle. Feels smoother and more deliberate than either alone.
- linear: constant speed throughout. Feels mechanical, precisely because almost nothing in the physical world actually moves this way.
These aren't arbitrary labels - each one is itself defined as a specific cubic-bezier() curve under the hood, just given a memorable name for the common cases.
The exact curves behind the keywords
The CSS specification defines each keyword as a fixed set of control points: ease is cubic-bezier(0.25, 0.1, 0.25, 1.0), ease-in is cubic-bezier(0.42, 0, 1.0, 1.0), ease-out is cubic-bezier(0, 0, 0.58, 1.0), and ease-in-out is cubic-bezier(0.42, 0, 0.58, 1.0). Notice the pattern: ease-in's first control point sits low (slow start) and its second sits at the maximum (still accelerating right up to the end), while ease-out mirrors that on the way out. ease-in-out roughly combines both halves. None of this is a coincidence, it's why the keywords behave the way the earlier bullet list describes.
Where cubic-bezier lets you go beyond the presets
A cubic-bezier(x1, y1, x2, y2) value defines a custom curve using two control points, letting an animation start fast, slow down, then speed up again - motion the four named keywords can't represent at all, since they're limited to simple single-direction acceleration or deceleration. Cubic Bezier Generator makes this tunable visually, dragging the control points and watching the resulting motion directly, rather than guessing at four numbers and reloading to check.
Why "natural-feeling" motion is rarely linear
Real physical motion almost always involves acceleration or deceleration - something dropped speeds up under gravity, something thrown decelerates from friction. Because people have a lifetime of intuition about how physical objects actually move, an interface animation that mimics that acceleration/deceleration pattern reads as natural, while one that moves at a dead-constant rate reads as artificial - even though linear is, mathematically, the simplest possible option.
Common easing mistakes worth avoiding
Using the same curve for every animation regardless of direction. An element entering the screen and one leaving it are different physical stories, arriving and settling versus departing and building speed, so ease-out on the way in and ease-in on the way out (rather than the same ease-in-out for both) usually reads more intentional.
Pairing a slow curve with a very short duration. ease-in-out on a 100ms transition barely has room to show its shape before the animation ends, so the deliberate, weighted feeling it's meant to produce gets lost. Gentler curves generally need more time to register; snappier ones work fine short.
Reaching for linear out of habit. It's the default a lot of people write first because it's the easiest to reason about mentally, but it's almost never the best-looking choice for interface motion. Reserve it for cases where constant speed is literally the point, like a progress bar or a marquee.
Forgetting that easing and keyframed animations interact. A multi-step @keyframes animation can specify a different timing function per step, so applying one global curve to the whole sequence can flatten motion that was meant to speed up and slow down at specific points along the way. Keyframes Generator makes it straightforward to preview per-step timing without hand-editing percentages and guessing at the result.
Matching easing to duration
Easing and duration aren't independent choices, they compound. A short, snappy interaction (a button press, a toggle flip) typically reads best around 100-200ms with a curve like ease-out that front-loads the motion, since the eye barely has time to track a slower buildup. A longer, more choreographed transition (a panel sliding into view, a modal opening) commonly sits around 250-400ms and can afford a gentler ease-in-out, giving the motion room to actually show its acceleration and deceleration rather than reading as an abrupt jump. Pushing a slow curve onto a very short duration, or a snappy curve onto a long one, is a common way an otherwise reasonable easing choice ends up feeling wrong anyway.
The short version
Easing controls the shape of motion over a fixed duration, not the duration itself - the same 300ms transition can feel completely different depending on whether it's constant-speed, accelerating, decelerating, or something more complex entirely. The named keywords cover the common shapes, and the CSS spec's own fixed control-point values behind them explain exactly why each one feels the way it does; cubic-bezier() and linear() are what let an animation's motion be tuned precisely rather than picked from a short list of presets. Cubic Bezier Generator and Keyframes Generator both make that tuning visual instead of trial-and-error.
Tools mentioned in this article
Frequently asked
What's the actual difference between ease-in and ease-out?
ease-in starts slow and accelerates toward the end - good for something leaving the screen, since it builds momentum on the way out. ease-out starts fast and decelerates toward the end - good for something arriving, since it settles into place rather than slamming to a stop.
Why does linear easing usually look worse than any curve?
Real-world motion almost never moves at a perfectly constant speed - things accelerate and decelerate due to physics. Linear easing removes that entirely, which is exactly why it tends to read as mechanical or robotic rather than natural, even though it's the mathematically simplest option.
Is cubic-bezier the only way to define custom easing in CSS?
CSS also supports the newer linear() function for more complex multi-point curves (like bounces), but cubic-bezier() covers the vast majority of practical easing needs with just four numbers, and it's what the named keywords like ease and ease-in-out are themselves defined as under the hood.
What do the numbers inside cubic-bezier() actually mean?
They're two control points, (x1, y1) and (x2, y2), sitting between the curve's fixed start (0, 0) and end (1, 1). The x values pull the curve toward or away from certain moments in time; the y values pull it toward or away from certain amounts of progress. Push a y value past 1 or below 0 and the curve overshoots, which is how a bounce-like effect gets built from otherwise ordinary numbers.
Can two animations with the same duration still feel like different speeds?
Yes, and this is the whole reason easing matters. A curve that reaches most of its progress early (like ease-out) feels quick and responsive even at 400ms. A curve that holds back progress until late (like ease-in) can feel sluggish at that same 400ms, because most of the visible motion is crammed into the final stretch.
Does easing only apply to CSS transitions, or animations too?
Both. The transition-timing-function property covers transition, and @keyframes animations accept the same kind of curve through animation-timing-function, either once for the whole animation or set individually on each keyframe step for more control. JavaScript-driven animation libraries implement their own version of the same idea for the same reason.
More in Design & CSS
CSS Box Shadows: The Difference Between Elevation and Glow
The same box-shadow property produces a card that looks like it's floating above the page, or one that looks like it's radiating light, depending entirely on how the offset, blur, and color values are chosen.
How CSS Gradients Actually Work (And Why Yours Might Look Banded)
Linear vs radial gradients, what a color stop actually controls, and the real reason smooth gradients sometimes show visible stripes.
More guides like this
Practical, tool-linked how-tos across PDF, image, finance, video, and more, no signup to read them.
