Cubic Bezier in Depth: Mastering the Motion Curve

Every time you write transition: transform 0.3s ease-out, you’re using a shorthand that hides a more complex mathematical function. Understanding that function from the ground up lets you produce any motion feel you want instead of picking from five built-in presets.

What a bezier curve is

A cubic bezier is a curve defined by four points: P0 (start), P1 (handle 1), P2 (handle 2), P3 (end). In CSS, P0 is always (0, 0) and P3 is always (1, 1) — only P1 and P2 change.

cubic-bezier(x1, y1, x2, y2)
/*            P1        P2  */

The X axis is time (0 = start, 1 = end). The Y axis is the animation’s progress (0 = starting position, 1 = target position). The curve describes the rate at which progress changes over time — not the position.

Reading a curve

ease-out: cubic-bezier(0, 0, 0.58, 1)

  • P1 = (0, 0): the first handle sits at the origin → the animation starts fast
  • P2 = (0.58, 1): the last handle sits high and to the left → the animation slows down early

ease-in: cubic-bezier(0.42, 0, 1, 1)

  • P1 = (0.42, 0): the first handle is shifted strongly to the right → the animation starts slowly
  • P2 = (1, 1): the last handle is at the end corner → no deceleration

Why y > 1 creates overshoot

When the y of P1 or P2 exceeds 1.0, the curve produces output values > 1.0 — meaning the animation passes the endpoint before coming back. This is how CSS fakes a spring:

/* Spring — overshoot ~17% */
cubic-bezier(0.34, 1.56, 0.64, 1)

Y = 1.56 creates an overshoot of about 56% beyond the distance from start to end. With transform: translateX(100px), the element travels to ~156px and then returns to 100px.

Similarly, y < 0 creates undershoot — the animation moves in the opposite direction before heading toward the target. Used for a “pull back before launch” effect.

The five CSS presets and how they feel

PresetCurveFeel
linear(0, 0, 1, 1)Robotic, unnatural
ease(0.25, 0.1, 0.25, 1)Browser default, a bit lazy
ease-in(0.42, 0, 1, 1)Slow then fast, used for exits
ease-out(0, 0, 0.58, 1)Fast then slow, used for entrances
ease-in-out(0.42, 0, 0.58, 1)Balanced, used for loops

The problem: all five presets are “generic.” They don’t convey the product’s personality.

Building a custom curve to match a target feel

Snappy / crisp — instant response, settles immediately:

cubic-bezier(0.2, 0, 0, 1)

P1 pulls hard to the right (a fast burst), P2 sits low on the left (abrupt deceleration).

Gentle / soft — unhurried, no rush:

cubic-bezier(0.4, 0, 0.6, 1)

Both handles sit near the center; the curve is gentle and even.

Aggressive / dramatic — slow start, explosive finish:

cubic-bezier(0.8, 0, 0.2, 1)

P1 is far to the right (a large delay before starting), P2 is to the left (strong acceleration at the end).

Material Design standard — a balance between snappy and gentle:

cubic-bezier(0.2, 0, 0, 1)   /* standard */
cubic-bezier(0.05, 0.7, 0.1, 1) /* emphasized */

How it differs from a spring

A cubic bezier always has a fixed duration. No matter how complex the curve, the animation ends exactly when transition-duration specifies. A spring has no fixed duration — it settles when velocity approaches 0, and the time depends on stiffness and damping.

Bezier is good for: fades, color, layout shifts, page transitions — anything that needs predictable timing. Spring is good for: interactive response, direct manipulation — anything that needs a physical feel.

See more in Ease vs Spring and Spring Parameters: mass, stiffness, damping.

Tools

Chrome DevTools: click the curve icon next to transition-timing-function and drag the handles visually.

cubic-bezier.com: real-time preview with a side-by-side comparison.

Tip: always test a curve at 0.25× speed in DevTools — slow motion reveals even the smallest artifacts.

Common mistakes

Using linear for a loading animation: linear looks mechanical. Use ease-in-out for shimmer/pulse.

Duration too long with ease-out: ease-out 0.8s looks like lag. Usually 150–300ms is enough for a UI element.

Using the same curve for enter and exit: enter should be ease-out (fast in, slow settle). Exit should be ease-in (slow start, fast disappearance) — this matches how the human eye tracks motion.

See how these principles apply in practice: Hover Micromotion, Scroll Reveal Patterns, Page Transition Patterns.