A correct CSS cubic-bezier() easing is a syntactically valid declaration whose two x coordinates stay inside the closed 0–1 range, whose curve begins at progress (0, 0) and ends at (1, 1), and whose sampled output progress matches the timing you actually want at every fraction of elapsed input time. The CSS Cubic Bezier Generator enforces that contract for you: x1 and x2 are clamped to the required CSS range, y1 and y2 accept finite values between minus ten and ten, the curve is solved at every output sample using bounded Newton iterations plus a bisection fallback, and the declaration is serialized as a complete transition-timing-function statement with a trailing semicolon.
A cubic-bezier easing is defined by exactly four control coordinates: x1, y1, x2, y2. The curve is anchored at the start point (0, 0) and the end point (1, 1), and those anchors are fixed by the CSS specification rather than by the author. The four numbers you supply describe the position of two intermediate control points, P1 and P2, that pull the curve into a shape between the anchors. Every browser then evaluates the parametric formula B(t) = (1−t)3·P0 + 3(1−t)2·t·P1 + 3(1−t)·t2·P2 + t3·P3 at each moment of the animation, using t as a curve parameter (not the elapsed time), to determine how far the animated value has progressed.
Substituting t = 0 into the parametric formula gives B(0) = 1·(0,0) + 0 + 0 + 0 = (0, 0), and substituting t = 1 gives B(1) = 0 + 0 + 0 + 1·(1,1) = (1, 1). That single substitution is enough to prove the property the rest of this article leans on — no matter what control points you pick, every cubic-bezier curve the browser accepts starts at zero progress and ends at full progress.

