The idea: a shape is a sum of spinning circles

Put a circle on the tip of an arm, another circle on the tip of that one, and so on. Let every arm turn at its own whole-number speed. The last tip moves along a path, and with the right radii, speeds and start angles that path can be any closed shape: a heart, a star, a square, a music note, the letter π.

The right circles come from the discrete Fourier transform (DFT) of points on the shape. Each circle is one Fourier term, and sorting them biggest first shows how a shape is built: a few big circles give its rough form, many small ones the detail.

Left: the heart's 3 biggest circles tip to tail at t = 0.2: the big n = −1 circle centred at the start, then the n = −3 and n = −2 circles; blue arms join the centres and the red trace of the last tip has drawn the top of the heart so far. Right: after one full turn the 3-circle trace is a rounded heart inside the faint grey shape
Each arm is one term cₙ·e^(2πi·n·t); added tip to tail, the last tip draws the shape.

From a shape to 256 complex numbers

The page walks once around each outline and takes N = 256 points, evenly spaced along the outline (the heart: at evenly spaced values of the parameter s in its formula). A point (x, y) is written as one complex number

zₖ = xₖ + i·yₖ        k = 0, 1, …, 255

Each shape is moved so the mean of its points is 0 and scaled so it fills the plot. The grey dots on the canvas are these 256 points, and the faint grey line joins them.

The DFT gives the circles

The DFT turns the 256 points into 256 numbers, one per whole frequency n:

cₙ = (1/N) · Σₖ zₖ · e^(−2πi·n·k/N)        n = −128, …, 127
zₖ = Σₙ cₙ · e^(2πi·n·k/N)                 (the inverse DFT)

Read the second line with k/N replaced by a time t that runs from 0 to 1. Each term cₙ·e^(2πi·n·t) is an arrow of length |cₙ| that starts at angle arg cₙ and turns n times while t goes once round (e^(iθ) = cos θ + i·sin θ is the point at angle θ on the unit circle). That is one circle:

part of cₙ·e^(2πi·n·t)on the canvas
|cₙ|the radius of the circle
nthe speed: n full turns per turn, anticlockwise for n > 0, clockwise for n < 0
arg cₙthe start angle of the arm at t = 0
c₀the mean of the points, where the chain starts (0 here)

Adding complex numbers is adding arrows tip to tail, so the sum of the terms is the chain of arms, and its last tip is z(t). (Demo: heart, 5 circles, one full turn.) This page computes the sum straight from the definition; the FFT gets the same 256 numbers much faster.

Biggest circles first

The page sorts the terms by |cₙ| and draws only the K biggest. For most shapes |cₙ| falls off fast, so a handful of circles already gives the rough shape and the rest add detail. A good measure of "how much of the shape" K circles hold is the energy Σ|cₙ|²: by Parseval's theorem the total equals the mean of |zₖ|², and the canvas shows the share the K circles keep.

Bar chart of the 30 biggest radii |cₙ| of the star, sorted: n = 1 is far the biggest, then n = −4 and 6, then a long tail of small bars n = −14, 16, −9, 11 and so on; the first 3 bars hold 99.9 % of the energy
The star's circles sorted by size: three big ones hold 99.9 % of the energy, the tail sharpens the points.
shapeK = 1K = 3K = 10K = 50non-zero terms
heart87.0 %95.5 %100 % (exact)100 %8
star92.8 %99.90 %99.996 %99.99996 %255
square98.6 %99.93 %99.998 %99.99997 %64
music note60.1 %90.1 %99.63 %99.998 %255
letter π56.3 %91.7 %99.52 %99.996 %255

Energy is not everything: the square keeps 98.6 % with one circle, but a circle is not a square. The last fraction of a percent is what makes the corners sharp. The "miss" on the canvas, the largest distance between a sample point and the trace at that point's time, shows this better.

All 256 terms: exact at the points

With all N = 256 terms the sum is the inverse DFT, so at t = k/256 the last tip lands exactly on zₖ. (Demo: π with all 256 circles hits every point: the miss is 0 px.) Between the points the trace is the smoothest curve made of these frequencies, so it can wiggle a little near corners even then.

A shape with fewer non-zero terms needs fewer circles. The heart is x = 16 sin³s, y = 13 cos s − 5 cos 2s − 2 cos 3s − cos 4s, and sin³s = (3 sin s − sin 3s)/4, so only the frequencies ±1 … ±4 appear: 8 circles draw it exactly, with radii in the ratio 12.5 : 3 : 2.5 : 2.5 : 1 : 0.5 : 0.5 : 0.5. (Demo: the heart is exact with 8 circles.) For another view of a heart built from a sine, see The Heart Curve.

Corners need many circles

A corner is a sudden change of direction. Smooth circles can only round it off, and the faster circles that sharpen it are small: for a polygon |cₙ| shrinks only like 1/n². So the square's corners come out rounded, with small wiggles next to them (ringing), and they tighten slowly as K grows. (Demo: square, add circles 1 → 100: the miss falls from 78 px with 1 circle to 16, 8, 4 and 0.7 px with 5, 10, 20 and 50, and to 0 at 100.)

Three copies of the faint square with the red trace for K = 3, 10 and 50 circles: K = 3 is a rounded square, K = 10 has rounded corners with small wiggles along the sides, K = 50 is almost the square with a tiny wiggle at each corner
The square with 3, 10 and 50 circles: the corners sharpen slowly and ring as they do.

The square also shows symmetry. Turning it by a quarter turn gives the same square a quarter of the way along, and that forces cₙ = 0 unless n = 1, −3, 5, −7, 9, … (n − 1 divisible by 4). Only 64 of its 256 terms are non-zero, so K = 64 is already exact at the points. The star has five-fold symmetry and its biggest terms are n = 1, −4, 6, −14, 16, … (n − 1 divisible by 5).

Unlike a square wave, the square path has no jump: the outline is continuous, only its direction jumps. A jump makes |cₙ| shrink like 1/n and leaves the 9 % Gibbs overshoot that never goes away (see Fourier Series, Harmonic by Harmonic); a corner's ringing does shrink to nothing as K grows.

Playing it

Play: draw puts t at 0 and turns it once round in 128 steps; the speed choice makes one turn take about 34 s, 17 s or 9 s. Picking a shape or a number of circles shows the finished trace straight away. Circles smaller than about half a pixel are not drawn, but their arms still count.

Rolling circles draw curves too, with only two circles and a fixed ratio of speeds: see Spirograph: Hypotrochoids and Epitrochoids. A harmonograph adds swinging pendulums instead of spinning circles: see The Harmonograph.

What the page leaves out

Drawing your own shape, sampling that is not even in arc length (and the distortion it causes), choosing the circles by frequency (|n| ≤ K/2) instead of by size, the FFT itself, the exact rate at which |cₙ| falls for smooth and for cornered curves, open curves (which need to be closed first, often by going back along them), and 3D versions. The shapes use 256 points each and the trace 2048 points per turn.