Spirograph
A spirograph is a toothed wheel rolling round a ring, with a pen through one of the wheel’s holes. This plate draws five such figures in turn. For each one the ring radius R, the wheel radius r and the pen’s distance from the wheel’s centre d are whole numbers, and the figure closes after exactly r ÷ gcd(R, r) trips of the wheel round the ring. With R 96 and r 60, which share a factor of 12, that is 5 trips and an 8‑lobed star. The coral dot is the pen.
About this piece
The faint circle is the fixed ring. The translucent circle is the wheel, the thin line is the arm from the wheel’s centre to the pen, and the coral dot is the pen itself, drawn last so it is never hidden by the curve. The wheel’s centre takes 1.8 seconds per trip round the ring. When the pen is back where it started, the closed figure is held for 2.6 seconds and the next one begins, so all five take 58 seconds. The line along the bottom names the curve, its R, r and d, the trip in progress (“trip 3 of 5”) and the number of lobes it will have. With reduced motion switched on, you get one still frame: the first figure, closed.
The five figures
All three sizes are in the same unit, so the ring, the wheel and the arm on screen are to scale. “Trips” counts the trips of the wheel’s centre round the ring before the pen is back at its start. The gcd column is there because it decides the other two:
| Curve | R | r | d | gcd(R, r) | Trips | Lobes |
|---|---|---|---|---|---|---|
| Hypotrochoid | 96 | 60 | 45 | 12 | 5 | 8 |
| Hypotrochoid | 105 | 63 | 52 | 21 | 3 | 5 |
| Hypotrochoid | 96 | 84 | 56 | 12 | 7 | 8 |
| Hypotrochoid | 105 | 40 | 36 | 5 | 8 | 21 |
| Epitrochoid | 60 | 24 | 30 | 12 | 2 | 5 |
The first figure is an 8‑pointed star of long loops. The second has the same R : r ratio as a 5‑tooth ring and a 3‑tooth wheel (105/63 = 5/3), so it is a 5‑lobed star that closes in 3 trips. In the third the wheel is nearly as big as the ring: its centre circles only 12 units from the middle, and the pen draws a slowly turning 8‑sided loop. The fourth is the dense one, 21 lobes in 8 trips. The fifth rolls the wheel round the outside of a ring and draws five large overlapping loops, with a small inner loop near the ring between each pair.
The formula
Let t be the angle of the wheel’s centre round the ring. Inside the ring the centre stays on a circle of radius R − r. Rolling without slipping turns the wheel the other way, and the pen’s arm turns through (R − r)/r radians for every radian the centre travels. Outside the ring the centre is at R + r and the arm turns (R + r)/r as fast:
hypotrochoid (wheel inside the ring) x = (R − r) cos t + d cos((R − r) t / r) y = (R − r) sin t − d sin((R − r) t / r) epitrochoid (wheel outside the ring) x = (R + r) cos t − d cos((R + r) t / r) y = (R + r) sin t − d sin((R + r) t / r)
This is the form given on Wikipedia’s hypotrochoid page and its epitrochoid page. The shape depends only on the two ratios R/r and d/r, which is why R 105, r 63 draws the same star as R 5, r 3 would, just larger.
Why it closes after r / gcd(R, r) trips
After k whole trips (t = 2πk) the wheel’s centre is home. The pen is home only if the arm has also made a whole number of turns, and the arm has turned k(R − r)/r = kR/r − k times. So the figure closes at the first k for which kR/r is a whole number. Write g = gcd(R, r). Then R/r = (R/g)/(r/g) is a fraction in lowest terms, and kR/r is whole exactly when r/g divides k. The smallest such k is r/g.
Worked example, R 96, r 60. gcd(96, 60) = 12, so R/r = 8/5. Each trip turns the arm by 36/60 = 0.6 of a turn: 0.6, 1.2, 1.8, 2.4, then 3.0 after 5 trips, the first whole number. So the pen closes after 5 trips, not before.
The lobes are R/g. The pen’s distance from the centre satisfies |p|² = (R − r)² + d² + 2(R − r)d cos(Rt/r). That distance peaks R/r times per trip, so over r/g trips it peaks (R/r)(r/g) = R/g times. Each peak is the tip of a lobe. For the epitrochoid the cosine has a minus sign in front but the same Rt/r inside, so the count is the same. In toy terms: by the time the figure closes, the wheel’s rim has rolled its whole circumference along the ring R/g times, and the pen swings out towards the ring once per roll. For R 96, r 60 that is 5 trips of 96 teeth, 480 teeth in all, which is 8 turns of a 60‑tooth wheel.
Why these five avoid coprime sizes. With R 96 and r 35, which share no factor, the figure needs 35 trips and has 96 lobes. At 1.8 seconds a trip that is over a minute of drawing, and the result is a nearly solid disc. Sizes with a big common factor give the open figures.
Special cases. With d = r the pen sits on the rim and every lobe tip becomes a sharp cusp: that is a hypocycloid. With d = 0 the pen is at the centre and draws a plain circle of radius R − r. With R = 2r the figure closes in one trip and is an ellipse with semi-axes r + d and |r − d|. If d = r as well, that ellipse is flat and the pen runs back and forth along a straight line, the Tusi couple.
How it is computed
The picture is a pure function of the plate’s age. The age picks the figure and how far round it the pen has got, and the curve is redrawn from t = 0 every frame as straight segments, 720 per trip. That is at most 8 × 720 = 5,760 segments, for the 21‑lobed figure. The frame rate decides how often the picture is redrawn, never what is in it. The math lives in one block of code, used by the gallery tile, the embed and a node module that the test suite loads. The suite checks that the curve matches the formula above at 1,000 random parameter values. For 10 seeded (R, r) pairs, it checks that the pen is back within 10−9 after exactly r/gcd(R, r) trips and is not back after any earlier whole trip. It also counts the radius peaks over a closed figure and compares them with R/gcd(R, r), and checks the table above against the code. The first draft returned one trip too many. Nothing on screen showed it: the pen simply went round its closed figure once more. The closure test caught it.
A 30-line version
The snippet below draws the first figure, R 96, r 60, d 45, in the site’s palette, in 28 lines of HTML and plain JavaScript with no library. It adds 24 segments a frame, so it closes after 150 frames, about 2.5 seconds at 60 frames a second. With reduced motion it draws the closed figure at once. The test suite runs it against a stub canvas and checks that it stops exactly where it started, after 5 trips. It leaves out the ring, the wheel and the arm, the other four figures, sharp drawing on high-density screens (it draws in CSS pixels) and the reaction to theme changes.
Things worth changing: r = 35 shows why coprime sizes are dense (35 trips, 96 lobes). d = 60 puts the pen on the rim and turns the loops into cusps. R = 120, r = 60 gives an ellipse in one trip.
<canvas id="spiro" style="position:fixed; inset:0"></canvas>
<script>
const dark = matchMedia('(prefers-color-scheme: dark)').matches;
const [ground, ink, pen] = dark ? ['#0B0D12', '150,180,255', '255,120,84']
: ['#E7E2D5', '40,72,205', '190,68,28']; // the site's palette
const R = 96, r = 60, d = 45; // ring radius, wheel radius, pen distance: whole numbers
const gcd = (a, b) => (b ? gcd(b, a % b) : a);
const TRIPS = r / gcd(R, r); // 5: the pen is home after this many trips, and not before
const N = 720 * TRIPS, END = 2 * Math.PI * TRIPS; // 720 straight segments per trip
const point = (t) => [(R - r) * Math.cos(t) + d * Math.cos((R - r) * t / r),
(R - r) * Math.sin(t) - d * Math.sin((R - r) * t / r)];
const cv = document.getElementById('spiro'), ctx = cv.getContext('2d');
const W = cv.width = innerWidth, H = cv.height = innerHeight;
const k = Math.min(W, H) * 0.45 / Math.max(R, R - r + d); // the ring fills 90% of the short side
const still = matchMedia('(prefers-reduced-motion: reduce)').matches;
let n = still ? N : 0; // samples drawn so far
function frame() {
n = Math.min(N, n + 24);
ctx.fillStyle = ground; ctx.fillRect(0, 0, W, H);
ctx.strokeStyle = `rgb(${ink})`; ctx.lineWidth = 1.5; ctx.beginPath();
for (let i = 0; i <= n; i++) { const [x, y] = point(END * i / N); ctx.lineTo(W / 2 + x * k, H / 2 + y * k); }
ctx.stroke();
const [x, y] = point(END * n / N); // the pen, on top
ctx.fillStyle = `rgb(${pen})`; ctx.beginPath(); ctx.arc(W / 2 + x * k, H / 2 + y * k, 5, 0, 2 * Math.PI); ctx.fill();
if (n < N) requestAnimationFrame(frame);
}
frame();
</script>
The plates it sits nearest
- Fourier Epicycles adds up many circles turning at different speeds. A hypotrochoid is the case with just two: one of radius R − r turning forwards, and one of radius d turning backwards (R − r)/r times as fast.
- Lissajous Ribbon is another closed curve whose shape is set by a ratio of whole numbers, there 3 : 2 between two sine waves.
- Cosmic Circles also moves things on circles, but leaves no trace of where they have been.
Honest limits
A toy cannot draw every figure here. On a real wheel the pen holes are inside the rim, so d is less than r. All four hypotrochoids keep to that. The epitrochoid does not: its d 30 is bigger than its r 24, which is what gives it the small loops, and a real wheel would need an arm sticking out to draw it.
Real gears are not ideal circles. Teeth have a pitch, the wheel wobbles in the ring and the pen line has width, so a hand-drawn spirograph rarely closes perfectly. The plate draws the ideal no-slip curve.
The curve is a polyline. At 720 segments per trip, the 21‑lobed figure still gets about 270 segments per lobe, and no corner shows at screen sizes. In floating point the pen comes back to within about 10−13 units of its start, far less than a pixel, and the plate stops each figure exactly at its closing value of t anyway.
Curious how the loop and canvas fit together? Read how it works →
More from the gallery
- Plate 43 Ulam Spiral Count 1, 2, 3 in a square spiral outward, light every prime, and diagonal streaks appear.
- Plate 44 Pendulum Wave Fifteen pendulums tuned to 51 to 65 swings a minute drift into snakes, split into two rows at 30 s and line up again at 60 s.
- Plate 01 Quantum Fibonacci Golden-angle phyllotaxis blooming from the seed outward.
- Plate 02 Bitcoin Matrix A quiet rain of ₿ and hex sliding down the glass.
- Plate 03 Cosmic Circles Soft orbs drifting through slow orbital rounds.
- Plate 04 Spiral Waves Archimedean arms turning, so the bands seem to stream inward.