I tagged v0.1.0 on March 23. Today is April 30 and the head of main is v2.4.0. That's 75 tags across 38 days — at one point on April 18, ten of them landed in a single afternoon.
Every release is loadable via the GitHub-tag CDN: https://cdn.stackdump.com/gh/stackdump/beats-bitwrap-io@<TAG>/public/index.html opens the frozen studio for any version. So I wrote a Playwright harness that walked all 75, took screenshots of representative UI states, and built a machine-readable catalog of the whole thing. This post is the curated version: one shot per minor version. 24 frames of an app finding its shape.


Same app, same Stage mode, same Constellation visualizer. Different inputs.
The first is a single loop: five rings (kick, snare, hihat, bass, melody) joined through the connector places at the centre. That's what you get on a fresh load — one section of music laid out as one diagram.
The second is the same studio with structure: standard applied and the Show Variants toggle on (⇆ in the Stage menu). Every A/B/C variant for every slot rings the centre as one continuous circle. The audio plays only one variant per slot at a time, but the thing it's choosing from is now visible. The structure was always there; only the second picture exposes it.
That contrast is the whole post in one beat. The rest is how the UI got far enough to draw the second picture.

Tone.js from CDN, the Petri-net engine wired up to the audio thread, a Generate button, a Play button. Hit play, hear a beat. The five rings are already in there; you can hear them. You can't see them yet.

Control nets stop being long linear chains and become countdown timers. A 4-bar mute is now one place plus one transition, not 64 nodes in a row. Project-file size drops by orders of magnitude. (v0.3.0 stabilises the boot path; v0.4.0 adds a WebSocket backend mode so the same protocol the in-page worker speaks can be pointed at a Go server.)

Dynamic ring size and hits per track, plus a per-track preset manager. The mixer goes from "list of mute buttons" to "instrument-shaped." v0.5.0 had already added per-channel audio routing; v0.7.0 adds per-one-shot pitch and a redesigned reverb wash.
This is when the app starts feeling like something you'd actually want to play with.

Ten tags landed on April 18. The big one was v0.8.0: Auto-DJ. A panel that picks random macros every N bars and fires them — Mute, FX, Pan, Shape, all triggered automatically. Right-click any tile to disable it; Auto-DJ skips disabled tiles when picking.
The app could now play itself for hours, hands-free, without repeating the same loop.

Four abstract performance sliders — Energy / Groove / Chop / Space — that don't map to specific FX but ride curves over many parameters at once. v1.0.0 puts them in a modal with a Panic button and a clean apply/cancel/regen flow. First version I'd hand to a stranger.

The CIDv1 share URLs (covered in the launch post) needed a visual identity. v1.3.0 put a QR code in the centre of the ring with both client and server rendering paths producing byte-identical SVG. v1.4.0 added the PNG raster twin for social cards.
The image is the link, the link is the track, the track is deterministic from the seed. The chain doesn't break anywhere.

The composer had JS and Go implementations from the earliest tags. v1.7.0 is when both gained an arrangement DSL — structure: drop, fadeIn: [pad,melody], drumBreak: 4, feelCurve: [...] — and parity tests in CI prove they produce byte-identical output from the same seed. A 1 KB share envelope can now reconstitute a 3 MB arranged track in the listener's tab.
This is the version that makes the second picture at the top of this post possible. Variants and arrangements stop being implementation-only and become things authors can ask for by name.

The version everything else was building toward.
Press M. The whole app turns into a Petri-net display reading the same place/transition state the audio engine reads. Constellation mode shows nine sub-nets in a ring, each rendering the active variant for its slot, joined through central connector places. Beat particles pulse toward the centre on each fire.
Press ⇆ Expand and it switches to Mandala — the second image at the top of this post. Same nine slots, but every variant unfolds at once instead of just the active one. The audio doesn't change; the diagram does. What you were hearing was a choice across this denser structure.
That's the moment people say oh.
If you want the math word: the studio is, structurally, a category of composed nets — sub-nets glued through shared connector places, variants as parallel objects sharing an interface. Mandala is the moment that category becomes the picture on the screen. Of every shot in this post, this is the one that pays the architectural rent.
Five visualizer modes ship in v1.8 (Constellation, Mandala, Corona, Sonar, Petal — plus Shuffle to rotate them). Four independent viz layers (Flow / Pulse / Flames / Tilt) stack on top of any visualizer without rebuilding the DOM. They're variations on one idea: the runtime is the visual.

Mobile lock-screen playback was unreliable until v2.0.0. The fix was scheduling beats against the AudioContext clock instead of setInterval. Phone screen off, headphones in, music plays as long as the battery does.

/feed gallery with sortable column headers. Built-in Winamp-flavoured sidebar player. Mobile fullscreen overlay; iPad list-view wrapping. Macro stack mode — shift-click any tile to build a hand-curated stack, Fire Stack triggers them simultaneously.
The launch post covers the v2.4 surface in detail.
The thing I wasn't expecting: the studio's chrome barely changes for the first 15 tags. It starts looking like a DAW around v0.8, stops looking like a DAW and starts looking like a generative-music interface around v1.5. The biggest visual shifts in the catalog aren't the days Auto-DJ landed or the share card got a QR code — they're the days the Petri net rendered itself differently on the page.
There's a metric in there about how much of an app's perceived complexity comes from showing its structure vs. hiding it. I'll come back to it.
Cataloged from 75 git tags via scripts/visual-catalog.py. Per-release analysis (commit subjects, files changed, all states captured) is in screenshots/catalog/analysis.md — 2,856 lines, frontmatter-tagged, kept under version control as a long-term reference.