Oscilloscope
A phosphor trace on a CRT grid, from waveforms or live audio.
Installation
npx shadcn@latest add https://neonkit.heyaadi.com/r/oscilloscope.jsonUsage
import { Oscilloscope } from "@/components/ui/oscilloscope"
<Oscilloscope wave="sine" frequency={2} amplitude={0.6} />Examples
Waveforms
Square and saw edges overshoot and ring before they settle, like a function generator seen through the scope's bandwidth.
Persistence
How much light each frame keeps. Higher values leave a longer afterglow as the trace drifts.
Live audio
Pass an AnalyserNode as source to draw real audio, triggered on a rising edge so periodic sound stands still. Both sources start their AudioContext inside the click.
API reference
Renders a div with the screen's canvases and accepts div attributes; the ref points at the div. It defaults to the full width at 16:10; size it with className.
| Prop | Type | Default |
|---|---|---|
wave | "sine"Generated waveform. Ignored while source is set. | "sine" |
frequency | numberCycles across the screen. | 2 |
amplitude | numberPeak height as a fraction of half the screen. With source, the input gain. | 0.6 |
speed | numberDrift in cycles per second. 0 holds the trace still, like a triggered scope. | 0.25 |
persistence | numberShare of each frame's light that survives into the next, 0 to 0.98. | 0.85 |
grid | booleanThe graticule: 10 by 8 divisions with a ticked center cross. | true |
source | AnalyserNodeLive audio. Draws its time-domain data instead of the generated wave. | None |
children | ReactNodeOverlay content, positioned absolutely over the glass. Text picks up a mono, glowing readout style. | None |
color | NeonColorPhosphor color. Falls back to --neon-accent, then cyan. | inherited |
Accessibility
- It's hidden from screen readers by default. Pass aria-label (for example "Sine wave, 200 Hz") to expose it as an image.
- The canvases pause while off screen or in a hidden tab, size themselves with a ResizeObserver and render at the device pixel ratio, capped at 2.
- With reduced motion, generated waves draw one still frame. A live source keeps updating a few times a second without the afterglow.
- The phosphor color is read from --neon, and the canvas follows changes to an ancestor's --neon-accent without a re-render.
- The component never creates an AudioContext. Build the AnalyserNode in response to a click so the browser lets it start.