
RHEOME / DOCUMENT RH-02 / AMMONITE
ammonite
00Summary
A retroactive MIDI looper. It is always recording, quietly, into a buffer. Play something you like on your keyboard, and only then press a single button to keep it. You never have to press record.
It sounds trivial. It is not. This is the skeleton the whole mutax instrument is built on.
01The whole point
“The most revolutionary part of my interaction with this machine was that I never had to press record. Triggering loops happened with a simple single button press, after I played something decent. While it seems almost trivially simple, I can't stress enough how much of a difference this makes.”
I'm not a keyboard player. I can't translate what I hear straight to the keys — I feel my way around until I land on something I like. With a normal looper, by the time I've armed it and played it back, I've lost the shape my fingers were in (fingering shapes are how my brain remembers melodies). ammonite deletes that problem. It takes the anxiety out of the red record button entirely: play freely, keep what resonates.
02How it works
A Teensy 4.1 listens to the MIDI clock and to everything you play, timestamping each note into a circular buffer in external PSRAM. It is recording the whole time. When you hear something worth keeping, you press a loop-length button; it reaches back into the buffer, grabs that many bars, and loops them — applying any modifiers (transposition, probability, velocity offset, quantize) on the way out.
Getting there meant abandoning a VST and going bare-metal, then fighting instability until a from-scratch rewrite (see the log) made it rock solid: fixed buffers, interrupt-safe sections, no dynamic memory. The final layer of polish came from the PJRC forum — break the system into small testable chunks — which is how it stopped crashing for good.
03Build log
Years of trying to loop live with existing gear. Beardyman's Beardytron and Tim Exile's Flow Machine are retroactive, but they loop audio. The MIDI loopers I found were the opposite: Bastl's is three channels with no retroactive capture; X-Looper is a brilliant Max device but locked to Ableton and, its own author told me over email, can't do retroactive with the objects it's built from. Nothing existed. So I'd build it.
First tried it as a VST with JUCE. Got a rough thing kind of working — only in Bitwig, never even opened in Ableton — but juggling the UI and the actual MIDI processing was brutal. Pivoted to bare-metal C++ on a Teensy. No OS overhead, no threading, no GUI, no cross-platform anything. Just the machine.
Breadboard proof of concept: four NeoKey buttons to trigger 1, 2, 4, or 8-bar loops. It listened to Ableton's clock over USB, timestamped every keypress into a circular buffer, and looped it back. It worked — with one caveat. It usually crashed after playing back a few times.
The breakthrough was a prompt. My crashing code was AI-generated, and I realized the training data was probably drowning in web-dev JavaScript, not clean embedded C++. So I told it: assume the role of a seasoned embedded C++ developer and rewrite the whole thing from scratch, safe and robust, for a Teensy 4.1. "An idea so stupid it just might work." It never crashed again.
Hardened it properly — buffers moved to external PSRAM, interrupt-safe critical sections, per-switch debounce and double-press detection, zero dynamic allocation. Moved the interface off TouchOSC onto a real NeoKey 5×6 matrix and I2C rotary encoders (encoders over pots — infinite rotation, re-mappable). Added the features that make it musical: three overdub layers, note probability, transposition, velocity offset, pre- and post-quantize, channel select.
04Watch
05Spec
| Type | retroactive MIDI looper (hardware prototype) |
|---|---|
| Brain | Teensy 4.1 (16MB PSRAM) on a ProtoSupplies Project System |
| Input | Akai MPK Mini MKIII over USB host |
| Interface | NeoKey 5×6 matrix + I2C quad rotary encoders |
| Capture | always-on circular buffer, timestamped to clock |
| Layers | 3 overdub layers, note probability, pre/post quantize |
| Language | bare-metal C++, no dynamic allocation |
06A note from the bench
“It's hard to express how gratifying it is when you cobble together a bunch of wires and solder and code and knobs and buttons, and cross your fingers and eventually get something that does what you want it to do.”