Generalizing the Curves in triple_rotating_circle_animated*.html
Generalizing the Curves in triple_rotating_circle_animated*.html
Every curve drawn by the four files in this folder is of the form
\[x(t) = c_x + r_0 \cos(\omega_0 t) + r_1 \cos(\omega_1 t) + r_2 \cos(\omega_2 t), \qquad y(t) = c_y + r_0 \sin(\omega_0 t) + r_1 \sin(\omega_1 t) + r_2 \sin(\omega_2 t),\]where the code’s radius, radius1, radius2 are the amplitudes $r_0, r_1, r_2$ and freq, freq1, freq2 are the angular frequencies $\omega_0, \omega_1, \omega_2$. In complex notation this is a single clean expression:
This is an epicycle: a point sitting on the end of a chain of rotating arms, or equivalently a truncated Fourier series of a closed curve. Ptolemy’s deferent-plus-epicycle model and the classic Spirograph are the same object.
Part 1: The General N-Arm Epicycle
The natural generalization of all the curves drawn here is a sum of $N$ uniform circular motions, each with its own radius $r_k \ge 0$, (signed) frequency $\omega_k \in \mathbb{R}$, and phase $\phi_k \in \mathbb{R}$:
\[\boxed{\; z(t) \;=\; \sum_{k=0}^{N-1} r_k \, e^{i(\omega_k t + \phi_k)} \;}\]In Cartesian form:
\[x(t) = \sum_{k=0}^{N-1} r_k \cos(\omega_k t + \phi_k), \qquad y(t) = \sum_{k=0}^{N-1} r_k \sin(\omega_k t + \phi_k).\]The four HTML files are the special case $N = 3$ with all phases $\phi_k = 0$ — which is why every drawing starts at its rightmost point, $x = c_x + r_0 + r_1 + r_2$, and every arm points due east at $t = 0$.
Mechanical interpretation: arm $k$ is a circle of radius $r_k$ turning at rate $\omega_k$, mounted at the tip of arm $k-1$ (Ptolemy) or all mounted at the common center (flat superposition). The two realize the same trace.
Part 2: Named Subfamilies
| Arms | Condition | Curve |
|---|---|---|
| $N=1$ | — | Circle of radius $r_0$, traced $\lvert\omega_0\rvert$ times per period |
| $N=2$, $\omega_1/\omega_0 > 0$ | $r_0 \ne r_1$ | Epitrochoid (Spirograph curve) |
| $N=2$, $\omega_1/\omega_0 < 0$ | $r_0 \ne r_1$ | Hypotrochoid |
| $N=2$, same direction, $\omega_1 = \omega_0$, $r_0 = r_1$ | — | Cardioid: $z = r e^{it} + r e^{i2t}$ |
| $N=2$, $\omega_1 = 2\omega_0$, $r_0 = 2r_1$ | — | Nephroid |
| $N=2$, $\omega_1 = -3\omega_0$, $r_0 = 3r_1$ | — | Astroid: $x = 4r\cos^3 t,\ y = 4r\sin^3 t$ |
| $N=2$, $\omega_1 = -2\omega_0$, $r_0 = 2r_1$ | — | Deltoid (3-cusp hypocycloid) |
| $N=2$, $r_0 = r_1$, $\omega_1 = -\omega_0$ | — | Circle (arms cancel) |
| $N=2$, $r_0 = r_1$, opposite signs | — | Rose curve (rhodonea): $\rho = 2r\lvert\cos(k\theta)\rvert$ with $k = \frac{\omega_0+\omega_1}{\omega_0-\omega_1}$ |
| $N \to \infty$ | any square-integrable closed curve | Fourier series — every periodic curve is an epicycle |
The last row is the punchline: the three-arm sum here is not a special trick, it is the first three terms of a representation in which every closed curve lives.
The cousin family Lissajous curves, $x = A\cos(a t + \phi)$, $y = B\sin(b t)$, is what you get by dropping the circular pairing — allowing different amplitudes and phases per axis. A two-arm epicycle with equal radii is exactly a Lissajous figure rotated $45^\circ$ (via $\cos a + \cos b = 2\cos\frac{a+b}{2}\cos\frac{a-b}{2}$).
Part 3: Closure, Period, and Symmetry — the Arithmetic of Frequencies
Let $\omega_k = n_k \Omega$ with $n_k \in \mathbb{Z}$ and $\Omega > 0$ the greatest common divisor of the frequencies.
Closure. The curve closes iff all frequency ratios $\omega_j/\omega_k$ are rational. The fundamental period is
\[T = \frac{2\pi}{\Omega},\]and arm $k$ wraps $n_k$ times per period.
Rotational symmetry. If $d = \gcd(n_0, \dots, n_{N-1})$, the completed figure has $d$-fold rotational symmetry about $c$ — because $z(t + T/d) = z(t)$.
Winding number. If one arm dominates, $r_0 > r_1 + \cdots + r_{N-1}$, then the curve winds $n_0$ times around $c$: the trace never reaches the center, and the index of a Fourier curve with a dominant term is the index of that term.
Irrational ratios. If any $\omega_j/\omega_k$ is irrational the curve never closes and fills an annulus densely (an ergodic linear flow on a torus).
| Two arms. If $\omega_1/\omega_0 = m/n$ in lowest terms, the figure closes after $n$ turns of the slow arm and shows $m$-fold structure; the number of cusps of the Spirograph is $ | m - n | /\gcd$-dependent — for the integer case $m \in \mathbb{Z}$, an epitrochoid has $m$ arches. |
Signs. $\omega_k < 0$ is a clockwise arm. Reversing the sign of one frequency reflects the figure (this is the epi- vs. hypo-trochoid switch).
Part 4: What Each File Instantiates
All files sweep $\theta \in [0, S \cdot 2\pi]$ with 360 points per revolution, so the number of complete turns available to arm $k$ is $S \cdot \omega_k / 2\pi \cdot$; the figure is closed iff that is an integer for every arm.
| File | Effective arms | Sweep $S$ | Frequency relations | Closed iff |
|---|---|---|---|---|
triple_rotating_circle_animated.html |
2 ($r_2$ is overwritten to 0 in drawCircleGraphics, despite the label) |
$f_1 f_2$ | $\omega_0 = 1/k$, $k\in[2,8]$; $\omega_1,\omega_2 \in \mathbb{Z}$ | $k \mid f_1 f_2$ |
...rotation_r1_r2_index.html |
3 | 100 | $\omega_0 = 1/3$; $\omega_2 = 3\omega_1$ (pure harmonic chain) | never — arm 0 makes $33\tfrac13$ turns |
...savefile_index.html |
3 | 100 | $\omega_0 = 1/k$, $k\in[2,15]$; $\omega_1,\omega_2\in\mathbb{Z}$ | $k \mid 100$, i.e. $k \in {2,4,5,10}$ |
...savefile_uniq_index.html |
3 | $f_1 f_2$ | $\omega_0 = 1/k$, $k\in[2,8]$; $\omega_1,\omega_2\in\mathbb{Z}$ | $k \mid f_1 f_2$ |
Notes:
- The sweep bound $2\pi\, f_1 f_2$ is deliberately chosen so the inner arms always complete an integer number of loops ($f_1 f_2$ turns each); only the slow outer arm $\omega_0 = 1/k$ can fail to close.
- In
rotation_r1_r2_index.html, $\omega_2 = 3\omega_1$ makes arm 2 the 3rd harmonic of arm 1, producing the nested 3-lobed detail; $\omega_0 = 1/3$ is a subharmonic “drift” arm that never closes. - When the figure is open, the end point misses the start point by the residual rotation of the slow arm — the “seam” you see at the rightmost reach.
Part 5: Directions of Generalization
From the base form $z(t) = \sum r_k e^{i(\omega_k t + \phi_k)}$:
- Nonzero phases $\phi_k$. The files pin all $\phi_k = 0$. Lifting this breaks the common east-pointing start and shears/lops the figure; with phases, a two-arm figure becomes a general Lissajous/rose hybrid.
- Signed frequencies. Let $\omega_k$ be any integer (negative = clockwise). This one knob turns the same code into roses, hypocycloids, and epicycloids.
- Per-axis amplitudes (Lissajous form). $x = \sum A_k\cos(\omega_k t+\phi_k)$, $y = \sum B_k\sin(\omega_k t+\psi_k)$ — dropping the circular pairing $A_k = B_k = r_k$.
- Damping → harmonograph. $r_k \to r_k e^{-\lambda_k t}$ turns closed loops into inward spirals — the classic pendulum harmonograph drawings.
- Frequency drift (chirp). $\omega_k t \to \omega_k t + \alpha_k t^2$ — the lobes open into flame-like figures.
- More arms. $N$ arbitrary; as $N \to \infty$ this is Fourier synthesis, and any closed curve (a cat, an outline of any shape) can be traced by epicycles.
- Third dimension. Add $z(t) = \sum s_k \sin(\nu_k t + \xi_k)$ for 3D epicycles / spherical Lissajous figures.
- Algebraic form. For integer $\omega_k$, $\cos(\omega_k t) = T_{\omega_k}(\cos t)$ (Chebyshev) and $\sin(\omega_k t) = \sin t \cdot U_{\omega_k-1}(\cos t)$, so every such figure is a polynomial parametrization — an algebraic curve.
Part 6: A Generalized Drawing Function
The whole family (and every file above) is one function parameterized by an array of arms:
// arms: [{r, w, phi}, ...]; sweeps: number of 2π turns of the parameter
function drawEpicycle(canvas, arms, sweeps, stepsPerTurn = 360) {
const ctx = canvas.getContext('2d');
const cx = canvas.width / 2, cy = canvas.height / 2;
ctx.beginPath();
const total = Math.round(sweeps * stepsPerTurn);
for (let i = 0; i <= total; i++) {
const t = (i / stepsPerTurn) * 2 * Math.PI;
let x = cx, y = cy;
for (const {r, w, phi} of arms) {
x += r * Math.cos(w * t + phi);
y += r * Math.sin(w * t + phi);
}
i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
}
ctx.stroke();
}
Then the existing pages are exactly:
triple_rotating_circle_animated.html:drawEpicycle(canvas, [{r: radius, w: 1/k, phi: 0}, {r: radius1, w: freq1, phi: 0}], freq1 * freq2)...rotation_r1_r2_index.html:drawEpicycle(canvas, [{r: radius, w: 1/3, phi: 0}, {r: radius1, w: freq1, phi: 0}, {r: radius2, w: 3*freq1, phi: 0}], 100)...savefile_index.html:drawEpicycle(canvas, [{r: radius, w: 1/k, phi: 0}, {r: radius1, w: freq1, phi: 0}, {r: radius2, w: freq2, phi: 0}], 100)...savefile_uniq_index.html:drawEpicycle(canvas, [{r: radius, w: 1/k, phi: 0}, {r: radius1, w: freq1, phi: 0}, {r: radius2, w: freq2, phi: 0}], freq1 * freq2)
with random ranges exactly as in each file. One function, all four galleries.