OpenGroove Toolkit (proposal, pre-review)
Status: design proposal for review. Not implemented yet.
Why
Vibe-coding agents (and humans) rebuild the same pieces in every firmware: menus, lists, pagers, parameter pages, step grids, clocks, envelopes, voice allocation. Hand-written versions are where most bugs and real-time violations come from. A small, proven toolkit makes firmware faster to write, safer, and more consistent across devices.
Principles
- A library, not the ABI. The ABI (
opengroove.h) stays minimal and stable; it's what hardware runtimes implement. The toolkit is header-only C (og_ui.h,og_music.h, …) that firmware compiles in statically. It's versioned separately, and the host never needs to know about it. - Device-agnostic through semantic input. Widgets consume intents, not physical buttons:
NAV_NEXT/PREV,NAV_UP/DOWN/LEFT/RIGHT,SELECT,BACK,VALUE_DELTA(d),TOUCH(x,y),PAGE_NEXT/PREV,SHIFT(modifier). A per-profile input map (data, overridable by the firmware) turnsog_events into intents: encoder/jog → VALUE_DELTA/NAV, touch → direct hit-testing, buttons → NAV/SELECT/BACK. No KICK-specific assumptions: the generic, KICK-class, Fire-class and future profiles are just different maps and display classes. - Adaptive layout across display classes. The display class comes from og_device:
LARGE_COLOR(≥320 px wide, RGBA),SMALL_MONO(≤128x64, MONO1),NONE(headless). Widgets carry two renderings (rich and compact) and a common model. Pads stay a separate surface (pad grid helpers), because not every device has a screen. - Real-time safe by construction. No allocation (caller-provided storage or fixed capacity set at init); no work in
og_processbeyond music primitives; UI work only inog_render/event handling; render is idempotent and never advances musical state (ABI §9). - Testable headless. Every widget is drivable through intents and renderable to a framebuffer in
og render, so agents can verify UI through PNG snapshots.
Modules
og_gfx.h(exists in examples/common; promote and stabilise it): framebuffer abstraction over RGBA8888/RGB565/MONO1, clipping, rects, lines, bitmap fonts (two sizes plus a mono-OLED font), icons, dirty-rect tracking.og_ui.h: an immediate-mode-style widget set with a retained focus model:- list view (scroll, selection, long lists via a callback data source), pager/tabs, menu (nested, with back stack), parameter page (binds to
og_param_info: name, value, unit, stepped vs continuous), value editor (encoder or touch drag), confirm dialog/toast, text label/marquee, meter, waveform/envelope view, step grid (N steps x M rows), piano-roll strip, XY pad (touch). - focus and navigation: a focus ring, a back stack, a shift layer.
- theme: tokens for colour and spacing per display class.
- list view (scroll, selection, long lists via a callback data source), pager/tabs, menu (nested, with back stack), parameter page (binds to
og_input.h: event → intent mapping tables per profile, plus helpers for pad grids (index ↔ row/col, banks, LED colouring) and shift/hold gestures (tap vs hold, double tap) with timing done in samples.og_music.h: a clock (tempo, swing, PPQN, followsog_process_ctxtransport and sync), a step sequencer core (patterns, length, probability, ratchet, slide/accent flags), scales/quantize, an arpeggiator, a voice allocator (poly/mono/legato, stealing), and sample-accurate event scheduling inside a block.og_dsp.h(exists partially): oscillators (band-limited saw/square, sine, noise), SVF/ladder-ish filter, ADSR/AD envelopes, a one-pole smoother for param changes (sample-accurate), drive/soft clip, a simple delay/reverb, a sample player (works with og assets), denormal suppression.og_state.h: helpers to serialise and deserialise with the 16-byte envelope, schema versions and migrations.
Templates
og new --template {blank, drum-machine, mono-synth, sampler, sequencer-only}: each one is a small but complete firmware built from the toolkit, adapting to every profile. AGENTS.md teaches agents to start from a template and compose widgets instead of drawing pixels.
Non-goals
No device-specific widget sets, no retained scene graph, no dynamic allocation, no host-side UI rendering, and no mandatory use (raw ABI firmware stays first-class).
Open questions for review
- Immediate mode or retained, given wasm size and agent ergonomics?
- Is header-only C right, or should it be a static library (
.a), given code size per firmware (~42 KiB today)? - Is the intent set sufficient for future devices (rotary-only, touch-only, many-encoders, keyboards)?
- How to version the toolkit independently from the ABI and keep old firmware rebuildable?
Addendum (2026-10-27): DSP sourcing and Faust
- Faust → C is a supported route into firmware:
faust -lang coutput plus a thin og wrapper and anog build --faust file.dsppath. This lets more-fire's Faust voices (808/bassdrum, slicer, monosynth, kit, mixer, all in ~/dev/akai-fire/more-fire/audio-core/src/faust) become OpenGroove showcase firmwares. - Curated, permissively licensed DSP vendored into og_dsp, each with its licence verified per file before inclusion: Mutable Instruments (MIT), DaisySP (MIT), Airwindows (MIT), Soundpipe (MIT), msfa FM (Apache-2.0), Faust standard libraries (check each library's licence header). NO GPL code in the SDK or toolkit (keeps Apache-2.0 clean).
- Each vendored block gets a toolkit wrapper (caller-owned state, no allocation, sample-accurate params) plus a golden render test, so agents can compose them safely.