ui-builder.js — UI Components
Generates the per-module parameter control cards, handles canvas drawing for waveform and envelope visualisation, and synchronises lock states.
Module Sections
buildModuleSection(container, mod, params, lockedParams, onChange)
Creates a card for one module. Each card contains:
┌─────────────────────────────────────────────┐
│ Module Label [🔓] [🎲] ┌──────────┐ │
│ Param Label ═══●═════ val [🔓] │ canvas │
│ Param Label ══○═══════ val [🔓] │ (if │
│ ... │ mod. │
│ │canvasId│
│ └────────┘ │
└─────────────────────────────────────────────┘
- Section lock button (🔓/🔒) toggles all params in that section
- Randomise button (🎲) randomises unlocked params and calls
onChange - Each param row has label, control (range/select/checkbox), value display, lock
- Optional inline canvas to the right of controls when
mod.canvasIdis set. Setmod.canvasExternal = trueto suppress inline canvas creation — the canvas is placed in the HTML template instead and drawn bydoRender()via its ID.
Supports three control types:
- type: 'range' → <input type="range"> + value <span>
- type: 'select' → <select> with options
- type: 'checkbox' → <input type="checkbox">
For select params, set ui: 'pills' to render the options as clickable
pill buttons instead of a dropdown:
pitchCurve: { type: 'select', options: ['exponential', 'linear'], default: 'exponential', label: 'Curve', ui: 'pills' }
syncModuleUI(container, modules, params)
Syncs UI controls ([data-key] elements) with current param values. Used after
randomise or preset/template load. Builds a param-def lookup from modules for
proper formatValue rendering (units, note names).
syncLockUI(container, lockedParams)
Updates all per-param lock buttons ([data-lock-key]) and section lock buttons
(.section-lock) to reflect the current lockedParams state. Used when
a template is applied or locks are cleared programmatically.
Canvas Drawing
drawEnvelopeCurve(c, fn, colour)
Draws a normalised envelope curve on a 2D canvas context. fn(t) receives t
in [0, 1] and returns a value in [0, 1]. The curve is drawn with:
- Theme-aware background (dark/light)
- Light horizontal grid lines
- Filled area under the curve at reduced opacity
- Stroked curve at full opacity
- "time" label at bottom-right
drawWaveform(c, samples)
Draws a PCM waveform as a centred line graph. samples is a Float32Array.
Down-samples to match canvas width (buffer width, not CSS width).
Auto-scales to the peak sample so the waveform always fills the full
canvas height regardless of signal level (uses 95% of height, leaving 5%
padding at top and bottom, clamped to ±1.0 to prevent clipping). Theme
colours are applied by the caller.
isDarkTheme() → boolean
Returns true when data-bs-theme attribute is "dark".
formatValue(pd, val) → string
Formats a param value using its definition:
- Appends unit string (Hz, s, dB) where present
- Hz values show nearest note: "220 Hz (A3)"
- Non-unit values get 2 decimal places
- Log-scale Hz params (marked log: true) show 2 decimal places for fine
control at low rates (e.g. "0.75 Hz")
Slider types
| Property | Effect |
|---|---|
log: true |
Maps slider position logarithmically for fine control at low end of range |
step: <number> |
Explicit step size (default: (max-min)/200) |
Section Info Popovers
Modules can include a description field. When present, an ⓘ button is
appended to the section title bar. Clicking it opens a Bootstrap popover
with the description text. The popover is dismissed by clicking outside or
pressing Escape.
{
id: 'osc',
label: 'Osc',
description: 'Two main oscillators with FM routing, pulse width modulation, per-oscillator wavefolding, and stereo spread.',
params: { ... },
}
Param Label Tooltips
Each param definition can include a description field. When present, the
param's label <span> gets a native HTML title attribute, showing the
description as a tooltip on hover.
osc1Fold: { min: 0, max: 1, default: 0, label: 'Fold1',
description: 'Sine-based wavefolder on Osc1 adds harmonic richness before the filter.' }