Goniometer
goniometerVisualizersThe stereo image shown as a shape: mono points straight up and phase problems lie flat.
$ pnpm dlx shadcn@latest add @pluginui/core-goniometerInstall
Copies the source into your project. Anything it depends on comes with it. Run this in your React project with shadcn initialized; no registry configuration is needed.
Pulls in
Install using the registry URL
$ pnpm dlx shadcn@latest add https://pluginui.com/r/core-goniometer.jsonUsage
Connect the documented props to your application or host.
import { Goniometer } from "@/components/ui/goniometer"
// A live pair in read-only mode; never connected or reconfigured.
<Goniometer analyserLeft={left} analyserRight={right} />
// Engine-agnostic: fill two arrays per frame and return true.
<Goniometer getStereoData={(l, r) => engine.readStereo(l, r)} height={280} />
// Calibration: plot at the signal's own amplitude, no auto-gain.
<Goniometer analyserLeft={left} analyserRight={right} autoGain={false} />About Goniometer
Where the oscilloscope plots amplitude against time, this plots a buffer against a phase-shifted copy of itself. One channel can therefore draw circles and rosettes without a stereo pair. The phase shift determines the figure: a quarter period turns the degenerate diagonal into a circle, and other offsets sweep through ellipses and loops. Each frame washes over the last instead of clearing it, so trails decay like phosphor on a real CRT. The geometry is an exported pure function that can be asserted in a test or rendered without a canvas.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| analyserLeft / analyserRight | AnalyserNode | — | A live pair, read-only. The component never connects, disconnects or reconfigures a node, and never builds an AudioContext. Both inputs are required; half a pair has no stereo image, so the loop does not start. |
| getStereoData | (left: Float32Array, right: Float32Array) => boolean | void | — | Fill both arrays with the current frame and return true. This engine-agnostic form lets a worklet, compiled engine tap or test harness drive the display without Web Audio. Without this or an analyser pair, the screen shows its graticule and reports no source instead of animating invented samples. |
| autoGain | boolean | true | Normalise the plotted pair toward a readable level, fast down and slow up. A display decision only: the readings are always taken from the raw pair. Turn it off to plot at the signal's own amplitude, which is what a calibration check wants. |
| gain | number | 1 | Fixed display gain, used when autoGain is off. |
| orientation | "mid-side" | "left-right" | "mid-side" | Choose which axes to plot. By default, the component uses the rotated diamond found on hardware goniometers: mono points straight up, pure side lies flat, and upright means mono-compatible. “left-right” plots raw per-channel X-Y values, with X as left and Y as right, so mono moves to the rising diagonal. This mode answers a different question about the channels rather than the image. A dead channel collapses the figure onto one axis, and a level mismatch between channels tilts the diagonal; both details are hidden by the diamond. The graticule and captions also change from a diamond to a box with diagonals, and from M and S to L and R. |
| trailLength | number | 600 | How many points to plot per frame. The frame is walked in strides, so a short trail still spans the whole buffer instead of showing a shrinking window of it. |
| fadeSpeed | number | 0.08 | Alpha of the per-frame wash that fades the previous frame, like CRT phosphor decay. Lower values mean longer persistence; 0 lets the trace accumulate. |
| enableGlow / glowIntensity | boolean / number | true / 2 | Additive multi-pass bloom around the trace, 1–5 passes with a quadratic falloff so it tapers instead of banding. Costs a stroke pass per level. |
| showGraticule / showCorrelation / showBalance / showReadout | boolean | true | The chrome, each independently droppable: the diamond rim and axes, the correlation meter down the right edge, the L…R baseline with the measured balance tick, and the numeric CORR/WIDTH readout. |
| ballistics | { attack?: number; release?: number } | { attack: 0.35, release: 0.08 } | Needle ballistics for the readouts deliberately reverse the kit's meter default. On a correlation meter, the movement worth catching is a drop toward antiphase, so the fast constant applies to downward travel. meterFollow names its constants after the direction of travel. |
| color | string | — | Trace colour. Defaults to the --uikit-accent token. The warning tint is shared with stereo-field and gain-reduction-meter so a face cannot use two different reds for the same warning. |
| height | number | 244 | Screen height in pixels; width fills the container. |
| paused | boolean | false | Stop the loop and leave the last trace on screen to decay. |
Interaction
- Drag with pointer capture, so the gesture keeps tracking past the edge of the control.
- Shift while dragging for a fine pass; release it mid-gesture and coarse tracking resumes.
- Double-click resets to
defaultValue. - Arrow keys step normally, Shift+arrow steps finely, PageUp/PageDown jump by ten, Home/End go to the extremes.
- Proper
sliderandswitchroles with livearia-valuetext, so a screen reader announces the formatted value rather than the raw number. - The scroll wheel adjusts the value while the pointer is over the control; stepped ranges move at least one declared step per notch by default.