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 transitionsanimation-timing-function— controls@keyframesanimations
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 valuesstartandend
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
-
Using
linearfor everything. Linear motion feels mechanical and unnatural. Real objects accelerate and decelerate due to friction and gravity. Useease-outas a safe default. -
Using
ease-infor entrances. This is counterintuitive:ease-in(slow start) is better for exits because the element accelerates away. For entrances, useease-out(fast start, slow landing). -
Overshoot on functional elements.
easeOutBacklooks great on decorative animations but can cause layout issues on functional elements. An overshooting width change might temporarily overlap adjacent elements. -
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; } } -
Same easing for enter and exit. Symmetric easing (same curve for appearing and disappearing) often feels unbalanced. Entrances should decelerate; exits should accelerate.
-
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-outor 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:
transformandopacityare GPU-composited. Animate these for smooth 60fps motion.width,height,margin,paddingtrigger layout recalculations. Animating these can cause jank.color,background-colortrigger 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
- Easing Function Explorer — visualize and compare CSS easing curves with live animation previews
- CSS Gradient Generator — build CSS gradients with live preview
- Box Shadow Generator — create CSS box-shadow effects visually
- Border Radius Generator — design CSS border-radius with preview
- CSS Specificity Calculator — calculate selector specificity scores
- CSS Unit Converter — convert between CSS units (px, rem, em, vw)