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 — on April 18 alone, ten of them landed between mid-afternoon and late evening (v0.8.0 through v1.0.4).
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 (v1.8.0, for instance). So I wrote a Playwright harness that walked all 75, took screenshots of representative UI states, and built a catalog of the whole thing. The harness and the catalog are not committed to the beats-bitwrap-io repo, so the tag URLs are the reproducible part: any frame below can be re-taken by opening its tag. The run captured 24 frames, one per minor version; ten of them are here.


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 ⇄ Expand toggle on in the Stage toolbar. 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, rendered twice — in the browser and by the Go server — from the same URL at the same error-correction level. The two are not byte-identical, and the commit says so: conforming QR encoders may pick different masks, so what qr_test.go pins is that both sides encode the same payload at the same level and scan to the same place. 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 composer started as JavaScript; the Go authoring layer was folded in from petri-note on April 22, the day before this tag. v1.7.0 gave both an arrangement DSL — structure: drop, fadeIn: [pad,melody], drumBreak: 4, feelCurve: [...] — with arrange.js written as a port of Go's ArrangeWithOpts, so a ~1 kB recipe in the share envelope can reconstitute a multi-megabyte arranged track in the listener's tab. What is tested is narrower than "the two agree": arrange_test.go checks that the Go side is deterministic for a seed. No test compares Go and JS arrangement output. The one cross-language check in CI (test-cohesion-parity.mjs, added in June) pins the cohesion-v2 motif, not the arrangement.
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 composition of nets in the Settle sense — sub-nets are the morphisms, glued through shared connector places, and the variants for a slot are parallel morphisms with the same boundary. Mandala is the moment that composition becomes the picture on the screen. Of every shot in this post, this is the one that pays the architectural rent.
Four visualizers ship in v1.8 — Constellation, Corona, Sonar, Petal — plus Shuffle, which rotates through them every 64 transition fires (stage.js). Mandala is not a fifth mode; it is Constellation with Expand on. Four layers (Flow / Pulse / Flames / Tilt) stack on top of Constellation without rebuilding the DOM, and hide when another visualizer is active. 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.
Revised 2026-09-25, checked against the beats-bitwrap-io tags and commits. The tag counts hold: 75 tags from v0.1.0 to v2.4.0, ten of them on April 18. Four claims did not match the code and were corrected. The earlier version said the client and server QR codes were byte-identical SVG; the commit that added them says byte-identity is not asserted. It said parity tests in CI proved Go and JS arrangements byte-identical and that the composer had both implementations from the earliest tags; CI did not exist until June, its one parity test covers the motif rather than the arrangement, and the Go composer arrived on April 22. It counted Mandala as a fifth visualizer and said the Flow/Pulse/Flames/Tilt layers work on any visualizer; they are Constellation-only. And it linked the catalog to the repo's scripts/ directory and called the per-release analysis version-controlled, but neither the harness nor the analysis is in the repo. The "Show Variants" toggle is labelled ⇄ Expand in the code. This post was written on April 30 and published on August 22; since then v2.5.0 through v2.9.0 have been tagged (the latest on August 26), so "today" in the headings means April.
Cataloged from 75 git tags with a Playwright harness that is not in the public repo; the frames are reproducible from each tag's CDN URL.
Related: beats.bitwrap.io is a Jambox Now · A browser music sequencer where every note is a Petri-net transition firing · Petri Nets as a Music Sequencer · A Generator You Can Read