Skip to content
ColorKit

Free easing curve editor

Cubic bezier generator with four curves in a race

This cubic bezier generator is free and needs no signup: pull the two orange handles, and the indigo path between them is the easing curve, printed underneath as a cubic-bezier() value you can copy. Because a curve on its own is unreadable, the same motion runs on four synchronised lanes — yours against linear, ease and ease-in-out — and a table prints the progress your curve has made at every tenth of the duration. Handles reach y = -0.5 and y = 1.5, so overshoot and anticipation are in range, and eleven published curves load with one click.

  • 100% free
  • No signup
  • 4 lanes in sync
  • y from -0.5 to 1.5
  • 11 preset curves

Horizontal is elapsed time, vertical is how much of the change has happened. The grey band above and below the square is the region CSS allows for y and forbids for x.

Press Ctrl+V anywhere on the page to drop in a cubic-bezier() value copied out of a stylesheet — no need to click the field first.

cubic-bezier(0.34, 1.56, 0.64, 1)

This curve overshoots: progress passes 100% and comes back, which reads as weight on scale and translate and as a flicker on opacity.

Your curve0.0%
linear0.0%
ease0.0%
ease-in-out0.0%

Progress at each tenth of the duration

Time0102030405060708090100
Progress0407091103109110108104101100

Half the distance is covered 13% of the way through the run. A curve that reaches 50% before the halfway mark front-loads the motion; one that reaches it after saves the movement for the end.

Start from a known curve

How to build a cubic-bezier easing curve

Three moves between an empty curve and a value your stylesheet can use.

  1. Grab a handle and pull

    The orange dot near the origin is the first control point and the one near the top right is the second. Dragging left and right changes how much of the duration that half of the curve spends; dragging up and down changes how much distance it covers. Focus a handle with Tab and the arrow keys move it 0.01 at a time, Shift plus an arrow 0.1, which is how you land on a round number like 0.42 instead of 0.418.

  2. Run the race before you trust the shape

    A curve drawn on its own tells you very little; the same curve next to three you already know tells you everything. Press Run the race and four dots leave the gate on the same animation frame — yours in indigo, then linear, ease and ease-in-out in grey. Stretch the duration slider out to 4,000ms when two lanes look identical: differences that vanish at 300ms are obvious at four seconds.

  3. Copy the value, or the whole declaration

    Copy value puts cubic-bezier(0.34, 1.56, 0.64, 1) on the clipboard for pasting into a design token or a JavaScript animation config; Copy declaration wraps it as animation-timing-function for a stylesheet. Coming the other way works too — press ⌘V or Ctrl+V anywhere on the page with a cubic-bezier() copied out of DevTools and the handles jump to it.

Technical specifications

Handle rangex is clamped to 0-1 because the specification requires the time axis to advance; y runs from -0.5 to 1.5, which covers every overshoot curve published in a mainstream design system
Keyboard controlTab focuses a control point, an arrow key moves it 0.01, Shift plus an arrow moves it 0.1
Curve inversionNewton-Raphson, 8 passes, 1e-7 tolerance, falling back to 40 bisection steps where the derivative flattens — the same problem a browser solves on every frame
Preset curves11 — the five CSS keywords, Material's standard, decelerate and accelerate curves, Expo out, Back out and Anticipate
Race lanes4, started on one requestAnimationFrame tick so the comparison is not a frame out: your curve against linear, ease and ease-in-out
ReadoutProgress at each tenth of the duration, plus the point in the run at which half the distance has been covered
Playback200ms to 4,000ms per run, with a 500ms pause before the next one starts
Outputcubic-bezier() rounded to three decimals, as a bare value or as an animation-timing-function declaration — free, with no account, and solved in the tab rather than on a server

Frequently asked questions

Why will the handles not go past the left and right edges?

Because a cubic-bezier() whose x ordinates fall outside 0 to 1 is invalid CSS and the browser throws the whole declaration away. The x axis is elapsed time, and time has to advance: a control point at x = 1.4 would let the curve fold back on itself, which would mean the animation is at two different points of its progress at the same instant. The y ordinates carry no such rule, which is why the editor lets them out of the square in the other direction.

What happens when the curve climbs above 1 or dips below 0?

The property overshoots its target and comes back, which is where the sense of weight in a good entrance animation comes from. It is safe on transform and on custom properties driving a transform, and it is a mistake on anything that clamps: opacity above 1 is simply 1, so the extra travel is dead time in which nothing moves, and a color channel pushed past its range clips rather than bounces.

