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

  1. 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.
  2. 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) turns og_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.
  3. 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.
  4. Real-time safe by construction. No allocation (caller-provided storage or fixed capacity set at init); no work in og_process beyond music primitives; UI work only in og_render/event handling; render is idempotent and never advances musical state (ABI §9).
  5. 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.
  • 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, follows og_process_ctx transport 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 c output plus a thin og wrapper and an og build --faust file.dsp path. 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.