Files
skills/docs/ui/react/react-control-surface/src/controls/meter-spectral/meter-spectral.md
2026-05-29 08:20:56 +01:00

3.9 KiB

<MeterSpectral> — multi-band spectrum visualiser

(no submodules)

Purpose

<MeterSpectral> renders a real-time bar graph of magnitude bins. Each bin is drawn as a vertical bar whose height is proportional to the bin's magnitude in [0, 1]. The component is deliberately tier-pure: it has no dependency on @suaveplan/audio-analysis-spectrum or any audio code at all. Bins are pre-computed by the caller — wire it up to whatever analyser is appropriate (web-audio AnalyserNode, the spectrum-analysis package, a network stream, or a synthetic generator).

This separation keeps the React rendering layer free of audio processing and makes it trivial to use the same component for any 1-D magnitude signal — FFT magnitudes, wavelet bands, frequency-warped mel bins, or live envelope follower output.

Features

  • Static Float32Array or streaming AsyncIterable<Float32Array> source
  • Configurable width, height, and inter-bar gap
  • Per-bin data-value attributes for E2E and screen-reader inspection
  • role="img" with a single aria-label describing the visualisation
  • CSS-variable themed bars (--cs-meter-spectral-bar-color)
  • Out-of-range bin values are clamped to [0, 1] automatically

Usage: Basic

import { MeterSpectral } from "@suaveplan/react-control-surface";

<MeterSpectral
    bins={new Float32Array([0.1, 0.5, 0.9, 0.3])}
    label="Output spectrum"
/>;

Usage: Advanced

import { MeterSpectral } from "@suaveplan/react-control-surface";

async function* spectrumStream(
    analyser: AnalyserNode
): AsyncIterable<Float32Array> {
    const buf = new Float32Array(analyser.frequencyBinCount);
    while (true) {
        analyser.getFloatFrequencyData(buf);
        const out = new Float32Array(buf.length);
        for (let i = 0; i < buf.length; i++) {
            out[i] = Math.max(0, Math.min(1, (buf[i] + 100) / 100));
        }
        yield out;
        await new Promise((r) => requestAnimationFrame(() => r(null)));
    }
}

<MeterSpectral
    bins={spectrumStream(analyserNode)}
    width={480}
    height={160}
    label="Master spectrum"
/>;

API Reference

Prop Type Default Description
bins Float32Array | AsyncIterable<Float32Array> req Magnitude bins in [0, 1].
width number 240 Total width in CSS pixels.
height number 80 Total height in CSS pixels.
gap number 1 Inter-bar gap in CSS pixels.
label string — Visible + accessible label.
ariaLabel string derived Override the accessible label.
className string — Appended to rcs-meter-spectral.

Implementation Notes

When bins is a Float32Array, the component sets its frame state directly on mount and whenever the prop identity changes. When bins is an AsyncIterable, an effect subscribes via for await … of and calls setFrame for every yielded frame. The subscription is torn down via a cancelled flag in the cleanup function; errors thrown from the iterator are swallowed because visualisation is best-effort.

Bars are absolutely-positioned divs aligned to the bottom of the container; bar width is (width - totalGap) / count. This keeps the layout pure CSS and avoids <canvas> — every bar is independently inspectable in DOM and accessible tooling.