Spirograph (hypotrochoid) via SVG Canvas
Parametric hypotrochoid drawn as one long polyline onto an SVG canvas, then rendered with neon-glow layered strokes (four successively thinner strokes, each more opaque) for the characteristic glow. Tweak R_OUTER / R_INNER / D_OFFSET / TURNS for endlessly different flower patterns.
Spirograph
JavaScript
// Spirograph (hypotrochoid) drawn into an SVGCanvas — thousands of
// line segments form intricate flower patterns. Resolution-independent;
// the SVG output stays crisp at any zoom.
// demo_svg_spirograph.js
//!NOIMAGE
//!OUTPUT: OUTPUT
//!PARAM: WIDTH:integer=800,min=400,max=1600
//!PARAM: HEIGHT:integer=800,min=400,max=1600
//!PARAM: R_OUTER:number=200,min=80,max=400
//!PARAM: R_INNER:number=63,min=10,max=300
//!PARAM: D_OFFSET:number=120,min=10,max=300
//!PARAM: STEPS:integer=2400,min=200,max=8000
//!PARAM: TURNS:number=30,min=1,max=100
const svg = Engine.createSVGCanvas(WIDTH, HEIGHT);
const cx = WIDTH / 2, cy = HEIGHT / 2;
// Dark background
svg.fill("#0b0f1a").drawPath(Engine.createPath().rect(0, 0, WIDTH, HEIGHT));
// Hypotrochoid formula:
// x(t) = (R-r) cos(t) + d cos((R-r)/r · t)
// y(t) = (R-r) sin(t) - d sin((R-r)/r · t)
// Stretches across a single PathHandle for tightly packed SVG output.
const path = Engine.createPath();
const R = R_OUTER, r = R_INNER, d = D_OFFSET;
const rd = R - r;
const total = TURNS * 2 * Math.PI;
for (let i = 0; i <= STEPS; i++) {
const t = (i / STEPS) * total;
const x = cx + rd * Math.cos(t) + d * Math.cos((rd / r) * t);
const y = cy + rd * Math.sin(t) - d * Math.sin((rd / r) * t);
if (i === 0) path.moveTo(x, y); else path.lineTo(x, y);
}
// Glow effect — draw the same path several times at decreasing
// stroke width / increasing alpha for a neon look. Pure vector,
// no raster blur required.
const palette = [
{ c: [0.06, 0.72, 1.0, 0.10], w: 12 },
{ c: [0.40, 0.60, 1.0, 0.18], w: 6 },
{ c: [0.95, 0.40, 0.70, 0.55], w: 2 },
{ c: [1.00, 1.00, 1.00, 0.85], w: 0.6 },
];
for (const layer of palette) {
svg.pen(layer.c, layer.w).drawPath(path, true);
}
// Caption
svg.drawText("hypotrochoid · R=" + R_OUTER + " r=" + R_INNER + " d=" + D_OFFSET,
cx, HEIGHT - 24, {
font: "JetBrains Mono", size: 14, color: "#94a3b8", anchor: "middle",
});
// Serialise + rasterise for preview
const out = svg.toString();
const img = Engine.loadSVG(out, 1.0);
Engine.saveImage(img, "OUTPUT");
`${STEPS} segments, ${out.length} chars SVG`;
// © 2026 Michael Lechner · mlc OpticScript · https://mlcgo.eu · Elastic License 2.0