How does a computer turn a few points into a smooth curve?
A designer drags four handles. A movie character gets a smooth cheek, a car gets a perfect hood, a letter gets its curve. The handles are not on the curve; they pull at it like magnets. Move one handle and only the part of the curve nearby changes — the rest sits still. One operation, applied recursively, is doing all of this. That operation is linear interpolation between two points.
Drag any handle in the widget below. Slide t across the bottom and watch the construction collapse layer by layer onto a single point — that point traces the curve.
Why not just connect the dots?
Imagine asking a computer for “a smooth curve passing through these five points.” A first attempt: fit a single polynomial. It works for two or three points; for five or more it oscillates wildly between them (the Runge phenomenon). Splines fix this by stitching local pieces together — but each piece still has to be defined. Bezier’s idea is different: don’t ask for a curve through the points; ask for one shaped by them. The points become handles, not anchors.
The two-point case — lerp
Between two points and , the linear interpolation
# A point is a 2-tuple. Lerp is one line.
def lerp(a, b, t):
return (a[0] + (b[0] - a[0]) * t,
a[1] + (b[1] - a[1]) * t)
lerp((0, 0), (4, 2), 0.0) # → (0, 0)
lerp((0, 0), (4, 2), 0.5) # → (2, 1)
lerp((0, 0), (4, 2), 1.0) # → (4, 2)Recursive lerp — De Casteljau
With four
In the widget, each layer is drawn in its own color: the dashed gray polygon connects the original controls, orange the level-1 lerps, brown the level-2 lerps, and the final green dot is . Sweep across the slider — the construction collapses, the green dot moves, and its trace is the curve. The curve is not drawn by a separate formula; it is the locus of recursion.
# De Casteljau: lerp every adjacent pair, then again, until 1 point remains.
def bezier(controls, t):
pts = list(controls)
while len(pts) > 1:
pts = [lerp(pts[i], pts[i+1], t) for i in range(len(pts) - 1)]
return pts[0]
# Cubic Bezier — four control points
P = [(0, 0), (1, 2), (3, 2), (4, 0)]
bezier(P, 0.0) # → (0, 0) (= P[0], starts at first)
bezier(P, 1.0) # → (4, 0) (= P[-1], ends at last)
bezier(P, 0.5) # → (2.0, 1.5) (midpoint by recursion)What you can read off the picture
Four facts fall out without proof, just from staring at the construction.
- Endpoints. At every lerp returns the left point, so . Symmetrically . The curve passes through the first and last control points.
- Tangents at endpoints. The level-1 lerps near sit on segments ; the curve emerges along that direction. So the curve leaves heading toward , and arrives at along . Designers use this to “make two curves meet smoothly”: align their end-handles.
- Convex hull. Every layer of lerps is a convex combination of the previous layer; the final point is a convex combination of the controls. So the curve lives inside the polygon’s convex hull and never escapes — useful for collision bounds and clipping.
- The control polygon is not the curve. It bounds the curve, points along it, can be moved to drag the curve — but the curve sits inside the polygon, smoothed away from its corners.
# Bernstein form — algebraically equivalent to De Casteljau.
def bezier_bernstein(P, t):
s = 1 - t
bx = (s**3 * P[0][0] + 3*s*s*t * P[1][0]
+ 3*s*t*t * P[2][0] + t**3 * P[3][0])
by = (s**3 * P[0][1] + 3*s*s*t * P[1][1]
+ 3*s*t*t * P[2][1] + t**3 * P[3][1])
return (bx, by)
bezier_bernstein(P, 0.5) # → (2.0, 1.5) same answer, different bookkeeping
#
# B'(0) = 3(P[1] - P[0]) → tangent at start points along P0→P1
# B'(1) = 3(P[3] - P[2]) → tangent at end points along P2→P3
# A designer reads "the curve leans into the next handle" off these two facts.Why every graphics stack ships this
The properties above are exactly what a tool needs. Local control — moving one handle changes only the nearby curve. Affine invariance — transform the controls, and the curve transforms the same way (rotate, scale, translate without re-evaluating the formula). Numerical stability — De Casteljau is just lerps, no high-degree polynomial cancellation. Composable — splice many cubic Beziers end-to-end with matching tangents and you get B-splines, the workhorse of CAD and animation.
Concretely: TrueType fonts use quadratic Beziers; PostScript and most modern fonts use cubic; SVG path data is a Bezier syntax with shorthand; Figma, Illustrator, Inkscape — all the same recursion at the bottom; Pixar’s smooth animation curves and the “ease-in-out” timing function in CSS are cubic Beziers picked by hand. One algorithm, four handles, an entire industry of curves.
The deeper bridge: Bezier curves consume the parametric-curves module. The image of the curve is the smooth path; the parametrization is . Designers usually only see the image; the parametrization is what the renderer steps through to draw it.
Bezier’s “smooth” is a local claim, not a global one. A cubic Bezier whose control polygon makes a sharp loop will produce a self-intersecting curve — still smooth in the differential sense (B’(t) is continuous), but visually pathological. Try the widget with : a self-crossing Z. Designers’ “smooth” and differential geometry’s “smooth” diverge here, and the four-handle abstraction does nothing to warn you.
Bezier is lerp, recursively. One operation between two points, applied to adjacent pairs, then to the new pairs, then again. Move a handle and the lerps follow; the curve follows the lerps.
Compute . Then . Sketch both points between the endpoints.
For the cubic Bezier with controls , compute by De Casteljau. Show every layer.
Why does the cubic Bezier curve start in the direction of and end in the direction of ? Argue geometrically from De Casteljau, then confirm by computing from the polynomial form.
The control polygon and the Bezier curve have the same first and last point and roughly the same shape. List three ways they differ. Use the widget to construct an example where the differences are obvious.
Computer-graphics texts usually present Bezier curves through their Bernstein basis formula — — a polynomial sum the reader has no reason to trust. Lemma starts at the other end: lerp, the one operation a designer already knows. Three lerps, recursively, derive the Bernstein form. Industry uses the formula; understanding starts at the recursion. The Bernstein basis is a consequence, not a starting point.