Appendix: The Math Behind Curves
Lerps, Bezier equations, Bernstein polynomials, and why every curve is just nested interpolation.
The Math Behind Curves
In Lesson 2, you used quadraticCurveTo and bezierCurveTo to draw curves. This appendix shows you why they work: the actual math, derived from scratch, with no prerequisites beyond basic algebra.
Everything starts with one idea: lerp.
Lerp (Linear Interpolation)
Interpolation = finding values between two known values.
Linear = in a straight line.
Lerp = find the point that sits some fraction of the way between A and B.
Building the formula step by step:
Example: A=10, B=50, you want the point 30% of the way.
Distance from A to B: B - A = 40
30% of that distance: 0.3 x 40 = 12
Start at A, walk 12: 10 + 12 = 22
As a formula:
result = A + t * (B - A)
Distribute the terms:
result = A + t*B - t*A
result = A*(1 - t) + B*t
Final form:
lerp(A, B, t) = (1-t)*A + t*B t is a fraction from 0 to 1. Think of it as “how far along.” (1-t) is the leftover. If t is 0.3, then (1-t) is 0.7. Together they are weights that say how much each point contributes:
t=0.0 -> (1.0)*A + (0.0)*B = all A, none of B -> at A t=0.3 -> (0.7)*A + (0.3)*B = mostly A, some B -> 30% toward B t=0.5 -> (0.5)*A + (0.5)*B = equal mix -> midpoint t=1.0 -> (0.0)*A + (1.0)*B = none of A, all B -> at B The weights always sum to 1: (1-t) + t = 1 This keeps you on the line between A and B. If they didn't sum to 1, you'd fly past the endpoints.
For 2D points, apply lerp to x and y separately:
x = (1-t) * Ax + t * Bx y = (1-t) * Ay + t * By
That green dot is lerp in action. It computes (1-t)A + tB for every value of t from 0 to 1, tracing a straight line. Simple, but this is the only operation you need to build any curve.
Quadratic Bezier (3 Points, Degree 2)
A quadratic Bezier curve uses 3 points:
P0 = start (curve passes through this) P1 = control (the "magnet" that pulls the curve) P2 = end (curve passes through this) The curve always starts at P0 and ends at P2. It bends toward P1 but never touches it.
The curve is built from two levels of lerp:
Level 1: Two lerps running in parallel.
A(t) = lerp(P0, P1, t) = (1-t)*P0 + t*P1
slide along P0 -> P1
B(t) = lerp(P1, P2, t) = (1-t)*P1 + t*P2
slide along P1 -> P2Level 2: Lerp between those two results.
C(t) = lerp(A, B, t) = (1-t)*A(t) + t*B(t)
this is the curve point Deriving the Formula
Substitute Level 1 into Level 2:
C(t) = (1-t) * A(t) + t * B(t)
Replace A(t) and B(t) with their definitions:
C(t) = (1-t) * [(1-t)*P0 + t*P1]
+ t * [(1-t)*P1 + t*P2]
Multiply through each bracket:
C(t) = (1-t)(1-t)*P0 + (1-t)*t*P1
+ t*(1-t)*P1 + t*t*P2
Simplify the powers:
C(t) = (1-t)^2 * P0
+ (1-t)*t * P1
+ t*(1-t) * P1
+ t^2 * P2
The two middle terms both have P1.
Combine them:
(1-t)*t + t*(1-t) = 2*(1-t)*t
Final result:
C(t) = (1-t)^2 * P0 + 2*(1-t)*t * P1 + t^2 * P2
--------- ----------- ------
weight of P0 weight of P1 weight of P2 Watch the yellow line (Level 1 lerps) slide along the control polygon. The green dot (Level 2 lerp) traces the curve. That’s all a Bezier curve is: lerps of lerps, evaluated at every t from 0 to 1.
Cubic Bezier (4 Points, Degree 3)
A cubic Bezier adds one more point and one more level of lerp:
P0 = start (curve passes through) P1 = control point 1 (pulls curve near the start) P2 = control point 2 (pulls curve near the end) P3 = end (curve passes through) Two control points allow S-curves: P1 pulls one direction, P2 pulls another.
Three levels of lerp:
Level 1 (3 lerps):
A = lerp(P0, P1, t)
B = lerp(P1, P2, t)
C = lerp(P2, P3, t)
Level 2 (2 lerps):
D = lerp(A, B, t)
E = lerp(B, C, t)
Level 3 (1 lerp):
F = lerp(D, E, t) <- the curve point Deriving the Formula
Same substitution process as quadratic, one more level deep:
F(t) = lerp(D, E, t)
= (1-t)*D(t) + t*E(t)
D(t) = (1-t)*A(t) + t*B(t)
E(t) = (1-t)*B(t) + t*C(t)
A(t) = (1-t)*P0 + t*P1
B(t) = (1-t)*P1 + t*P2
C(t) = (1-t)*P2 + t*P3
Substituting all the way down and collecting terms:
F(t) = (1-t)^3 * P0
+ 3*(1-t)^2*t * P1
+ 3*(1-t)*t^2 * P2
+ t^3 * P3 Three lerp levels visible: yellow (Level 1), purple (Level 2), green dot (Level 3 = curve point). More levels = more control = smoother shapes.
Why the Coefficients Work
The coefficients in the Bezier formulas aren’t arbitrary. They come from a fundamental identity: (1-t) + t = 1. Raise both sides to any power and expand:
The binomial theorem says: if a + b = 1, then (a + b)^n = 1 for any n.
Here a = (1-t), b = t, so:
Degree 2 (quadratic):
((1-t) + t)^2 = 1
(1-t)^2 + 2*(1-t)*t + t^2 = 1
^
these are the coefficients
from the quadratic formula!
Degree 3 (cubic):
((1-t) + t)^3 = 1
(1-t)^3 + 3*(1-t)^2*t + 3*(1-t)*t^2 + t^3 = 1
^
these are the coefficients
from the cubic formula! These weight functions are called Bernstein polynomials (named after Sergei Bernstein, a Russian mathematician). Because they always sum to 1, the curve point is always a weighted average of the control points. It can never escape the shape formed by connecting the outermost points (the convex hull).
The numbers in front (1, 2, 1 for quadratic; 1, 3, 3, 1 for cubic) are binomial coefficients. They come from Pascal’s triangle, where each number is the sum of the two above it:
Pascal's triangle:
Row 0: 1
Row 1: 1 1
Row 2: 1 2 1 <- quadratic
Row 3: 1 3 3 1 <- cubic
Row 4: 1 4 6 4 1 <- quartic (degree 4)
Each row gives the coefficients for that degree.
The numbers tell you "how many ways" each weight
appears when you expand the nested lerps. Verifying the Endpoints
You can check that the curve really passes through P0 and the last point by plugging in t=0 and t=1:
Quadratic at t=0:
(1-0)^2*P0 + 2*(1-0)*0*P1 + 0^2*P2
= 1*P0 + 0 + 0
= P0 at start
Quadratic at t=1:
(1-1)^2*P0 + 2*(1-1)*1*P1 + 1^2*P2
= 0 + 0 + 1*P2
= P2 at end
Same logic for cubic:
t=0 gives P0 (only the first term survives)
t=1 gives P3 (only the last term survives)
Control points get zero weight at both endpoints.
They only have influence between 0 and 1. The General Pattern
Degree Points Endpoints Control pts Pascal's row
1 2 2 0 1, 1 line
2 3 2 1 1, 2, 1 one bend
3 4 2 2 1, 3, 3, 1 S-curve
n n+1 2 n-1 row n
Always 2 endpoints (start and end).
Everything else is a control point. Why Canvas Stops at Cubic
Higher degrees (4+ control points) are mathematically valid. You could build a degree-10 curve with 11 points. But nobody does, for three reasons:
1. INSTABILITY
Move one control point slightly and the
entire curve wobbles unpredictably.
2. UNNECESSARY
Any shape can be made by chaining multiple
cubics end to end. Fonts, SVG, Figma, and
Photoshop all do this.
3. PERFORMANCE
More levels = more lerps per point.
Cubic is the sweet spot.
One degree-10 curve: Chained cubics:
hard to control 3-4 points each
wobbles everywhere edit one, rest stays
slow to compute fast and predictable Canvas only provides quadraticCurveTo (degree 2) and bezierCurveTo (degree 3) because that’s all anyone needs. Complex shapes like font glyphs and vector illustrations are made from dozens of short cubics stitched together, not one high-degree monster.
The Core Insight
Every smooth curve in computer graphics is lerps stacked on lerps. One level = line. Two levels = quadratic. Three levels = cubic. The operation never changes; you just add layers.
The Bezier formula, Bernstein polynomials, and binomial coefficients are all just the algebra that falls out when you substitute nested lerps into each other. If you understand (1-t)A + tB, you understand the whole thing.