Lissajous Scope
lissajous-scopeVisualizersA vectorscope figure from a single mono signal.
$ pnpm dlx shadcn@latest add @pluginui/core-lissajous-scopeInstall
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-lissajous-scope.jsonUsage
Connect the documented props to your application or host.
import { LissajousScope } from "@/components/ui/lissajous-scope"
<LissajousScope analyser={analyserNode} height={260} />
// In a module face, draw from parameter values instead of audio:
<LissajousScope figure={(t) => orbitPoint(t, depth, voices)} />About Lissajous Scope
Where an 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 |
|---|---|---|---|
| figure | (t: number) => { x: number; y: number } | — | Draw a figure derived from parameter values instead of live audio: t runs 0 to 1 along the trace and each point is in -1..1 space. Sampled at trailLength points and repainted when it changes. Takes precedence over analyser and getData. |
| points | readonly { x: number; y: number }[] | — | An already sampled figure in -1..1 space, head last. Takes precedence over figure and the audio props. |
| analyser | AnalyserNode | — | A live analyser to read time-domain data from. |
| getData | (target: Float32Array) => boolean | void | — | Supply samples yourself instead of using an analyser. |
| phaseShift | number | 248 / 360 | Offset between the X and Y reads as a fraction of the buffer, not degrees. This is what sets the figure's shape. |
| trailLength | number | 450 | How many points to plot. Clamped to half the buffer, and sampled in strides so the trail always spans the whole frame. |
| fadeSpeed | number | 0.1 | Alpha of the per-frame wash over the previous frame. Lower means longer persistence; 0 lets the figure accumulate. |
| zoomLevel | number | 0.6 | Size multiplier. 1 makes a full-scale signal span the shorter axis exactly, below 1 insets the figure, above 1 lets it overshoot. |
| lineWidth | number | 1 | Trace thickness in CSS pixels at the head of the trail. |
| enableGlow | boolean | false | Additive multi-pass bloom around the trace. Costs a pass per level. |
| glowIntensity | number | 3 | Number of bloom passes, 1–5. Only read when enableGlow is on. |
| showGrid | boolean | true | Draw the centre crosshair. |
| color | string | — | Trace colour. Defaults to the --uikit-accent token. |
| height | number | 220 | Display height in pixels; width defaults to 100%. |
| paused | boolean | false | Stop the animation loop and hold the last frame. |
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.