Is cubic-bezier(0.25, 0.1, 0.25, 1) exactly the same as ease?

Yes, byte for byte in effect — the CSS specification defines ease as that curve, and the preset button here will tell you when your handles land on it. The other four keywords are the same story: linear is (0, 0, 1, 1), ease-in is (0.42, 0, 1, 1), ease-out is (0, 0, 0.58, 1) and ease-in-out is (0.42, 0, 0.58, 1). Where the keyword exists, write the keyword; it compresses better and it survives a colleague reformatting the file.

Why does my curve look wrong at very short durations?

There are not enough frames to draw it. A 100ms animation on a 60Hz display gets six samples, and six points cannot describe an acceleration change — the eye reads it as a straight ramp no matter which curve you chose. Under roughly 150ms the choice of easing stops being visible and only the duration matters; above 400ms the curve is doing most of the work.

Can a cubic-bezier make a spring or a bounce?

No, and this is the honest limit of the function. Two control points give the curve exactly one change of direction, so it can overshoot once and settle — a spring that oscillates three times needs three, and a bounce needs a hard stop at the floor each time. Those want either a keyframes block with the stops written out or the linear() function from CSS Easing Level 2, which takes an arbitrary list of points and is supported in Chrome 113, Safari 17.2 and Firefox 112.

How is this different from steps()?

steps() is a discrete timing function and cubic-bezier() is a continuous one. A cubic curve produces a new value on every frame; steps(6, end) produces six values and holds each of them for a sixth of the duration, which is what a sprite animation or a ticking counter needs and what a moving panel definitely does not. They are alternatives in the same slot in the grammar, not variations on a theme.

Does the same value work for a transition and for an animation?

Yes — transition-timing-function and animation-timing-function accept identical syntax — but they apply over different spans. A transition runs the curve once, from the old value to the new one. An animation applies the curve to each interval between keyframes separately, so the same cubic-bezier on a five-stop block runs four times, easing in and out at every stop and producing a visible hesitation nobody asked for.

About cubic-bézier timing functions

A CSS timing function is not a graph of position over time in the way it first looks. It is a parametric Bézier curve with its two endpoints nailed to (0, 0) and (1, 1), where the horizontal axis is the fraction of the duration that has elapsed and the vertical axis is the fraction of the change that has happened. Nothing about that construction gives the browser y directly: it has a time, and it needs the t parameter that produces that time before it can evaluate y. So on every frame the engine solves x(t) = elapsed numerically — Newton-Raphson, with bisection when the derivative goes flat — and that requirement is exactly why the x ordinates are locked to 0 through 1. A time axis that doubled back would have no single solution.

The five keywords are curves too, and knowing which ones saves a lot of invention. The surprise is usually ease: people assume it is symmetric and it is (0.25, 0.1, 0.25, 1), which accelerates fast, coasts, and lands slowly — try it against ease-in-out in the race above and the difference in the last third is unmistakable. Design systems long ago stopped using them raw; Material's standard curve is (0.4, 0, 0.2, 1) and its whole character is the very steep exit. Once you have a value, it goes either into the shorthand on the CSS animation generator or, per interval, into the block you assemble on the keyframes generator — and it is worth knowing that in the second case one curve is applied to every gap between stops, not once across the whole run.

Two things most curve editors leave out. The first is the comparison: a curve shown alone looks plausible whatever its shape, and the only way to judge one is to run it beside a curve you have already internalised, which is why there are four lanes here rather than one. The second is the limit — two control points buy you a single change of direction, so a curve can overshoot once and settle but cannot oscillate, and every attempt to fake a spring by pushing y to 2.5 produces a curve that looks broken at 300ms. Overshoot also only earns its keep on properties that can exceed their target, which in practice means the ones you would build on the CSS transform generator. And whatever curve you settle on, it should be wrapped in a reduced-motion query before it ships — the media query generator writes that one for you.

Where the curve is computed

Every number on this page is worked out by JavaScript running in the tab you are reading it in. Nothing you type, paste or open is uploaded, logged or kept, which is also why the tools carry on working after you disconnect from the network.

The four numbers are also not written to storage of any kind, which has one practical consequence worth knowing before you close the tab: a reload puts the editor back on the Back out preset and your curve is gone. Copy the value out first.