The idea: a curve made of nothing but lerps

A Bézier curve is drawn from a few control points P0, P1, … Pn. It starts at P0, ends at Pn and bends toward the points in between without passing through them. Every point of the curve comes from one operation, linear interpolation: lerp(P, Q, t) = (1 − t)·P + t·Q, the point that is a fraction t of the way from P to Q. de Casteljau's algorithm applies it over and over.

Cubic Bézier curve with control points P0 (10, 10), P1 (30, 90), P2 (100, 95), P3 (120, 15). At t = 0.3 the three blue points Q0, Q1, Q2 sit 30% along each leg, the two orange points R0, R1 sit 30% along the legs between them, and the red point B(0.3) = (38.8, 61.5) lies on the curve
At t = 0.3: three lerps on the control polygon, two on the blue legs, one on the orange leg, and the last point is on the curve.

Level by level

Fix a value of t between 0 and 1. Lerp every leg of the control polygon at that t: a cubic has 3 legs, so you get 3 new points (Q0, Q1, Q2). They form a polygon with one leg fewer. Lerp its legs at the same t (R0, R1), and again, until one point is left. That point is B(t). A curve of degree n needs n levels and n(n+1)/2 lerps.

CurveControl pointsLevelsAt t = 0.3
LinearP0 (10, 10), P1 (110, 80)1B = (40, 31)
QuadraticP0 (10, 10), P1 (60, 95), P2 (120, 20)2B = (40.9, 46.6)
CubicP0 (10, 10), P1 (30, 90), P2 (100, 95), P3 (120, 15)3B = (38.8, 61.48)

A linear Bézier curve is just the segment from P0 to P1 (Demo: linear lerp at t = 0.3). A quadratic is a lerp of two lerps (Demo: quadratic at t = 0.3), and a cubic adds one more level (Demo: cubic at t = 0.3). The panel on the right lists every lerp with its coordinates.

Sweeping t traces the curve

Run de Casteljau again for t = 0, 0.05, 0.1, … 1 and keep only the last point each time. The whole scaffold slides along the legs and the last point draws the curve, one bead per value of t (Demo: trace the cubic, then its hull). At t = 0 every lerp sits on the first point of its leg, so the curve starts at P0; at t = 1 it ends at Pn. The curve leaves P0 in the direction of P1 and arrives at Pn from the direction of Pn−1, which is why the inner points act as handles in drawing programs.

Bernstein weights

Expanding the lerps gives one formula: B(t) = Σ bi,n(t)·Pi, with the Bernstein polynomials bi,n(t) = C(n, i)·ti·(1 − t)n−i. For a cubic they are (1−t)³, 3t(1−t)², 3t²(1−t) and t³. At t = 0.3 they are 0.343, 0.441, 0.189 and 0.027, and 0.343·P0 + 0.441·P1 + 0.189·P2 + 0.027·P3 is the same (38.8, 61.48) that the lerps gave. The bars on the right show the weights for the current t.

The four cubic Bernstein polynomials on t from 0 to 1: (1-t) cubed falls from 1 to 0, t cubed rises from 0 to 1, 3t(1-t) squared and 3t squared (1-t) are humps peaking at 4/9 at t = 1/3 and t = 2/3. At t = 0.3 a dashed line reads 0.343, 0.441, 0.189 and 0.027, which add up to 1
Each weight is never negative, and at every t the four weights add up to exactly 1.

de Casteljau is the way to compute the curve in practice: it uses only additions and multiplications by t and 1 − t, so it stays accurate, while the expanded power form loses precision for high degrees.

The convex hull property

Because the weights are ≥ 0 and sum to 1, every point B(t) is a convex combination (a weighted average) of the control points. So the whole curve lies inside the convex hull of its control points, the smallest convex polygon around them (Convex hull button). Graphics code uses this as a cheap test: if two hulls do not overlap, the two curves cannot cross, and if the hull is off screen, the curve is too. The same reasoning gives affine invariance: to rotate, scale or move a curve, transform its control points and redraw.

Splitting at t

One de Casteljau run at t gives more than a point. The first point of every level (P0, Q0, R0, B(t)) is the control polygon of the curve from 0 to t, and the last point of every level (B(t), R1, Q2, P3) is the control polygon of the curve from t to 1 (Demo: split the cubic at t = 0.5). At t = 0.5 the left half is P0 (10, 10), Q0 (20, 50), R0 (42.5, 71.25), B (65, 72.5), and the right half is B (65, 72.5), R1 (87.5, 73.75), Q2 (110, 55), P3 (120, 15).

Before: the cubic with its grey control polygon P0 P1 P2 P3. After: the same curve split at t = 0.5 into a green left half with control points P0, Q0, R0, B and a teal right half with B, R1, Q2, P3; each new polygon hugs its half much more closely
Splitting at t = 0.5 gives two cubics whose control polygons hug the curve more tightly.

Each split cuts the gap between the control polygon and the curve roughly by a factor of 4, so a few rounds of splitting make each piece flat enough to draw as a straight line. That is how renderers draw curves and how curve intersection is computed.

Curves in fonts and SVG paths

Real shapes are many pieces joined end to end. TrueType fonts use quadratic pieces; PostScript, CFF fonts, SVG C commands and the HTML canvas bezierCurveTo use cubics. The S-curve demo joins two cubics at P3. Because P2, P3 and P4 lie on one straight line, both pieces leave P3 in the same direction and the join has no corner (G1 continuity). Each piece keeps its own convex hull (Demo: S-curve (a font-glyph path)).

See also Rotation and Scaling (2D) for the transforms you apply to the control points, and Convex Hull for how the hull itself is computed.

Once a curve or surface is cut into triangles, Rasterization turns them into pixels.

What the page leaves out

Rational Bézier curves (with weights, which can draw exact circles), B-splines and NURBS, curves of degree above 3, surfaces, arc-length parameterization (equal steps in t are not equal steps along the curve, as the uneven bead spacing shows), and adaptive flattening are not drawn. Control points are fixed; the page does not let you drag them.