The two validity rules the browser enforces
CSS, as defined by the W3C CSS Easing Functions Level 1 specification, has exactly two validity rules for cubic-bezier(), and the browser applies them silently. If either rule is broken, the timing function becomes invalid and the browser falls back to a default easing — usually ease — without surfacing a warning.
Rule one: x1 and x2 must lie within the closed range 0 to 1. They describe input-time positions along the horizontal axis of the easing graph, so the browser considers the entire declaration invalid the moment one of them slips outside that window. The CSS Cubic Bezier Generator prevents this by constraining the x inputs at the interface level and by independently validating the values before serializing them, so you cannot accidentally ship an invalid easing by typing the wrong number.
Rule two: y1 and y2 must be finite numbers, but they are not bounded to 0–1. The y values describe output progress and may extend below zero or above one, which is what produces anticipation and overshoot effects. The generator's interface accepts finite y values between minus ten and ten, which is wide enough to cover nearly every practical easing without letting typos slip through unnoticed.
If your goal is motion that lands cleanly on its target, keep both y values inside 0–1. If you want a control to bounce past its end point before settling, push y2 above one. If you want the value to start away from the rest position before being pulled back, push y1 below zero. Both are valid CSS; whether they feel right depends on what your real interface needs.
Generate and audit a curve with the CSS Cubic Bezier Generator
- Open the CSS Cubic Bezier Generator and pick a starting point. Either click one of the five keyword buttons (linear, ease, ease-in, ease-out, ease-in-out) to load the standardized control points, or enter your own x1, y1, x2, and y2 values directly. Choosing a keyword does not output the keyword itself; the generator exposes all four numbers so the easing can be recorded, compared, and modified without hidden defaults.
- Inspect the square chart to see the curve shape between (0, 0) and (1, 1). The chart keeps a stable 0–1 frame so ordinary timing curves can be compared visually, but a deliberately extreme y value will extend outside the visible square — that extension is still part of the curve. The animated preview and the numeric samples use the actual computed easing rather than a clipped version, so overshoot will appear in those outputs even when the chart makes the curve appear contained.
- Read the five input-to-output samples at 0, 0.25, 0.5, 0.75, and 1. These are the solver's answers to the only question that matters at runtime: given this fraction of elapsed time, how far has the animated value progressed? The samples are computed with bounded Newton iterations and a bisection fallback, so the value at input 0.5 reflects the curve's actual x = 0.5 coordinate rather than the curve parameter t = 0.5.
- Click Run to toggle the preview circle between two horizontal positions using a 900 ms transform transition with the exact generated easing string. Repeated clicks let you compare forward and reverse motion. The preview demonstrates timing only — it does not prove how the curve will feel inside your actual interface once distance, property, and duration change.
- Click Copy CSS to write the complete transition-timing-function declaration (including the trailing semicolon) to your clipboard. If your browser denies clipboard access, the declaration remains visible and selectable and the status does not claim success, so you can always copy it manually.
- Paste the declaration into your own stylesheet and test it on the real property, distance, and duration you intend to ship. If the result still does not look right, the guide on avoiding common mistakes when you generate a CSS cubic bezier walks through the most likely culprits.
Read the graph, samples, and preview like an auditor
A correct easing is not just a declaration the browser accepts — it is a declaration that produces the timing you intended at every fraction of elapsed input time. Three of the generator's outputs let you audit that contract before you commit.
The square chart answers "what shape is this curve?" It plots the parametric curve from (0, 0) to (1, 1), and the five standardized keyword presets produce easily recognizable shapes inside the chart: linear is a straight diagonal, ease bends gently toward the end, ease-in hugs the bottom, ease-out hugs the top, and ease-in-out s-curves symmetrically. If your custom control points do not produce the shape you expected, the chart is the fastest signal that something is off.
The numeric samples answer the more useful runtime question: how far has the animated value progressed when a given fraction of time has passed? For each input at 0, 0.25, 0.5, 0.75, and 1, the solver finds the curve parameter whose x coordinate matches the requested input and then evaluates y at that parameter. A common confusion is to assume the sample at 0.5 means "the value of y when the curve parameter t equals 0.5." It does not — CSS uses input progress on the x axis, and the curve parameter and input progress are only the same value when x1 = 0 and x2 = 1 (the linear case). The generator's solver accounts for this distinction by inverting the x-to-t mapping at every sample, which is what makes the table trustworthy when the control points pull the curve away from the diagonal.
The animated preview answers "does this feel right at runtime?" A 900 ms transform transition with the exact easing string applied to a moving circle is a tight approximation of what a real component will do for the same string. If the preview overshoots noticeably, snaps too sharply, or settles too slowly, the samples will already show the same behavior at 0.75 and 1 — the preview just makes it visceral. Numbers are rounded to three decimal places only when serialized for the visible declaration, so the calculation retains full JavaScript numeric precision internally and the samples do not accumulate rounding error.
The keyword mappings the generator treats as reference data
When you click one of the keyword buttons, the generator loads the control points defined by CSS Easing Functions Level 1 and exposes them as four numbers in the visible declaration, rather than outputting the keyword itself. That means every easing you build from a preset can be recorded, compared, and modified without hidden defaults, and the keyword mappings double as tested reference cases you can use to confirm the tool is solving the curve correctly.
| Keyword | x1 | y1 | x2 | y2 |
|---|---|---|---|---|
| linear | 0 | 0 | 1 | 1 |
| ease | 0.25 | 0.1 | 0.25 | 1 |
| ease-in | 0.42 | 0 | 1 | 1 |
| ease-out | 0 | 0 | 0.58 | 1 |
| ease-in-out | 0.42 | 0 | 0.58 | 1 |
These exact numbers come from the W3C specification and are documented independently on MDN's cubic-bezier() reference. If your generator output matches one of these rows when you click the corresponding keyword button, you know the solver, the serializer, and the rounding rules are all behaving as expected. Mismatches are an immediate signal to investigate before you trust any custom curve the tool produces.
Test the declaration in the real interface before shipping
A declaration that audits clean on the generator can still feel wrong in production because cubic-bezier() describes only the timing function — not the property, distance, duration, delay, or surrounding motion. The generator deliberately limits its scope to transition-timing-function so the output stays predictable and easy to audit, and does not produce keyframes, spring physics, linear() stop lists, steps(), duration, delay, iteration count, or a full transition shorthand.
When you paste the declaration into your real stylesheet, test it with the same property the animation will actually animate, at the same distance it will travel, for the same duration the user will experience. A 900 ms transform transition looks different from a 200 ms opacity transition at the same easing, and a 50 pixel slide looks different from a 400 pixel slide. Watch focus behavior while the element is transitioning, and confirm pointer events still land where users expect them to land. Large overshoots can make controls appear to reverse, leave their container, or reveal content that was expected to remain clipped, so verify the visual outcome rather than assuming the easing alone is enough.
Respect reduced-motion preferences. A technically correct easing is not automatically appropriate for every user. If animation is not essential to the interaction, give users who prefer reduced motion a fallback that skips or shortens the transition. All calculation in the generator runs locally in your browser — no curve, CSS, or interaction data is uploaded, stored, or sent to an animation service — which makes it safe to experiment with sensitive control points without sending work outside the page.
Related reading: Make Sure You Generate a CSS Toggle Switch Correctly.