Skip to content

Search documentation

Enter a title, API, or topic.

    ↑ ↓ NavigateEnter Open resultEsc Close

    UI Kit

    @webmusic/ui supplies music controls, readouts and layout surfaces that you can mount into your own interface. A presenter reads a small binding to your state, routes interaction to your commands, and owns the DOM and cleanup for its view.

    The package has no runtime dependencies or required peers. It creates no audio resources, registers no Custom Elements and requires no framework or theme provider. Score’s visual Elements compose these same presenters with their musical behavior.

    Install the npm package in your application:

    Terminal window
    npm install @webmusic/ui

    Add a host to your HTML:

    <div id="level"></div>

    In a TypeScript module processed by your application’s bundler, mount a parameter control against a value your application owns:

    import {mountParameterRack} from '@webmusic/ui/parameter';
    const host = document.querySelector<HTMLElement>('#level')!;
    let level = 50;
    const handle = mountParameterRack(host, {
    snapshot: () => ({
    parameters: [
    {id: 'level', label: 'Level', value: level, min: 0, max: 100, step: 1, unit: '%'},
    ],
    }),
    setValue: (_id, value) => {
    level = value;
    },
    });
    // When your route or component unmounts:
    // handle.destroy();

    The control displays Level 50 % and supports pointer and keyboard input. snapshot() supplies the current value and bounds; setValue() updates the application state. The presenter reads the snapshot again after that command. The example changes a value only. To control sound, connect the command to an application-owned player, synth or other engine.

    When something else changes the value, call handle.update() or provide the binding’s optional subscribe(notify) so the view can refresh. Call handle.destroy() when removing the view; it releases its DOM, listeners and subscription without disposing your state or engine. See parameter for the complete contract.

    Integration Use it when Start here
    Web Components You want ready-made tags that combine musical behavior with UI. Quick Start and Score setup
    Headless Your application supplies every visual and reuses WebMusic’s musical behavior. Score Headless
    API + UI You want independent presenters bound to a store, controller or your own model. The example above and the presenter Catalog

    These choices can coexist. Subclassing an exported Score Element is an advanced customization of the Web Component path; it is optional. Direct presenter mounts need no Element class, tag registration or @webmusic/score dependency.

    Group Use it for
    Transport and navigation Playback, seeking, visible ranges, playlists and row selection.
    Parameters and modulation Parameter racks, macros, envelopes, LFOs and EQ curves.
    Mixing and capture Mixer faders, level/spectrum meters and recording controls.
    Note input Playable piano, grid and chord surfaces.
    Views and analysis Passive pitch and harmony readouts, reports and analysis output.
    Layout and feedback Panels, render hosts, workbench shells and loading/error feedback.

    note owns input and note-release behavior; pitch displays supplied pitches. analysis supplies report renderers; harmony supplies changing analytical readouts. Choose by interaction and lifetime even when two surfaces have a similar appearance.

    Applications theme presenters through inherited --wm-* custom properties:

    :root {
    --wm-component-background: #fff;
    --wm-component-border: 1px solid #d8d8d8;
    --wm-component-radius: 0;
    --wm-foreground: #171717;
    --wm-foreground-muted: #666;
    --wm-accent: #111;
    --wm-accent-foreground: #fff;
    --wm-font-family: system-ui, sans-serif;
    }

    Changing a custom property does not remount a presenter, reset playback or rebuild an audio graph. No theme provider, registry or JavaScript runtime is required.

    Presenters expose a shared outer-surface contract. Most default to a neutral card; Harmony, Pitch and a bare Workbench default to an unframed transparent surface. These four inherited tokens customize either form without changing the binding or remounting:

    Token Typical fallback Controls
    --wm-component-background --wm-surface, then presenter default Outer background; accepts transparent.
    --wm-component-border 1px solid #d8d8d8, or 0 for an unframed surface Complete border shorthand, including 0 or none.
    --wm-component-padding .6rem, or the presenter’s existing inset Space between the outer surface and its content.
    --wm-component-radius 0 Outer component corner radius.

    Use the global names for a coherent application surface, or the corresponding --wm-<presenter>-surface-background, -border, -padding, and -radius names for one presenter (for example --wm-transport-surface-background). Older component variables such as --wm-stage-background and --wm-meter-background remain supported for their existing inner viewport or compatibility role. --wm-surface, --wm-border, and --wm-control-radius remain semantic palette fallbacks. --wm-control-border is the shared line color for non-primary inner controls; it falls back through --wm-border to #d8d8d8. Primary actions, active states, and focus rings keep their own stronger tokens instead of inheriting that soft line. The --wm-component-* quartet is the direct outer-surface API.

    Set --wm-foreground for text and drawing ink, --wm-foreground-muted for secondary text, --wm-accent for active fills and --wm-accent-foreground for their labels. Surface tokens never intentionally provide selected text colour. In particular, transparent Analyze backgrounds no longer make selected labels transparent; set the explicit accent foreground to customize them. Musical palettes (--wm-degree-*, --wm-progression-tone-*), inverse surfaces, note-key faces, and loading overlays retain their dedicated tokens so independent visual roles remain legible. Envelope, EQ and LFO graphs pair --wm-surface-inverse with --wm-foreground-on-inverse; generic surface and foreground values do not recolor their inverse plots. Set both inverse tokens when changing that palette. Use --wm-font-family for UI text and --wm-font-mono for fixed-width readouts; presenter-specific font overrides retain precedence.

    For one transparent outer frame, set --wm-component-background: transparent. To also clear ordinary inner surfaces, set --wm-surface: transparent; an explicitly configured inner or inverse surface still takes precedence. --wm-border is a colour for inner edges, while --wm-component-border is a complete outer-border declaration.

    When a Workbench or another presenter already owns the outer frame, pass surface: 'none' to nested Harmony or Pitch mounts. It suppresses only their outer background, border, padding and radius, on both stylesheet paths; their musical marks, controls and palette remain active. This prevents duplicate borders and translucent backgrounds being painted twice. Workbench’s public frame part is the painted box; its root remains the unpainted query container.

    stylesheet: false always skips installation, including owned child presenters. Harmony, Pitch, Workbench and Transport retain inline declarations; other presenters expect the caller to supply the exported stylesheet or replacement CSS. Pseudo states, focus rules, native control pseudo elements and container queries require stylesheet rules even when inline geometry is supplied. Private --wui-* properties are implementation or data values; new themes should use --wm-*.

    Shared control tokens establish the default size and radius for row controls such as transport buttons and seek controls. Presenter-specific overrides and the control’s function can require different geometry.

    Token Default Controls
    --wm-control-size 2rem Shared row-control height where the presenter supports it.
    --wm-control-radius 0 Shared control corner radius; square is the default.

    A vertical fader’s length sets its travel, a rotary dial retains its circular shape, and note keys preserve their musical layout. Consult the presenter’s Styling section for its size overrides and minimum dimensions.

    A drawing surface is not a control either. An envelope, an EQ curve, an LFO waveform, a piano board and a timeline lane need room, and how much room is an editorial choice, so they share one scale instead of each carrying its own number:

    Token Default Used by
    --wm-surface-sm 72px LFO waveform, timeline lane.
    --wm-surface-md 144px Envelope, EQ curve, piano board.
    --wm-surface-lg 216px Available for a taller drawing; no presenter defaults to it.

    Set a tier to re-proportion every drawing at once, or give one presenter any length you like through its own --wm-<presenter>-height — the tier is only the default behind it.

    Customization is progressive rather than all-or-nothing:

    Need Use
    One coherent application theme inherited semantic --wm-* custom properties
    Component-specific adjustment --wm-transport-*, --wm-parameter-*, --wm-envelope-*, --wm-lfo-*, --wm-meter-*, --wm-stage-*, --wm-status-*, --wm-minimap-* properties
    Style internal Shadow DOM nodes documented ::part(...) hooks
    Preserve an existing product skin legacy variables/classes remain compatibility aliases during migration
    Mount a presenter yourself a mount*() function and a structural binding
    Change markup or interaction compose different presenters, use Headless with your own UI, or customize an Element mount hook
    Change domain behavior/API subclass the controller/binding hooks, or compose the Headless object directly

    CSS custom properties cross open Shadow DOM boundaries. Stable part names expose selected internal nodes without treating all private markup as public. Arbitrary markup customization belongs in the subclass or Headless path instead of accumulating a large matrix of Element attributes.

    Each presenter’s Styling section owns its tokens, parts and named handle accessors. Use those documented hooks when styling a specific control; private wui-*__* selectors and DOM order are implementation details.

    Layer Owns Must not own
    Headless clocks, players, mixers, analysis sessions, audio graphs, domain state and resource ownership DOM nodes, CSS, Custom Element registration
    UI presenter accessible markup, input/listener wiring, readout formatting, UI timers/RAF and deterministic cleanup Domain resources, fetching, scheduling or analysis algorithms
    Concrete Element public tag API, attribute/property/event mapping, direct composition, owned-versus-borrowed choice duplicate domain state or a generic adapter hierarchy
    Kernel lifecycle base one connected-lifetime cleanup scope and attribute helpers Shadow DOM, theming, presenter or domain knowledge
    Renderer domain drawings such as staff, piano roll, waveform, spectrogram and waterfall generic transport state or package resource lifecycle

    The direction stays one way: Headless never imports UI or Element code; UI never imports a domain; the concrete Element may import both.

    • MIDI/MusicXML parsing, scheduling, tempo maps and audio graphs stay in Core/Headless.
    • Piano-roll, staff, waveform, waterfall and spectrogram algorithms stay in domain renderers.
    • Canvas/WebGL performance policy and long-audio virtualization stay with View.
    • Framework packages own framework DOM; they may reuse Headless contracts and CSS variables.
    • Documentation chrome stays in apps/doc and is never imported by a published package.
    You want Page
    To browse the twenty-one presenters, and see which Element composes which Catalog
    The shape every presenter follows — host, mount, handle, destroy(), onError, SSR safety @webmusic/ui
    One presenter’s State, Binding, Options, Handle, tokens and parts its page under the class it belongs to