spirographs
spirographs · sources · schema v1SPIROGRAPHS — a classic-spirograph video GENERATOR (a pure synth SOURCE: no video input). It draws 1–3 INDEPENDENT spirograph curves — HYPOTROCHOIDS (the rolling circle rolls INSIDE the fixed one) or EPITROCHOIDS (OUTSIDE) — each with its OWN full parameter set + matching CV, each DRIFTING around the screen with its fixed circle bouncing off the frame edges like a real spirograph pinned to the page. THE CURVES (a pen at offset p in a rolling circle of radius r rolling on/in a fixed circle of radius R): hypotrochoid x=(R−r)cos t + p·cos(((R−r)/r)t), y=(R−r)sin t − p·sin(((R−r)/r)t); epitrochoid x=(R+r)cos t − p·cos(((R+r)/r)t), y=(R+r)sin t − p·sin(((R+r)/r)t). The parameter t sweeps over exactly enough revolutions to CLOSE the figure — derived from the reduced R:r ratio (revolutionsToClose = the denominator of R/r in lowest terms); irrational-ish ratios are CAPPED at a sane maximum so the curve dense-fills the annulus instead of running forever. COUNT (1..3, DISCRETE knob + CV) sets how many spiros render. Each spiro i∈{1,2,3} owns ten params (port/param id sI_<name>), EACH with a knob AND a CV input: fixedRadius (R), rollingRadius (r), penOffset (p), inside (0=epitrochoid/outside, 1=hypotrochoid/inside — a discrete toggle), rotation, scale, xOffset, yOffset, thickness (real px line-width), and chroma (a colorwheel HUE for that spiro). MOTION: each spiro's CENTER drifts on its OWN per-spiro velocity/phase (the three never move in lockstep); the FIXED-radius circle (R scaled to screen) is CONSTRAINED to stay fully inside the frame and rolls/BOUNCES (elastic reflection) off the perimeter when it hits an edge — only the fixed circle's center+R is bound-constrained (closed-form, deterministic), while the drawn CURVE may extend past the viewport and clip (desired). The reflect/bounce + the curve math live in the pure, unit-tested $lib/video/modules/spirographs-math. RENDERING: Canvas2D polylines (real line-width with round joins/caps) painted to an OffscreenCanvas and uploaded as a GL texture each frame (the SHAPEGEN/TEXTMARQUEE path) — the right tool for thick stroked curves, where a GLSL distance-field would be costlier and read worse. OUTPUTS (all video): out (video) — the full-COLOUR composite, each spiro in its chroma hue additively blended on black so crossings glow; mono_out (mono-video) — every spiro stroked WHITE on black, a clean matte for keying / luma effects (reachable via read('outputTexture:mono_out')); overlap (video, labelled CANDY) — a COLOUR-OVERLAP output: the per-pixel overlap DENSITY (how many lines stack there — self-crossings AND multiple spiros) is colour-mapped into a rainbow that CASCADES with the count (deep cool hue for one line, racing through green→yellow→red→magenta as lines pile up) and blooms toward a white candy core where many overlap — gooey "candy" goodness (reachable via read('outputTexture:overlap')). CARD: a live preview of the colour OUT, the COUNT knob, a 1/2/3 SPIRO SELECTOR that swaps the knob bank to that spiro, an INSIDE/OUTSIDE toggle, a CHROMA colorwheel, and the per-spiro fader bank. All ports live on the yellow drill-down PATCH PANEL (no raw side jacks); the drill-down GROUPS the CV inputs per-spiro (a count section + spiro1 / spiro2 / spiro3 sections). USAGE: dial COUNT, pick a spiro tab, tune R/r/pen for the figure (low gcd ratios make few-lobed flowers, coprime ratios dense rosettes), set INSIDE/OUTSIDE + chroma, then patch out → OUTPUT / a video mixer / an effect — or modulate any per-spiro param from an LFO/sequencer for an animated, drifting spirograph.
the faceplate
outputs
| id | cable | what it does |
|---|---|---|
out | video | COLOR: the canonical full-color composite, each spiro stroked in its hue and additively blended (lighter) on black so overlaps glow toward white. RGB video stream |
mono_out | mono-video | MONO: every spiro stroked white on black, a clean matte for keying or luma effects downstream. mono video stream |
overlap | video | CANDY: the per-pixel line-stack density mapped to a cascading rainbow that blooms to a white core where many lines pile up (hue-independent: driven by density, not the chroma controls). RGB video stream |
controls
| control | what it does |
|---|---|
| Count | Count (1-3, discrete): how many of the three spiros render. Independent of which tab you are editing. |
| S1 R | Spiro 1 Fixed (R, 1-12): the fixed outer circle radius; with Roll it sets the petal/loop ratio. |
| S1 chroma | Spiro 1 Hue (0-1 colorwheel): the curve's color in the COLOR output (MONO and CANDY ignore it). |
| S1 inside | Spiro 1 In/Out toggle: INSIDE = hypotrochoid (rolling circle inside), OUTSIDE = epitrochoid (rolling circle outside). |
| S1 p | Spiro 1 Pen (p, 0-8): pen offset in the rolling circle; 0 traces a plain circle, larger makes deeper loops. |
| S1 r | Spiro 1 Roll (r, 0.5-11): the rolling circle radius; the R:r ratio defines the figure and revolutions to close. |
| S1 rotation | Spiro 1 Rot (0-2pi radians): static rotation of the whole figure about its center. |
| S1 scale | Spiro 1 Scale (4-60): zoom from spiro-space units to pixels; with R it also sets the fixed circle's bounce inset. |
| S1 thickness | Spiro 1 Width (0.5-12 px): stroke line width, drawn with round joins and caps. |
| S1 xOffset | Spiro 1 X (-1..1): nudges the drift home position horizontally (center still drifts and bounces). |
| S1 yOffset | Spiro 1 Y (-1..1): nudges the drift home position vertically (center still drifts and bounces). |
| S2 R | Spiro 2 Fixed (R, 1-12): the fixed outer circle radius; with Roll it sets the petal/loop ratio. |
| S2 chroma | Spiro 2 Hue (0-1 colorwheel): the curve's color in the COLOR output (MONO and CANDY ignore it). |
| S2 inside | Spiro 2 In/Out toggle: INSIDE = hypotrochoid (rolling circle inside), OUTSIDE = epitrochoid (rolling circle outside). |
| S2 p | Spiro 2 Pen (p, 0-8): pen offset in the rolling circle; 0 traces a plain circle, larger makes deeper loops. |
| S2 r | Spiro 2 Roll (r, 0.5-11): the rolling circle radius; the R:r ratio defines the figure and revolutions to close. |
| S2 rotation | Spiro 2 Rot (0-2pi radians): static rotation of the whole figure about its center. |
| S2 scale | Spiro 2 Scale (4-60): zoom from spiro-space units to pixels; with R it also sets the fixed circle's bounce inset. |
| S2 thickness | Spiro 2 Width (0.5-12 px): stroke line width, drawn with round joins and caps. |
| S2 xOffset | Spiro 2 X (-1..1): nudges the drift home position horizontally (center still drifts and bounces). |
| S2 yOffset | Spiro 2 Y (-1..1): nudges the drift home position vertically (center still drifts and bounces). |
| S3 R | Spiro 3 Fixed (R, 1-12): the fixed outer circle radius; with Roll it sets the petal/loop ratio. |
| S3 chroma | Spiro 3 Hue (0-1 colorwheel): the curve's color in the COLOR output (MONO and CANDY ignore it). |
| S3 inside | Spiro 3 In/Out toggle: INSIDE = hypotrochoid (rolling circle inside), OUTSIDE = epitrochoid (rolling circle outside). |
| S3 p | Spiro 3 Pen (p, 0-8): pen offset in the rolling circle; 0 traces a plain circle, larger makes deeper loops. |
| S3 r | Spiro 3 Roll (r, 0.5-11): the rolling circle radius; the R:r ratio defines the figure and revolutions to close. |
| S3 rotation | Spiro 3 Rot (0-2pi radians): static rotation of the whole figure about its center. |
| S3 scale | Spiro 3 Scale (4-60): zoom from spiro-space units to pixels; with R it also sets the fixed circle's bounce inset. |
| S3 thickness | Spiro 3 Width (0.5-12 px): stroke line width, drawn with round joins and caps. |
| S3 xOffset | Spiro 3 X (-1..1): nudges the drift home position horizontally (center still drifts and bounces). |
| S3 yOffset | Spiro 3 Y (-1..1): nudges the drift home position vertically (center still drifts and bounces). |
source
spirographs.ts on GitHub.