Skip to content
HexSlate

Cubic Bezier Editor

Side by side
Your curve
ease, for comparison

Both runs take 1200ms and cover the same distance. Only the shape of the curve differs.

CSS
.element {
  transition: transform 600ms cubic-bezier(0.4, 0, 0.2, 1);
}
As linear(), for anything cubic-bezier cannot say
linear(0, 0.0058 5%, 0.0259 10%, 0.066 15%, 0.1339 20%, 0.2366 25%, 0.3673 30%, 0.5 35%, 0.6136 40%, 0.7043 45%, 0.7756 50%, 0.8317 55%, 0.876 60%, 0.9109 65%, 0.9382 70%, 0.9594 75%, 0.9753 80%, 0.9867 85%, 0.9944 90%, 0.9986 95%, 1)
cubic-bezier(0.4, 0, 0.2, 1)

What the four numbers are

A CSS easing curve is a cubic Bezier that always starts at (0,0) and ends at (1,1). The four numbers are the two control points in between, and the axes are not both space: the horizontal one is time and the vertical one is progress. That single fact explains both of the rules the editor enforces. The x coordinates are clamped to 0 and 1 because time cannot run backwards, and the specification rejects anything outside that range as invalid. The y coordinates are not clamped at all, because progress going past 100% and settling back is exactly how a spring, a bounce or an overshoot is written.

The dashed diagonal in the editor is linear. Anywhere the curve sits above it, the animation is ahead of a constant-speed run; below it, behind. An ease-out curve leaves the diagonal immediately and rejoins it at the end, which is why it feels like something arriving and settling.

linear() is how springs are written now

cubic-bezier() has exactly two control points, so it cannot describe a curve that reverses direction more than once. A bounce that hits the floor three times is simply not expressible, and for a decade the answer was a JavaScript animation library. linear() changed that: it takes a list of progress stops and approximates any shape with a polyline, and it is baseline in current browsers.

The second output box is this curve as linear(), sampled at twenty even points in time. For a plain cubic curve it is the longer way to say the same thing, so use cubic-bezier(). Its value is as a starting point: paste it, then add or move stops to build the multi-bounce or multi-stage easing the two-control-point form could never reach. Almost no free generator emits it.

Common problems

  • The overshoot does not appear. The property clamps its own range. Opacity cannot go above 1 and most colour channels cannot go outside their gamut, so the overshoot is silently flattened. Overshoot on transform, which has no bounds.
  • The animation is invalid and nothing moves. An x outside 0 to 1 makes the whole cubic-bezier() invalid, and an invalid timing function takes the declaration with it. The editor cannot produce one; hand-editing can.
  • Ease-in feels wrong on things entering the screen. It usually is. Ease-out for things arriving, ease-in for things leaving, ease-in-out for things moving between two on-screen positions. An element that accelerates away as it appears reads as hesitant.
  • It looks fine at 300ms and awful at 1200ms. Easing and duration are one decision. A pronounced curve needs a short duration or the slow section becomes a stall, which is why the demo above fixes the duration and lets only the curve vary.

Frequently asked questions

What curve should I use by default?

cubic-bezier(0.4, 0, 0.2, 1), the Material standard curve, is a good default for interface motion: it leaves quickly and settles slowly, which reads as responsive without looking sudden. The CSS ease keyword is close but front-loads less, and ease-in-out is symmetric, which is why symmetric motion often feels mechanical.

Why does the editor let me drag outside the box?

Because that region is legal and useful. The pad shows progress from -0.4 to 1.4 and draws the 0-to-1 box behind the curve, so an overshoot is visible rather than clipped. Only the horizontal axis stops at the edges, and that is the spec's own bound.

Can I use this without a mouse?

Yes. Both handles are real buttons: tab to one and move it with the arrow keys, holding shift for a ten times larger step. The four number boxes accept values the handles cannot reach, which matters for a large overshoot.

What about steps() and jump terms?

steps(n, jump-start | jump-end | jump-both | jump-none) is a different family entirely: it quantises progress instead of smoothing it, which is how sprite-sheet animations and typewriter effects are built. It is not expressible as a Bezier, and it is not what this editor produces.