Bézier curves from first principles
Every glyph in this sentence, every icon in your toolbar and the way this page's animations speed up and slow down all come from one small idea, applied twice. I finally sat down to understand it properly. These are my notes, built up from a straight line, with demos you can drag.
A bit of history first, because I enjoyed it. In 1962 Pierre Bézier was an engineer at Renault with a problem: car bodies were designed as physical clay models, and there was no good way to describe their surfaces to the milling machines that had to cut them. The curves he popularised were actually worked out a few years earlier by Paul de Casteljau at Citroën, who wasn't allowed to publish. The idea in both places was the same. Pin a curve down with a handful of points, and it can be reproduced exactly, anywhere, at any scale.
Sixty years later the same family of curves is the backbone of computer graphics. Fonts, SVG icons, Illustrator's pen tool, CSS animation easing, robot motion planning. I knew the name for years without knowing how they actually work, and it turns out the construction is genuinely simple. The only prerequisite is knowing how to walk along a straight line.
Start with a line
Take two points, A and B, and a
number t between 0 and 1. Linear interpolation
(lerp, to its friends) gives you the point that is a fraction
t of the way from A to B:
lerp(A, B, t) = (1 − t) · A + t · B
At t = 0 you're standing on A, at t = 1
you're on B, at t = 0.5 you're exactly halfway. Sweep
t from 0 to 1 and you trace the whole segment. That's
the entire toolbox. A straight line traced this way is technically
already a Bézier curve, a degree-1 one with control points A and B.
Everything that follows is just lerp applied to the results of other
lerps, which honestly surprised me when it clicked.
De Casteljau's algorithm
Now take three points: P₀, P₁ and P₂. For a given
t, do this:
- Lerp from P₀ to P₁, call the result Q₀.
- Lerp from P₁ to P₂ with the same t, call it Q₁.
- Lerp from Q₀ to Q₁. That point is on the curve.
Sweep t from 0 to 1 and that final point sweeps out a
smooth arc: a quadratic Bézier curve. The recursive
walk is called De Casteljau's algorithm. Try it below. Drag the
points, scrub t, and watch the two-stage construction
happen:
The thing that made this click for me is what the amber line is
doing: at every t it is tangent to the curve.
The construction doesn't just give you points, it gives you
directions for free. That's why the same machinery can drive font
rendering and also steer a CNC cutter along a path.
One more level: the cubic
Nothing stops you at three points. With four control points you lerp three times to get three points, lerp those to get two, and lerp once more to land on the curve. That's a cubic Bézier, and it's the workhorse of the whole field. It's the cheapest curve that can hold an inflection (an S-bend), and you get independent control over the direction the curve leaves each endpoint.
Unrolling the recursion gives the closed form. For the cubic, collect
the terms and each control point ends up weighted by a polynomial in
t:
B(t) = (1−t)³ P₀ + 3(1−t)²t P₁ + 3(1−t)t² P₂ + t³ P₃
Those weights, (1−t)³, 3(1−t)²t, 3(1−t)t², t³, are the
Bernstein basis polynomials, and two things about
them are worth staring at. They are the terms of the binomial
expansion of ((1−t) + t)³, so at every t
they sum to exactly 1: the curve point is always a weighted average
of the control points. And each weight peaks at a different
t, which is the precise sense in which P₁ "pulls" the
early part of the curve and P₂ the late part. The curve passes
through its endpoints (B(0) = P₀,
B(1) = P₃) but only approaches the middle
control points. It never touches them.
The derivative falls straight out of the algebra, and it's another Bézier curve, one degree lower:
B′(t) = 3(1−t)² (P₁−P₀) + 6(1−t)t (P₂−P₁) + 3t² (P₃−P₂)
At t = 0 that collapses to 3(P₁−P₀): the
curve leaves P₀ aimed directly at P₁. So a pen tool's "handles" are a
literal picture of the derivative at each anchor. I'd been dragging
them for years without knowing that.
Why these curves won
Plenty of curve families existed before Bézier's. As far as I can tell, this one took over because a short list of properties happens to be exactly what designers, font engineers and renderers need:
- The control points mean something. Endpoints are on the curve; handles set the tangents. You can hand the four points to a person with a mouse and they can steer the curve by feel. Try explaining a polynomial's coefficients to anyone.
- Convex hull. Because the Bernstein weights are non-negative and sum to 1, the curve can never escape the convex hull of its control points. A renderer can trivially bound a curve, cull it, or test it for intersection without evaluating anything.
- Affine invariance. Rotate, scale or skew the control points and the curve transforms with them, exactly. You transform 4 points instead of 200 sampled ones, which is why a font scales from 8px to a billboard with no loss.
- Variation diminishing. The curve wiggles no more than its control polygon does: a line crosses the curve at most as many times as it crosses the polygon. No surprise oscillations, which is what makes the curves feel predictable under the cursor.
- Cheap to evaluate, cheap to split. A handful of lerps per point, in fixed or floating point, no trigonometry anywhere.
Where you've already used them today
Fonts. A glyph outline is a loop of Bézier segments.
TrueType (.ttf) uses quadratics, picked in the late '80s
because they're cheaper to rasterise, while PostScript and
CFF/OpenType outlines use cubics. When this page loaded, your browser
took the curve descriptions out of three font files and flattened
several thousand of them into the pixels you're reading right now.
Vector graphics. SVG's path syntax exposes the
machinery directly: Q is a quadratic segment,
C a cubic. These two paths are figures 1 and 2 of this
page, control point for control point:
<!-- one quadratic, one cubic: figures 1 and 2 -->
<path d="M 70 300 Q 320 60 570 300" />
<path d="M 70 300 C 180 70 460 70 570 300" /> Every pen tool, whether it's in Illustrator, Figma, Inkscape or Glyphs, is an editor for exactly these commands. When you drag a handle and the curve follows, you are moving P₁ in figure 2.
Animation easing. CSS's
cubic-bezier(x1, y1, x2, y2) is a cubic in disguise, with
a twist: the axes aren't space, they're time (x) against
progress (y). P₀ is pinned at (0,0) and P₃ at (1,1); you only
get to place the middle two points. The browser then answers "at 40%
of the duration, how far along is the animation?" by intersecting the
curve with a vertical line. That's why the spec clamps x₁ and x₂ to
[0,1]: time isn't allowed to flow backwards. The y values are
unclamped, and pushing them outside [0,1] is how you get overshoot and
bounce.
Everything that moves along a path. Camera rails in games, GPS route smoothing, the toolpath of a 3D printer, PowerPoint's motion paths, robot arm trajectories. Any time something needs to get from A to B without a visible kink, there's a good chance a cubic Bézier is doing the steering.
How a curve becomes pixels
A GPU rasteriser wants straight lines, so at some point every Bézier
on your screen gets flattened into a polyline. The naive way
is to sample a fixed number of t values, but that wastes
segments on the straight-ish parts and starves the tight bends. The
standard answer uses a property of De Casteljau's construction that I
did not see coming: the intermediate points from figure 2 don't just
locate one point, they split the curve into two smaller
Béziers whose control points hug the curve more tightly than
the original four.
So the renderer recurses: if a curve's control points are within
tolerance of a straight line, emit a segment; otherwise split at
t = ½ and repeat. Flat regions terminate early, bends
subdivide deeper, and the error is bounded everywhere:
What they can't do
Two honest limitations, and what the field does about them.
No polynomial curve is a circle. A cubic can only
approximate an arc. The classic trick pins a cubic to a quarter
circle and asks how long the handles should be; the answer is
k ≈ 0.5523 · r, a number quietly hard-coded into nearly
every graphics library that offers rounded corners. The error over a
quarter circle is about 0.02% of the radius. Invisible at any screen
size, which is why nobody minds.
One curve doesn't scale to complex shapes. You could raise the degree to ten or eleven control points, but high-degree Béziers are miserable: moving any single point bends the entire curve, and the shape drifts ever further from its control polygon. So practice goes the other way: keep the pieces cubic and chain them. As long as each joint's incoming and outgoing handles are collinear, the seam is invisible. That is exactly what a pen tool's "smooth point" enforces, and why breaking the handles gives you a corner. A chain of Béziers is a spline. Add per-point weights so arcs come out exact and you get NURBS, the standard in CAD, and a direct descendant of what Bézier and de Casteljau built for car bodies.
Further reading
Things I leaned on while figuring this out:
- A Primer on Bézier Curves, Pomax's free book. The reference for everything this post skipped (arc length, offsetting, intersections).
- The Beauty of Bézier Curves, Freya Holmér's 24-minute video. The best visual treatment of splines and continuity I've found anywhere.
- Raph Levien's curve notes, for when the practical questions (arc length, flattening error bounds) get serious.