CSS Easing Functions Explained: cubic-bezier, Timing Keywords, and When to Use Each

CSS easing functions define how an animation progresses over time — whether it starts slowly, ends abruptly, or accelerates through the middle. Here's how they work, from built-in keywords to custom cubic-bezier curves.

CSS Easing Functions Explained: cubic-bezier, Timing Keywords, and When to Use Each

A CSS easing function defines how an animation's progress maps to time. Without easing, an animation moves at a constant speed (linear). With easing, it can accelerate, decelerate, bounce, or overshoot — making motion feel physical and intentional rather than robotic.

Easing functions apply to two CSS properties:

  • transition-timing-function — controls CSS transitions
  • animation-timing-function — controls @keyframes animations

Both accept the same set of values: keywords (ease, linear, ease-in, ease-out, ease-in-out), the cubic-bezier() function, or the newer steps() function.


Quick Reference

/* Keywords */
transition: opacity 0.3s ease;
transition: transform 0.5s ease-in-out;
transition: left 1s linear;

/* Custom cubic-bezier */
transition: transform 0.4s cubic-bezier(0.25, 0.1, 0.25, 1);

/* Steps (frame-by-frame) */
animation: sprite 0.6s steps(6) infinite;

Every CSS easing keyword is shorthand for a specific cubic-bezier() value. There are no "magic" keywords — they all map to the same underlying math.


The Five Built-In Keywords

CSS provides five named easing functions. Each is a predefined cubic-bezier curve:

Keyword cubic-bezier equivalent Behavior
linear cubic-bezier(0, 0, 1, 1) Constant speed. No acceleration or deceleration.
ease cubic-bezier(0.25, 0.1, 0.25, 1) Fast start, slow end. The CSS default.
ease-in cubic-bezier(0.42, 0, 1, 1) Slow start, fast end. Acceleration.
ease-out cubic-bezier(0, 0, 0.58, 1) Fast start, slow end. Deceleration.
ease-in-out cubic-bezier(0.42, 0, 0.58, 1) Slow start, slow end. Symmetric acceleration/deceleration.

Key difference between ease and ease-in-out: Both slow down at the end, but ease starts slightly faster than ease-in-out. The ease-in-out curve is symmetric — it has equal acceleration and deceleration — while ease is asymmetric.

Try it yourself: Use the Easing Function Explorer to compare these curves side by side with live animation previews.


How cubic-bezier Works

The cubic-bezier() function defines a curve using four numbers: cubic-bezier(x1, y1, x2, y2).

