Marginalia, Side Effects & Loose Threads
Inside the factory: how SwarmQuarium was built
SwarmQuarium replays a month of my coding agents as an aquarium: every session is a fish, every repository a reef, every commit a pearl sinking into it. This post is about how it was made, and the short version is that I didn’t type much of it. A fleet of Claude Code and Codex sessions built it over three days, and the same records it draws are the ones I used to write this.
A Sunday with no plan
It started on a Sunday morning with a few hours of weekly token budget left and nothing scheduled. I asked a session for ideas, picked three and ran them side by side from one Control Tower board: the aquarium, a numbers demo, and what became the Portfolio Galaxy on this site’s homepage.
I wanted an entertaining way to watch my agents and their data: something to look at instead of a boring graph, or even more boring raw data.
Version one in five minutes
The first version took about five minutes. In that time an Opus session wrote an extractor that replays the coordination board’s logs with the message bodies, file paths and evidence stripped out, a 378-line page and a four-line build script, and published it as a private artifact. Even that first version had a fish per session, bubbles for messages, pearls for commits, and successors hatching on a handoff.
Its first data bug showed up within the hour. I looked at the tank and said the git activity looked like dozens of commits, when I knew it was thousands. The session found two causes. The extractor only counted commits that Conclave had logged itself, 226 of them, and the page capped each reef at 60 pearls. Reading git log --all on every repository instead gave 7,959 commits. About three quarters of them could be traced back to a session through a trailer in the commit message.
The lesson stuck: a coordination board records what agents say they did. Git records what they did.
Fifty-one versions in a day
By the evening the flat page was at version 51 and a 3D version at 27. That’s roughly one new version every nine minutes, all day.
Most of that wasn’t one session typing faster. The main session, also Opus, split the work into lanes. Each lane was a subagent working in its own copy of the folder, time-boxed to about 30 minutes, handing back a patch for the main session to merge. There were 13 of them: ten Opus, two Fable and one Sonnet. They covered:
- 3D realism and 3D parity with the flat page;
- spatial audio, wind and night sounds, and sounds for each kind of commit;
- a “jellyfish lens” that shows every open question, and a “why?” magnifier that shows the raw event behind anything you click;
- export, pinch-zoom and contrast;
- every LLM I use, subscriptions and local models included;
- a read-only privacy audit;
- easter eggs, which the event feed labels as decoration so they’re never mistaken for data.
All of this was a race against the weekly token reset. What was left of the week’s budget had to be spent before it ran out, so I steered mostly by that: “Send another Fable agent.” “15 minutes left.” “5 minutes.”
Along the way:
- The scratching jazz. The default style, Jazz Reef, started to crackle. The session blamed a bass sample pitched down too far and swapped in a synthesized bass. I said it sounded worse and slower. The real cause was an overloaded audio engine dropping buffers, and the extra bass voices had made it worse. Bigger buffers and fewer voices fixed it. The agent noted, fairly, that it couldn’t hear the result itself.
- The apostrophe that emptied the tank. One “developer’s” inside a script string broke the page, and the tank rendered as plain dark blue. The build now refuses to finish if any script on either page fails to parse.
- The firewall popup. A lane started a local test server on every network interface, and Windows asked me whether to let it through. I said no, and the agent rebound it to 127.0.0.1.
- The phantom rectangle. A floating vertical bar I reported three times. Two “fixes” went to a session other than the one I was talking to and changed other things: a manta silhouette came out, and a wall photo was cropped. It hasn’t been found yet. I parked it: too many other sessions had work in progress for me to bother.
The name came mid-afternoon. I suggested SwarmQuarium, a session checked the web for anyone already using it, found no one, and renamed both pages.
Making it public
The private tank shows real session handles, exact times and spend, so the public one is a separate build. Session names become species pseudonyms, times become dates, spend is rounded, and a scan reads the finished page for anything that must never appear before it’s allowed to deploy.
The scan’s first run flagged three things, all false positives:
- a variable called
carin the FM synthesizer; - a URL pattern that swallowed a semicolon;
- the domain name in a comment in the headers file.
Changing what the scan allows needs my explicit OK, and the agents ask for it.
Before any session touched the shared build code, it recorded checksums of the existing builds and proved afterwards that they came out byte for byte the same. The public page went up on swarmquarium.robderoo.com on the second day.
Live, and the music
I also wanted a tank for myself that keeps up with the fleet. SwarmQuarium Live is a private page that can take a new month of data without being rebuilt: a refresh uploads a snapshot, and an open page offers an Update button. A refresh went from about two minutes to about ten seconds once the extractor learned to reuse what hadn’t changed, with byte-identical output.
The sound grew into its own project. Swarm Radio composes the same month into music, and Live uses it as its soundtrack. Two Codex peers built its engine and adapters in their own worktrees while Claude sessions built the composer. When a stress run found that about 1 in 90 simultaneous note pairs clashed, the fix checked every note against what was already sounding. That run is now a script in the repo.
Two kinds of bug only showed up once I used it every day:
- Memory. A tab sat at about 1.2 GB. A cache of tinted fish sprites kept 24 copies per size and grew to gigabytes; it’s now capped at 64 MB. The sample bank was being decoded again for every player, and now it’s decoded once.
- Hidden tabs. The pane I watch it in never reports the tab as hidden, so animation simply stopped and the replay froze. The replay now advances on a timer whenever frames stall.
Codex builds, Claude publishes
On the third day a Codex session built Live’s evidence view, the panel that shows the raw request behind an open question. Codex can’t publish to the artifact itself, so when the work was done and reviewed, it asked a Claude session through Conclave to publish it. The Claude session published it and sent back a receipt.
The review found a real bug on the way. Session IDs were being lowercased, which could match a request to the wrong session. A regression check guards it now.
The numbers
| Elapsed, first version to the latest Live publish | a little over two days |
| Agent sessions involved | 32: 13 top-level Claude sessions, 13 Claude subagents, 6 Codex sessions |
| Models | Opus for every top-level Claude session; Fable and Sonnet as subagents; Codex for the evidence view and Swarm Radio’s engine |
| Published versions | 106 (flat 57, 3D 28, Live 18, open-source demo 3) |
| Source | about 11,000 lines in the private build; the open-source repo started at 78 files and 6,407 lines with 27 tests |
| Data | 7,959 commits in the first tank; about 20,000 events in a 30-day Live window |
Four words
Jellyfish in the tank are questions one agent asked another that are still waiting for an answer. One day I told an agent there had been too many jellyfish yesterday. That was the whole bug report. From those four words it went looking and found five bugs in Conclave.