What is cubic-bezier()?
The CSS cubic-bezier() function defines an easing curve for transitions and animations. It takes four parameters — x1, y1, x2, y2 — representing two control points of a cubic Bezier curve. The curve starts at (0, 0) and ends at (1, 1), where the x-axis represents time and the y-axis represents the animation progress. By adjusting the control points, you can create effects like overshoot (values above 1), slow starts, or bouncy endings.
Every CSS transition uses some form of easing. When you write transition: opacity 0.3s ease, the browser is actually applying cubic-bezier(0.25, 0.1, 0.25, 1.0) behind the scenes. The five built-in keywords — linear, ease, ease-in, ease-out, and ease-in-out — are just aliases for predefined cubic-bezier values. By writing your own cubic-bezier() function, you gain precise control over how an animation accelerates and decelerates, which is essential for crafting motion that feels intentional rather than generic.
The mathematics behind Bezier curves come from computer graphics, where they are used to define smooth parametric paths. In CSS, the curve is constrained so that the x-axis always moves forward in time (which is why x1 and x2 must be between 0 and 1), but the y-axis — representing output progress — can exceed those bounds. This means an animation can temporarily overshoot its final state before settling, creating a natural, spring-like feel that is difficult to achieve with simple linear interpolation.
How to use this tool
Drag the two control points on the graph or enter exact values in the number fields. The green control point (P2) determines the curve's ending behavior, while the purple control point (P1) controls the starting behavior. Select a preset from the dropdown to start from a known curve and modify it from there. Click "Play Animation" to see how a ball moves along the curve in real time. Adjust the duration slider to test different speeds and get a feel for how the curve performs at various animation lengths.
Copy the output in either direct CSS or CSS variable format using the copy buttons below the preview. The direct CSS output gives you a ready-to-paste transition-timing-function declaration, while the CSS variable format wraps the values in custom properties so you can manage your easing tokens from a single location. Use the "Copy Share URL" button to encode the current curve into the page URL, making it easy to share your custom easing with teammates or save it for later.
Practical examples
Button hover transition with overshoot effect
A subtle overshoot on button hover gives interactive elements a lively, tactile feel. The y2 value exceeding 1 makes the scale briefly go past the target before settling back, creating a micro-bounce that draws attention without being distracting.
.btn {
transform: scale(1);
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.btn:hover {
transform: scale(1.05);
}
Modal entrance animation (scale + fade)
When a modal dialog enters the screen, combining a scale transform with an opacity fade creates a polished entrance. Using an ease-out style curve — fast at the start, decelerating toward the end — makes the modal feel like it is arriving from somewhere and coming to rest naturally.
.modal-overlay {
opacity: 0;
visibility: hidden;
transition: opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1),
visibility 0.25s;
}
.modal-content {
transform: scale(0.95) translateY(10px);
opacity: 0;
transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1),
opacity 0.25s cubic-bezier(0.16, 1, 0.3, 1);
}
.modal-overlay.open {
opacity: 1;
visibility: visible;
}
.modal-overlay.open .modal-content {
transform: scale(1) translateY(0);
opacity: 1;
}
Dropdown menu slide-down with deceleration
Dropdown menus benefit from a curve that starts fast and slows down toward the end. This gives the impression of the menu quickly responding to the user's click and then gently easing into its final position, rather than abruptly stopping.
.dropdown-menu {
max-height: 0;
overflow: hidden;
opacity: 0;
transform: translateY(-8px);
transition: max-height 0.4s cubic-bezier(0.22, 1, 0.36, 1),
opacity 0.3s cubic-bezier(0.22, 1, 0.36, 1),
transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.dropdown.open .dropdown-menu {
max-height: 400px;
opacity: 1;
transform: translateY(0);
}
Common patterns and best practices
When designing custom easing curves, keep these guidelines in mind to create animations that feel both polished and performant.
- Match the curve to the interaction: Use ease-out curves (fast start, slow end) for elements entering the viewport, and ease-in curves (slow start, fast end) for elements exiting. This mirrors how physical objects behave — they accelerate when pushed and decelerate due to friction.
- Keep overshoot subtle: Y-values above 1.0 or below 0 create overshooting effects. Values between 1.0 and 1.3 produce a gentle bounce; values above 1.5 can feel cartoonish unless that is the intended style.
- Use consistent easing across your design system: Define two or three custom cubic-bezier curves as CSS custom properties and reuse them throughout your project. This creates a cohesive motion language rather than a collection of one-off animations.
- Test at different durations: A curve that looks smooth at 500ms may feel sluggish at 1000ms or jerky at 150ms. Always test your curve across the range of durations you plan to use.
- Animate only transform and opacity: Regardless of the easing curve, animating properties like width, height, margin, or top forces the browser to recalculate layout on every frame. Stick to transform and opacity for 60fps animations.
- Consider prefers-reduced-motion: Some users enable reduced motion settings for accessibility reasons. Wrap your animated transitions in a media query that checks for
prefers-reduced-motion: no-preference, or simplify the animation to an instant state change when reduced motion is preferred.
Browser Support
- Chrome / Edge — full support since the early Chromium era; current evergreen versions all behave identically.
- Firefox — supported since version 4 (2011); no version-specific issues remain.
- Safari — supported since iOS 4 / Safari 5; the only modern wrinkle is
linear()(the new multi-stop easing function), which Safari shipped in 17.4. Purecubic-bezier()has no Safari caveats. - Spec status — the function is part of the CSS Easing Functions Module Level 1, which is a W3C Candidate Recommendation in widespread use.
For developers working with newer CSS features, cubic-bezier() works seamlessly with the Web Animations API, the animation shorthand, individual transition properties, and the View Transitions API for page-level transitions. It remains the most universally supported easing method in CSS.
Common Pitfalls
- Don't bake overshoot into every interaction. A bouncy curve on a button hover is fine. The same curve on a modal entrance feels unprofessional. Reserve y-values above 1.0 for interactions where the playfulness is intentional.
- Avoid copying random curves from Stack Overflow. Most "smooth" curves people share are slight variations of
ease-out. Design two or three curves for your design system and use them consistently rather than making each animation a snowflake. - Don't use cubic-bezier on layout-triggering properties. The curve is fine, but animating
width,height, ortoprecalculates layout every frame regardless of how elegant the easing is. Animatetransformandopacity. - Test at the duration you'll actually ship. A curve that looks beautiful in a 2-second demo can feel jerky at 200ms. Always preview at the duration users will see.
- Don't ignore prefers-reduced-motion. The most polished cubic-bezier in the world is the wrong choice for users who've explicitly asked for less motion. Wrap non-essential motion in
@media (prefers-reduced-motion: no-preference).
FAQ
What's the difference between cubic-bezier and linear/ease?
Keywords like 'ease', 'linear', and 'ease-in-out' are shorthand for specific cubic-bezier values. For example, 'ease' is equivalent to cubic-bezier(0.25, 0.1, 0.25, 1.0). Using cubic-bezier() directly gives you full control to design a custom curve that isn't limited to these five presets.
Can I use values outside the 0-1 range?
The x-values (x1, x2) must stay between 0 and 1 because they represent time progression. However, the y-values (y1, y2) can go above 1 or below 0 — this creates an overshoot or anticipation effect where the animation temporarily exceeds its target value before settling.
How does cubic-bezier affect animation performance?
The cubic-bezier curve itself has no impact on rendering performance. The curve is pre-calculated by the browser before the animation starts, so changing the curve complexity does not add per-frame overhead. The performance cost comes from what you animate: transform and opacity are handled by the GPU compositor and are cheapest, while properties that trigger layout recalculation (width, height, margin) are expensive regardless of the easing function used.
What is the difference between cubic-bezier and steps()?
cubic-bezier() creates smooth, continuous curves where the animation progresses fluidly from start to finish. In contrast, steps() creates discrete frame jumps, dividing the animation into a fixed number of equal intervals with no interpolation between them. steps() is particularly useful for sprite sheet animations where you want to jump between frames, or for creating a typewriter effect. You can combine both in a single animation by using different timing functions on different keyframe segments.
How do I create a bounce effect?
Use y-values above 1 to make the animation overshoot its target before settling back. For example, cubic-bezier(0.34, 1.56, 0.64, 1) produces a noticeable bounce at the end of the animation. The higher the y-value, the more pronounced the overshoot. For a more complex multi-bounce effect, you would need to use CSS @keyframes with multiple percentage stops rather than a single cubic-bezier curve, since cubic-bezier can only produce one overshoot peak.
Can I use cubic-bezier with CSS custom properties?
You cannot store a cubic-bezier() function itself inside a CSS custom property and then use it as a timing function value, because the timing function must be a literal value that the parser can evaluate at declaration time. However, you can use custom properties for duration, delay, and other animation properties alongside a literal cubic-bezier() timing function. Some developers define their easing values as custom properties in a comment block or documentation for reference, then use the literal values in their actual transition declarations.
Related guides
- All CSS Guides — in-depth tutorials on modern CSS
- View Transitions Guide — easing functions are the secret to good page transitions
- 10 Modern CSS Features — broader context for where cubic-bezier fits