These four values define two control points (P1 and P2) on a Bézier curve. The curve always starts at P0 = (0, 0) and ends at P3 = (1, 1). The x-axis represents time (0% to 100% of the animation duration), and the y-axis represents progress (0% to 100% of the animated property's change).

The control points

  • P1 (x1, y1): Influences the beginning of the curve. Pulling it down creates slow starts; pulling it up creates fast starts.
  • P2 (x2, y2): Influences the end of the curve. Pulling it down creates fast endings; pulling it up creates slow endings.

Constraints

  • x1 and x2 must be between 0 and 1. Time cannot go backward — you can't have a control point at negative time or past 100%.
  • y1 and y2 can be any number. Values above 1 cause overshoot (the animation goes past the target, then comes back). Values below 0 cause undershoot (the animation goes backward before moving forward).

Reading the curve

At any given point in time (x-axis), the curve tells you how far through the animation you are (y-axis):

  • A steep section means fast motion — a lot of progress in a short time.
  • A flat section means slow motion — little progress over that time span.
  • A vertical section (or near-vertical) means almost instant change.
  • A section above y=1 means overshoot — the value temporarily exceeds the target.

Common Easing Families

Beyond the five keywords, developers often use easing curves from established libraries. These follow a naming convention based on mathematical functions:

Sine

Gentle, subtle easing based on a sine wave. The least dramatic of the named curves:

/* easeInSine */    cubic-bezier(0.12, 0, 0.39, 0)
/* easeOutSine */   cubic-bezier(0.61, 1, 0.88, 1)
/* easeInOutSine */ cubic-bezier(0.37, 0, 0.63, 1)

Quad (Quadratic)

Moderate easing based on t². More noticeable than sine but still smooth:

/* easeInQuad */    cubic-bezier(0.11, 0, 0.5, 0)
/* easeOutQuad */   cubic-bezier(0.5, 1, 0.89, 1)
/* easeInOutQuad */ cubic-bezier(0.45, 0, 0.55, 1)

Cubic

Stronger easing based on t³. A common default in animation tools:

/* easeInCubic */    cubic-bezier(0.32, 0, 0.67, 0)
/* easeOutCubic */   cubic-bezier(0.33, 1, 0.68, 1)
/* easeInOutCubic */ cubic-bezier(0.65, 0, 0.35, 1)

Quart (Quartic)

Pronounced easing based on t⁴. Strong acceleration and deceleration:

/* easeInQuart */    cubic-bezier(0.5, 0, 0.75, 0)
/* easeOutQuart */   cubic-bezier(0.25, 1, 0.5, 1)
/* easeInOutQuart */ cubic-bezier(0.76, 0, 0.24, 1)

Back (Overshoot)

The animation overshoots the target and springs back. Adds a playful or elastic feel:

/* easeInBack */    cubic-bezier(0.36, 0, 0.66, -0.56)
/* easeOutBack */   cubic-bezier(0.34, 1.56, 0.64, 1)
/* easeInOutBack */ cubic-bezier(0.68, -0.6, 0.32, 1.6)

Note the y-values outside 0–1. These cause the overshoot/undershoot effect.


The steps() Function

The steps() function divides the animation into a fixed number of discrete frames, creating a "stepping" or "frame-by-frame" effect instead of a smooth curve.

Syntax

animation-timing-function: steps(<number>, <position>);
  • number: How many equal steps to divide the animation into
  • position: jump-start, jump-end (default), jump-both, jump-none, or the legacy values start and end

When to use steps

  • Sprite sheet animations: Stepping through frames of a character walk cycle or icon animation
  • Typewriter effects: Revealing characters one at a time
  • Clock-like motion: Seconds ticking, loading indicators
  • Deliberate staccato: When you want jumpy, non-smooth motion

Example: sprite animation

.sprite {
  width: 64px;
  height: 64px;
  background: url('spritesheet.png');
  animation: walk 0.5s steps(8) infinite;
}

@keyframes walk {
  to { background-position: -512px 0; }
}

Choosing the Right Easing

Different UI actions benefit from different easing curves. Here are practical guidelines:

Entrances (elements appearing)

Use ease-out or easeOutCubic. Elements should arrive quickly and settle into place. The user's attention follows the element as it decelerates, which feels natural — like an object sliding to a stop.

.modal-enter {
  animation: fadeSlideIn 0.3s cubic-bezier(0.33, 1, 0.68, 1);
}

Exits (elements disappearing)

Use ease-in or easeInCubic. The element accelerates away. Departures should be quick — users don't need to watch something leave.

.modal-exit {
  animation: fadeSlideOut 0.2s cubic-bezier(0.32, 0, 0.67, 0);
}

State changes (hover, toggle, resize)

Use ease-in-out or easeInOutCubic. Symmetric easing feels balanced for changes that happen "in place." Neither the start nor end should feel abrupt.

.button {
  transition: background-color 0.2s cubic-bezier(0.65, 0, 0.35, 1);
}

Micro-interactions (small feedback)

Use ease or the default. For very short transitions (under 200ms), the easing choice matters less because the duration is too short for the human eye to perceive the curve shape in detail. ease works well here.

.icon {
  transition: transform 0.15s ease;
}

Playful/bouncy animations

Use easeOutBack. The overshoot gives a satisfying "pop" effect. Use sparingly — overuse makes a UI feel toy-like.

.notification {
  animation: popIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}

Duration and Easing Work Together

Easing without appropriate duration feels wrong. Some rules of thumb:

Action Duration range Why
Hover/focus effects 100–200ms Instant feedback feels responsive
Fade in/out 150–300ms Fast enough to not delay the user
Slide/transform 200–500ms Needs enough time for the curve to be visible
Page transitions 300–600ms Longer motions that establish context
Complex choreography 500–1000ms+ Multiple elements moving together

Shorter durations need simpler easing. A 100ms transition with easeInOutBack will look glitchy because there isn't enough time for the overshoot to read visually. Use ease or ease-out for fast transitions.

Longer durations need deliberate easing. A 500ms linear transition feels robotic. Use ease-out or ease-in-out to add character.


CSS linear() — The New Easing Function (2024+)

Modern browsers (Chrome 113+, Firefox 112+, Safari 17.2+) support linear(), a new easing function that defines curves as a series of points rather than Bézier control points.

Why it exists

cubic-bezier() can only express a limited set of curves — specifically, cubic Bézier curves with exactly two control points. It cannot create bounce effects, spring physics, or multi-segment curves natively.

linear() accepts an arbitrary number of output points, and the browser interpolates linearly between them:

/* A bounce effect that cubic-bezier cannot express */
transition: transform 0.6s linear(
  0, 0.36, 0.68, 0.92, 1.06, 1.12,
  1.06, 1, 0.97, 1, 1
);

When to use linear()

  • Bounce animations
  • Spring physics approximations
  • Multi-stage easing curves
  • Any shape that a single cubic Bézier cannot represent

For standard ease-in/ease-out patterns, cubic-bezier() remains simpler and more widely supported.


Easing in @keyframes vs. Transitions

Easing works slightly differently in transitions and keyframe animations:

Transitions

The easing applies across the entire transition from start state to end state:

.box {
  transform: translateX(0);
  transition: transform 0.5s ease-out;
}
.box:hover {
  transform: translateX(200px);
}

Keyframe animations

The easing applies between each pair of keyframes, not across the whole animation:

@keyframes bounce {
  0%   { transform: translateY(0); }
  50%  { transform: translateY(-100px); }
  100% { transform: translateY(0); }
}

.ball {
  /* ease-out applies: 0%→50% and 50%→100% independently */
  animation: bounce 1s ease-out;
}

This means each keyframe segment has its own easing curve. To apply different easing to different segments, set animation-timing-function within individual keyframes:

@keyframes bounce {
  0%   { transform: translateY(0); animation-timing-function: ease-in; }
  50%  { transform: translateY(-100px); animation-timing-function: ease-out; }
  100% { transform: translateY(0); }
}

Common Mistakes

  1. Using linear for everything. Linear motion feels mechanical and unnatural. Real objects accelerate and decelerate due to friction and gravity. Use ease-out as a safe default.

  2. Using ease-in for entrances. This is counterintuitive: ease-in (slow start) is better for exits because the element accelerates away. For entrances, use ease-out (fast start, slow landing).

  3. Overshoot on functional elements. easeOutBack looks great on decorative animations but can cause layout issues on functional elements. An overshooting width change might temporarily overlap adjacent elements.

  4. Ignoring prefers-reduced-motion. Some users experience motion sickness or distraction from animations. Always respect this media query:

    @media (prefers-reduced-motion: reduce) {
      * {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
      }
    }
    
  5. Same easing for enter and exit. Symmetric easing (same curve for appearing and disappearing) often feels unbalanced. Entrances should decelerate; exits should accelerate.

  6. Too many easing curves in one interface. Stick to 2–3 easing curves across your entire design system. Mixing five or six different curves creates visual inconsistency.


Testing and Debugging Easing

Browser DevTools

Chrome and Firefox DevTools show a visual curve editor when you click the easing icon next to transition-timing-function or animation-timing-function in the Styles panel. You can drag control points to experiment in real time.

Easing Function Explorer

Use the Easing Function Explorer to visualize and compare easing curves with live animation previews. Click any preset to see its cubic-bezier values and copy the CSS directly.

Slow down animations

During development, add a temporary override to slow down all animations:

* {
  transition-duration: 3s !important;
  animation-duration: 3s !important;
}

This makes it easier to see the shape of the easing curve.


Material Design and Apple HIG Easing

Major design systems have specific easing recommendations:

Material Design 3

Material Design uses four standard easing curves:

  • Emphasized: cubic-bezier(0.2, 0, 0, 1) — for prominent transitions
  • Emphasized decelerate: cubic-bezier(0.05, 0.7, 0.1, 1) — for entering elements
  • Emphasized accelerate: cubic-bezier(0.3, 0, 0.8, 0.15) — for exiting elements
  • Standard: cubic-bezier(0.2, 0, 0, 1) — for default motion

Apple Human Interface Guidelines

Apple recommends default spring animations in native development (SwiftUI), which don't map directly to cubic-bezier. For web, Apple's design patterns typically use:

  • Quick, subtle transitions (150–250ms)
  • ease-in-out or custom curves that emphasize deceleration

Performance Considerations

Easing functions themselves have negligible performance impact — the browser calculates them in constant time regardless of complexity. What matters is what you're animating:

  • transform and opacity are GPU-composited. Animate these for smooth 60fps motion.
  • width, height, margin, padding trigger layout recalculations. Animating these can cause jank.
  • color, background-color trigger paint but not layout. Generally fine for short transitions.

The easing curve doesn't change which properties are expensive. But perceptually, ease-out can make a janky animation feel smoother because the motion decelerates — any stuttering at the end is less noticeable when the element is already moving slowly.


FAQ

What is a CSS easing function?

A CSS easing function controls the speed of an animation over its duration. It maps time progression to animation progress. Without easing, animations move at constant speed (linear). With easing, they can accelerate, decelerate, or overshoot.

What is the default CSS easing?

The default CSS easing for transition is ease, which is equivalent to cubic-bezier(0.25, 0.1, 0.25, 1). For animation, the default is also ease. If no timing-function is specified, the browser uses ease.

What is the difference between ease and ease-in-out?

Both start and end slowly, but ease is asymmetric — it starts faster and ends more gradually. ease-in-out (cubic-bezier(0.42, 0, 0.58, 1)) is symmetric, with equal acceleration and deceleration. For most UI transitions, ease is fine. Use ease-in-out when you want strictly balanced motion.

What does cubic-bezier mean in CSS?

cubic-bezier(x1, y1, x2, y2) defines a custom easing curve using two control points on a Bézier curve. The x-axis is time (0 to 1), the y-axis is progress (0 to 1, or beyond for overshoot). The four numbers position the two control points that shape the curve.

Can cubic-bezier values be negative?

The x-values (x1 and x2) must be between 0 and 1 — time cannot go backward. The y-values (y1 and y2) can be any number. Negative y-values cause the animation to temporarily reverse before reaching the target. Values above 1 cause overshoot.

When should I use ease-in vs. ease-out?

Use ease-out (deceleration) for elements entering the screen — they arrive quickly and settle into place. Use ease-in (acceleration) for elements exiting — they speed up as they leave. This matches how physical objects behave.

What is steps() in CSS?

steps() divides an animation into a fixed number of discrete jumps instead of a smooth curve. Common uses include sprite sheet animations, typewriter effects, and clock-like ticking motion. Syntax: steps(count, jump-end).

What is the new CSS linear() function?

linear() is a modern easing function (Chrome 113+, Firefox 112+, Safari 17.2+) that lets you define arbitrary easing curves as a series of points. Unlike cubic-bezier(), it can express bounce effects, spring physics, and multi-segment curves.

How do I test CSS easing functions?

Use browser DevTools (click the easing icon in the Styles panel to get a visual curve editor), the Easing Function Explorer for side-by-side comparisons, or temporarily slow down all animations with transition-duration: 3s !important.

Does easing affect performance?

The easing calculation itself has negligible performance cost. What matters is the property being animated. Use transform and opacity for smooth GPU-composited animation. Avoid animating layout properties like width or height.

Should I respect prefers-reduced-motion?

Yes. Some users experience motion sickness from animations. Always include a @media (prefers-reduced-motion: reduce) rule that shortens or removes animation durations.

How many easing curves should a design system use?

Most design systems use 2–4 standard easing curves: one for entrances, one for exits, one for state changes, and optionally one for emphasis. Using too many different curves creates visual inconsistency.


Related Tools

Related Tools