Start blank
An empty engine with the contract and package skeleton already wired — the four controls, scenarios, and the local test bench — leaving just an empty Render() to fill.
Contribute a model
Every model here is a package in the same format, built on the same four-control contract. This page walks the whole path in order — from cloning the toolkit to a submitted package. Follow it top to bottom; once you name your model, every command fills itself in.
The plaits-lab CLI lives in the open firmware repo. Clone it, pull in the stmlib submodule the build links against, and run every command on this page from that eurorack/ folder.
git clone https://github.com/lylepmills/eurorack.git cd eurorack git submodule update --init stmlib
Prerequisites: Python 3 and a C++ compiler (clang or gcc). The plaits-lab CLI has no other dependencies.
For real-time live audition — the recommended way to hear your engine as you tweak it — also install Emscripten once. It compiles your engine to WebAssembly so the audition page plays it continuously, with no render step.
git clone https://github.com/emscripten-core/emsdk.git cd emsdk && ./emsdk install latest && ./emsdk activate latest source ./emsdk_env.sh
Run source ./emsdk_env.sh in each shell before plaits-lab dev. Skip this and dev still works with a render-and-listen page; live audition just won't appear.
Two answers personalize every command below — nothing to hand-edit later. The author name goes on the package, on its MIT license, and in the copyright header of every source file the toolkit writes for you. (Prefer BSD or ISC? Add --license to the create command — a fork keeps whatever its original used.)
Package folder my-engine/ · id community/my-engine · C++ class MyEngineEngine — placeholders until you name it
Pick the color this model shows in the catalog. It's a catalog identity — on the module the LED still follows whichever bank you place it in (green, red, or amber).
Two ways in — either way you'll shape Render() yourself. Start from an empty engine, or copy a working model and reshape it (an example just gives you working bones to read and lean on).
An empty engine with the contract and package skeleton already wired — the four controls, scenarios, and the local test bench — leaving just an empty Render() to fill.
Copy a working engine's source into a package of your own and reshape it — best when something in the catalog already has bones close to your idea.
Pick a starting point in step 3, and your create command appears here.
Start the local server. It compiles your package and serves its own audition page on your machine — open the http://127.0.0.1:4179/ link it prints. With Emscripten installed you get a real-time live audition: continuous playback where every control, pitch, envelope, and Strike is heard instantly, with a live scope and spectrum. (No Emscripten? The same page falls back to render-and-listen.) Same origin as the tool, so nothing to connect and no browser-permission hoops, and your source never leaves your machine.
Models are mono by default. If your engine renders a true stereo image — reporting stereo_capable() and honoring parameters.stereo to write OUT/AUX as a left/right pair — the audition's Stereo toggle lights up and plays that image; a mono engine leaves it greyed out. Stereo is opt-in, so a mono idea never has to fake one.
# from inside your eurorack checkout python3 alt_firmwares/plaits_lab_sdk/plaits_lab.py dev ./alt_firmwares/plaits_lab_sdk/packages/community/my-engine
Your whole instrument is alt_firmwares/plaits_lab_sdk/packages/community/my-engine/src/my-engine_engine.cc — the MyEngineEngine class. Render() receives the note, trigger, and accent plus the four panel controls, and writes the MAIN and AUX outputs in 12-sample blocks. Edit and save with the audition page open — it recompiles on its own and says so, and with live audition you hear the change without touching anything. A/B against any built-in model to keep your ears honest.
Need the chord tables, the string or modal physics, or the six-op FM core? They're shared modules — just #include their header and check/dev wires it up for you (it adds the entry to plaits-engine.json's sharedModules and says so). plaits-lab modules lists what's available.
The full check compiles your source with sanitizers, runs every declared scenario, and screens the audio for silence, DC offset, and runaway peaks — the same gates a submission has to pass, so there are no surprises later.
# from inside your eurorack checkout python3 alt_firmwares/plaits_lab_sdk/plaits_lab.py check ./alt_firmwares/plaits_lab_sdk/packages/community/my-engine --full
That compiles with your host compiler, which is more permissive than the module's pinned ARM toolchain — code can pass here and still fail the hardware build (e.g. std::log2/std::exp2, which the ARM newlib doesn't put in std::). Once you've set up the ARM toolchain or the builder image (next step), add --arm to also compile against the real hardware toolchain and catch those early — no full firmware build needed:
# from inside your eurorack checkout python3 alt_firmwares/plaits_lab_sdk/plaits_lab.py check ./alt_firmwares/plaits_lab_sdk/packages/community/my-engine --full --arm
The check also estimates your model's CPU cost against the module's ~1,500-cycles-per-sample budget, and qemu/estimate.py can turn that into a calibrated band ("79%, likely 68–101%") before you ever flash. The full story — how to read the on-module LED meter, what actually costs cycles on this chip, and the optimization playbook that took one engine from 145% to 61% — is in the SDK's performance guide.
Hardware is the final authority; an estimate is only a pre-flight check. You can test your model without review by building an audio-install WAV and flashing it like any other Plaits alternative firmware.
The build contains only your model, loaded into the first slot with the remaining banks empty. It also includes --cpu-probe, which turns the LEDs into a live CPU meter with roughly 1% resolution and blinks red if your model risks missing the audio deadline. See the performance guide for details.
Your model plays through MAIN and AUX exactly as it will in a finished palette. A one-model build also gives your model nearly all of the module's flash, so even a large model should fit. Keep in mind, though, that larger models leave less room in a full 24-model palette. We recommend keeping yours light; for comparison, Speech is the largest stock model at 22.8 KB.
One-time prep: this step needs the stm_audio_bootloader submodule (audition and checks don't) and, unless you already have the ARM 4.8.3 toolchain locally, Docker plus a one-time builder image. The image build downloads the toolchain and warms a compile cache, so it's slow the first time only; after that, builds are quick.
# from inside your eurorack checkout git submodule update --init stmlib stm_audio_bootloader docker build --platform linux/amd64 -t plaits-lab-builder:local -f Dockerfile.plaits-builder .
# from inside your eurorack checkout python3 alt_firmwares/plaits_lab_sdk/plaits_lab.py build ./alt_firmwares/plaits_lab_sdk/packages/community/my-engine --hardware --cpu-probe --output my-engine-firmware.wav
This is an unreviewed local build, for your own hardware. The hosted builder only ever ships approved, published packages.
One command does the whole thing: it runs the full check gauntlet, bundles the exact source with deterministic preview renders, shows you what is about to leave your machine, and — once you confirm — uploads it and sends it to review. (--bundle-only builds the zip without submitting, for a look inside or for CI.)
# from inside your eurorack checkout python3 alt_firmwares/plaits_lab_sdk/plaits_lab.py submit ./alt_firmwares/plaits_lab_sdk/packages/community/my-engine