Materializr
Open-source parametric 3D CAD for makers — constraint sketches, solid modeling, threads, SVG & text engraving, STL/STEP export. Android, iPad, Linux, Mac, Windows. Vibe coded.
Roll gears of any shape, write names with Fourier epicycles, and save as PNG, SVG, video or GIF. Free, no sign-up.
DesignHTML · JavaScriptGitHub @ianmcmanOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Spirograph inspired self-hostable web app
Translation pending
Taken from the repository README
Loading the full README.
Read the full README ↗Draw spirograph patterns in the browser, then turn your name into a chain of spinning circles.
Open the app · Features · Controls · How it works · Self-host
One HTML file, no dependencies, no build step, no tracking, no server-side state. It runs on desktop and phones, installs to a home screen, and works offline.
| Wheels shown while rolling | Second wheel | Name as epicycles |
|---|---|---|
Type a name and press Play. A chain of rotating circles (a Fourier series of the letter strokes) writes it out, drawn from the real outlines of JetBrains Mono.
Upload a picture and its edges are traced as one line, then drawn by the same chain of rotating circles.
#p=..., only what differs from the defaults). Reload keeps your work. Copy link shares it (the share sheet on phones). Older long links still open./3d): arms joined end to end, each turning about its own axis (tilt, azimuth, phase, whole-number speed) and carrying the rest; the pen is at the tip. Up to 16 arms, orbit view in Three.js as a line or a tube, "Show the arms" to see the chain follow the pen, Randomise, a link that holds the settings, PNG, a binary STL of the curve as a closed tube in millimetres for printing, and a 10 s WebM turntable. "Open in 3D" on the main page fits the finished pattern with up to 16 arms (its strongest Fourier terms, chained on parallel axes, second arm tilted out of the plane). Round shapes come out as 2 arms; other shapes and the second wheel match to within a few percent of the radius (sharp corners soften). Names and pictures are not converted. Music mode: the Music card turns the curve into a visualiser fed by the microphone, shared tab or system audio (desktop Chrome and Edge, tick "Share tab audio"; works with the Spotify and Apple Music web players) or an audio file. Bass pulses the arm lengths, mids sway the tilt, highs shift the colour and beats kick the phases; whole-number speeds are left alone so the curve stays closed. Sensitivity and Motion sliders. Music video (lab only): with an audio file chosen, "Render MP4 with this music" uploads the track and queues a job that analyses it on the server with the same bands and logic, draws every frame on worker threads (glowing line that breathes with the bass and flashes on beats, 720p30, 1080p30 or 1080p60) and muxes the original audio into an MP4; it appears in the job list with progress, cancel and re-run. Tracks up to 6 minutes. Spotify and Apple Music cannot be read directly (protected audio, and Spotify's audio analysis API is closed to new apps). Without WebGL it falls back to a flat 2D viewer (drag to turn, scroll to zoom); /3d?flat forces that. Three.js is bundled into site/vendor/three-lab.js (rebuild: npx esbuild server/three-entry.js --bundle --minify --format=esm --outfile=site/vendor/three-lab.js, with three installed in server/).| Key | Action |
|---|---|
Space | Play / pause |
W | Show / hide wheels |
- / + | Slower / faster |
D | Draw the current settings |
S | Shuffle |
K | Keep layer |
U | Undo layer |
C | Clear all |
F | Finish now |
0 | Fit the design |
Settings changes don't redraw on their own: press Draw (or D). Gallery picks and Shuffle draw immediately.
k with k * ring perimeter ~ m * wheel perimeter to within 1e-3, so the curve ends where it began.site/index.html, plus a manifest, service worker, icons and the font.Current Chrome, Edge, Safari and Firefox. Video recording needs MediaRecorder and canvas capture; where a browser lacks them the button is disabled and everything else works.
The live site is the site/ directory, served as static assets from Cloudflare; a push to main deploys it. site/_headers sets the same security headers as nginx.conf.
python3 -m http.server 8080 -d site
Open http://localhost:8080/.
server/ is a second, private build for a homelab: the same page, served by a small Node server that does the heavy work itself.
site/index.html between the ENGINE-BEGIN and ENGINE-END markers and run in Node with @napi-rs/canvas, so the server draws what the browser would. Spirograph patterns match exactly; names differ slightly because text is rasterised by Skia instead of the browser.noindex, serves no service worker and has no authentication. Put it on a LAN/VPN-only hostname.cd server
docker compose up -d --build # http://HOST:8099/ ; LAB_PORT in .env changes the port
Beyond the exports, the server adds:
data/ and open from a short link (/?d=ID).u2net.onnx or u2netp.onnx in server/models/ or set MODEL_PATH.LAB_DAILY_AT (for example 07:00), LAB_TZ and the LAB_SMTP_* and LAB_MAIL_* variables and the server emails a seeded random design every day (PNG attached, link to open it saved in the lab). POST api/daily/run?send=0 previews today's without sending.POST api/export, api/jobs (kind: "batch" for names), , , ; lists jobs and downloads one.Set LAB_PUBLIC_URL, LAB_NTFY_URL, LAB_NTFY_TOKEN, LAB_HA_WEBHOOK_URL and LAB_DATA in server/.env to change where notifications go and where data lives (../data by default, writable by uid 1000).
The image is Node 22 on Debian with ffmpeg. Exports run one at a time. cd server && npm install && node test.js prints checksums of a few models for comparing the engine against the browser.
docker compose up -d --build
Open http://HOST:8080/. The container serves the app at /, runs nginx as the unprivileged nginx user on port 8080, and has a read-only root filesystem.
./deploy.sh wraps the common commands: build, up, down, logs, redeploy (docker compose up -d --build).
Copy .env.example to .env and set the host port:
cp .env.example .env
# SPIRO_PORT=9090
Then docker compose up -d. The default is 8080.
docker inspect --format '{{.State.Health.Status}}' spirograph
It prints healthy once /healthz answers. You can also curl http://HOST:8080/healthz.
docker-compose.yml contains a commented block. To use it, delete the ports: entry, uncomment the labels: and networks: blocks and the top-level networks: entry, and set the hostname, entrypoint and certificate resolver to match your Traefik. The container must share the external Docker network named proxy with Traefik.
site/JetBrainsMono-Regular.woff2 is JetBrains Mono, under the SIL Open Font License 1.1 (see site/OFL.txt). It is served with the page, so the page makes no third-party requests.
Copyright (c) 2026 Ian McManus. MIT, see LICENSE. The bundled font is under its own licence, see site/OFL.txt.
If you enjoy it, you can sponsor me on GitHub.
Rose window - Spirograph.png.api/designsapi/fetchapi/pic/splitGET api/jobsapi/jobs/ID/file