Design modern interfaces for instruments and effects, on the web or in native DAWs. Customizable controls and themes powered by the Maru Audio platform.
The LED readout off the front of a rack unit — 7, 14 or 16 bars per cell, drawn as geometry rather than set in a font, because the thing that makes a real one unmistakable is the bars that are off: showing 1 still shows the ghost of 8. Around that: a recessed pane, a bloom on the lit bars, and light spilling onto the panel in proportion to how much of the display is lit. A plain value with a window of digits, or several captioned fields behind one piece of glass the way a multi-tap delay says what it is doing. Decimal points ride the cell before them, overflow truncates from the end the text is not aligned to, and the cells lean like every module in a rack does.
One choice out of a few as a pill track of pressed-or-not buttons on Base UI's ToggleGroup and Toggle (roving arrow keys, aria-pressed). Selection never empties; pressing the pressed option reports onReselect. The pressed option has a fill and an edge at least 3:1 against the track. sm/md/lg heights (md 28px by default) through the control-height variables, per-option tooltips and names, and a wrapping layout.
The pure core of a segment display: David Madison's ASCII mask tables for 7, 14 and 16 segments, plus the polygons that draw them. Bars are built from four numbers (width, height, thickness, gap) as mitred hexagons that meet at a gutter, diagonals are the thinner quads real glass has, and the italic lean is sheared about the baseline so the geometry and its bounding box agree. Reading a string is where the small real behaviours live — a decimal point rides the cell before it, overflow eats the end the text is not aligned to — and litRatio reports the duty cycle the panel's glow spills in proportion to.
Canonical shadcn and Base UI select primitives with shared keyboard selection, focus return and document/fullscreen-aware portals.
Responsive arrow navigation for domain Select grids; Base UI owns selection and other keys.
Shared class composition for portable shadcn controls.
Complete Tailwind instrument face: mixed-size knobs, rank faders, switches, LCD, editable filter, envelope and modulation window. Silent UI assembly.
Reusable controlled graphite fader with scoped material artwork.
A silent graphite spectral console with four editable spectrum bands, inset characters, orbital master dial, dual meters and filter controls.
Scoped graphite material, recessed choices and colored spectral geometry.
Reusable controlled graphite choice with scoped material artwork.
Reusable controlled graphite button with scoped material artwork.
A time × frequency heatmap. Realtime scrolls an analyser; static analyses PCM; prepared renders bounded host-produced magnitudes at explicit timestamps without analyzing PCM in the face. Log frequency axis, six colour ramps, and the bin-to-pixel mapping is a pure exported function you can test or render headlessly.
An FFT magnitude display as bars or a filled curve, on a logarithmic frequency axis — because a linear one spends half its width above 10 kHz and crushes the bass into nothing.
Controlled list reordering with stable identities, pointer and keyboard gestures, and cancellable previews.
Portable SVG music engraving with spelled notes, rests, chords, beams, dots, triplets, ties and producer-supplied playback colors.
Pure measure-preserving score row layout for responsive staff notation.
The LED-style chip a face uses to say what a facility is doing — AUDITION lit, HOST SYNC off, LIVE — in five states that stay tellable apart. The distinction it exists for is off (present, not engaged: label fully readable, lamp dark) versus dim (not available in this host at all: the whole chip recedes), because drawing "no telemetry from this host" the same as "telemetry switched off" tells the user something false about their session. warn is the second accent — on, but not the state you asked for — and live is the only one that pulses. Presentational and stateless: the host decides which state is true. data-status-chip and data-state let a face's render test assert on the state without matching on copy.
A compact controlled dropdown for rhythmic step divisions, styled by the UI kit theme variables.
The sequencer grid: steps across, lanes down, a page at a time. Tap a cell to toggle it, drag across a row to paint a whole stroke on or off, drag up or down on a lit cell to ride its velocity — with velocity drawn as fill height and brightness both, gate as the fill's width, ghost cells for computed previews, beat-group tinting, page tabs past 16 steps, and a playhead column driven by the host's clock, never by a display timer. Read-only it becomes a mini piano-roll, which is how the arpeggiator draws its computed line through the same cells a sequencer edits.
One row of a step sequencer, with three states rather than two: off, on and accent, because accent is a real louder hit and a two-state lane forces it into a second row the player has to keep in sync by hand. Press to cycle, or press and drag to paint the value the first step took across the row. The playhead takes a fractional position, so a face animating from the audio clock draws a line that travels instead of a light that arrives after the step has already sounded. Layout, the press/drag reading, the paint rule and the `.`/`x`/`X` pattern notation sit in a pure core beside it.
The sequencer/arpeggiator pattern model and the grid it draws on, as pure maths: held notes walked up, down, up-down (endpoints unrepeated), in press order or by a seeded per-step hash — transcribed from the shipped arpeggiator and pinned bit-identical against it — plus octave stacking, pattern rotation, a sequence-to-lanes resolver for read-only piano-roll views, page arithmetic, an exact steps × lanes tiling with inverse hit-testing, and the tap/paint/velocity-drag gesture maths. No React, no DOM — testable in Node and safe to lay out on the server.
Retained-resolution timeline math for step sequencers. Coarser divisions hide fine cells without deleting them.
The parameter-side picture of a stereo image: an L-R field with a pan cursor the host can animate from an LFO phase, the width parameter drawn as a spread around it, mid/side balance bars, and a correlation meter whose lower half is tinted because correlation below zero is the mono-compatibility danger zone. Takes precomputed values, a live analyser pair, or a getStereoData callback. Pairs with lissajous-scope, which is the signal-side view of the same signal — this one is not a goniometer and does not try to be.
What a stereo image is doing, as pure numbers: correlation at lag zero on the meter convention (no mean removal), mid/side energies and the width ratio they imply, an L-R energy balance, the constant-power pan law Web Audio specifies for a mono input, framerate-independent meter ballistics, and the field geometry a pan cursor and a width spread are drawn with. Every function has a defined answer for silence, empty buffers, mismatched lengths and non-finite samples, and returns a finite number for all of them. No React, no DOM — testable in Node and safe to draw with on the server.
The spectrogram's whole-buffer short-time Fourier transform, forwarded from @maruaudio/audio-sdk/analysis: the canonical radix-2 FFT, paired stereo transform, a Hann-windowed whole-buffer magnitude spectrogram in dB, and a planar-to-mono down-mix. Pure functions over typed arrays — no canvas, no DOM — so an entire clip's time × frequency grid can be computed and tested headlessly.
Shared screen-space sizes and round-cap paths for circular SVG markers in stretched plots.
A controlled category/keyword chip browser in the style of a hardware preset browser: a column per category from 560 px and stacked, wrapping categories below (no horizontal scroll at 360 px), per-chip match counts, zero-count chips disabled in browse mode, single-select replacement, an up-to-n limit that refuses and announces, per-category and global clear, and an optional search filter. ARIA group/checkbox semantics with one roving tab stop; arrows move across columns and within them, Space/Enter toggle. Quiet, Panel and Glass skins over the same headless useTagBrowser hook, with the rules as a pure unit-tested model.
Tag Suggestions shared by module faces and application hosts.
Generated variations as controlled tiles (A, B, C…) with a mini waveform, a badge slot, progress while generating, an error with retry, and pin and regenerate buttons whose refusals disable with a reason. A radiogroup with one tab stop: arrows, [ and ] (inside the switcher or window-wide, never in text fields) and previous/next buttons cycle selectable takes. Tiles wrap to the width. The host owns generation, pinning and what selecting does.
A silent molded rhythm console with recessed volume fader, dimpled tempo dial, glossy levels, patterned lamps and power rocker.
A read-only multitrack piano roll of a prepared timeline: ruler with cycle numbers, a section strip, pitched rows and a compact drum strip with General MIDI names. Sized by CSS (SVG paths stretched with preserveAspectRatio none, HTML labels in percent, no height prop); fit view samples to a note cap, detail view scrolls at 72px per cycle, culls to the viewport and follows the playhead unless the reader just scrolled or prefers reduced motion. The playhead is a leaf on a PlaybackClock moved by transform, so playback never re-renders the notes. Stale state dims it and says 'previous'. TimelineRollLegend adds swatches, mute/solo toggles (aria-pressed, 24px, Alt-click solo), channel numbers below 480px and an actions slot. Track colours are --uikit-track-1..8, starting at blue. Pure core beside it.
The layout of a plucked-tine board as pure maths: the kalimba's centre-out fan and the music box comb's monotonic run, tine lengths from the clamped-free bar's square-root law, and one slot table driving the SVG paths, the hit test and the tip-to-bridge strike reading. No React, no DOM — so the ink and the touch targets cannot drift apart, and the whole thing is testable in Node.
Toggle shared by module faces and application hosts.
Toggle Group shared by module faces and application hosts.
The unstyled switch primitive a toggle is built on: controlled and uncontrolled state, the full role="switch" contract, the click and keyboard paths, and the pressed flag every hardware-style face needs. Renders no appearance — a render prop hands your face the state.
A row of on/off cells for choosing several values from a range, such as the notes of a chord on a keyboard strip or the semitones of a set, on Base UI's ToggleGroup with multiple selection: one Tab stop, arrow keys, Home and End to move, Space or Enter to toggle, aria-pressed and a label on every cell. Cells are at least 24px wide and as wide as the face's small control height (44px on a touch face), black keys follow the accidental pitch classes unless told otherwise, markers under cells start groups, on cells use the neutral --uikit-value colour, and the lane scrolls sideways when it overflows. The selection maths sits in a pure core beside it.
On-off in three looks: a sliding rocker, a round stomp footswitch with a status LED, or a vertical hardware lever in a recessed metal slot.
Two tonic models as pure functions. Krumhansl-Schmuckler correlates a 12-bin pitch-class histogram against all 24 rotated major/minor profiles to name the key, then interpolates toward the runner-up so an ambiguous passage lands BETWEEN two keys on the circle of fifths rather than snapping to one. The consonance model instead scores each candidate root by how consonant the histogram sounds above it — mode-blind, but it needs no trained profile and works on material the KS profiles have nothing to say about.
What a waveshaper does to a sample, drawn: input amplitude across, output up, and the gap to the identity diagonal is the distortion at true scale. A staircase treatment draws a quantiser's real decision boundaries rather than faking diagonals through them, an optional bar row names which harmonics a Chebyshev order produces, and a live overlay brightens the stretch of the curve the signal is currently exercising.
The transport bar, composed rather than reinvented: play, stop, loop and an optional record built from glow-button's toggle and momentary modes, a monospace tempo readout you drag, wheel, arrow or double-click to reset, a tap-tempo key that keeps only timestamps, and a segmented note-division picker printing the current division's length and rate at the current tempo. The beat indicator pulses from a phase or beat prop and nothing else — the host owns the clock — and every control independently becomes a lamp when its callback is absent, so with no callbacks at all it is a status display. The tempo readout has a second face: `tempoDisplay="segment"` puts the seven-segment display off a drum machine's front panel behind the same drag, wheel and arrow keys.
A ternary control surface driven by two normalised inputs. The default apex/base formula uses Y for distance from A and X to balance B against C. An alternate formula maps 00, 01 and 10 to the corners and 0.5,0.5 to the equal blend. Both report the same post-computed three-corner weights.
Reusable pale lavender material with controlled state.
Audio device permission, enumeration, selection and capture in one controller. `enable()` is the whole flow in one call — ask permission, enumerate, resolve the device, open the stream — so a microphone can be one tap anywhere. Permission is modelled explicitly (unknown / prompt / granted / denied / unavailable) instead of inferred from failure, output sinks are feature-detected, and the remembered choice is restored without ever prompting on load.
The input contract every audio-fed display shares — an AnalyserNode or a getData callback — plus a HiDPI canvas draw loop that pauses when the tab is hidden and re-measures rather than trusting the first frame's geometry.
Where a tempo-synced face's playhead is, and — just as important — whose clock that position came from. A host transport that is PLAYING drives the phase from its ppqPosition, so the window tiles from the project start and the same musical position always draws at the same x; anything else free-runs from a bpm on a requestAnimationFrame loop, and `running: false` parks at phase 0 rather than drifting. The returned `source` is "host" | "free" | "stopped" precisely so a caption can say which, instead of implying a sync the face does not have. The phase mappings, the source rule and the tempo fallback are pure exported functions, pinned in Node; the hook is only the loop around them, and nothing touches requestAnimationFrame outside an effect, so the first render is phase 0 on the server.
The interaction core: controlled/uncontrolled value state, pointer-capture dragging, and keyboard stepping with fine and coarse passes.
React bindings for the SDK's DiagnosticSession: useSyncExternalStore over the session's own snapshot/subscribe pair, ownership tied to the component's lifetime, and scope readers that adapt session.readScope to the getData contract the kit's scopes already take. Scope frames deliberately never enter the snapshot — a 512-sample buffer per tap per frame is exactly the allocation a UI must not diff — so they are read into caller-owned buffers inside the draw loop instead.