Real-time audio visualizer built as a fully functional digital oscilloscope
WebGL-accelerated phosphor simulation • Lissajous patterns • 3D/2D scene rendering • Music-reactive effects • 10 visual themes
- Why This Exists
- What is DSO-1?
- Quick Start
- The Scope Display
- Audio Input
- Signal Generator & Lissajous
- Beam Effects
- Frequency Filter
- 3D / 2D Scene Mode
- Movement FX
- Music-Reactive Modes
- CRT Emulation
- Presets
- Layout Rigs
- Themes
- Recording & Output
- Keyboard Synth
- Keyboard Shortcuts
- Easter Eggs
- Installation
- Tech Stack
Oscilloscope music has been around for a while. The idea of routing audio through a scope and watching geometry materialize on a phosphor screen is genuinely fascinating, but most tools to do it have a steep setup curve, require patching audio through virtual cables, or just don't have the features I wanted to play with.
I wanted something I could open, drop a song into, and immediately get something that looked cool. I also wanted features I hadn't seen elsewhere: image tracing, generative motion effects, a real signal generator for Lissajous figures, beat-reactive visuals, tiling and radial symmetry. And honestly, I partially just wanted to see if I could build something this polished working with Claude. Turns out you can.
DSO-1 is a desktop oscilloscope visualizer that renders audio as real-time phosphor beam graphics. Feed it music, a microphone, or the built-in signal generator and watch it trace waveforms, Lissajous figures, 3D wireframes, and image silhouettes, all with authentic CRT glow and persistence.
It's a real oscilloscope first. Every control maps to something an actual oscilloscope does: dual channels, adjustable V/DIV, timebase, trigger system, AC/DC coupling, frequency measurements. Then it goes further with GPU shaders, beat detection, 3D models, image tracing, tiling grids, and generative motion effects.
git clone https://github.com/HesNotTheGuy/Oscilloscope.git
cd Oscilloscope
npm install
npm startOr grab the installer from the Releases page — no Node.js required.
The main display renders at full WebGL resolution with a multi-pass phosphor pipeline: each frame draws beam geometry into an FBO, runs a separable 9-tap Gaussian blur for glow, then composites onto a persistent phosphor buffer that decays each frame. The result is the characteristic soft-edge, glowing look of a real CRT scope.
| Mode | Description |
|---|---|
| YT | Voltage vs. time — the standard waveform view |
| XY | CH1 drives X, CH2 drives Y — Lissajous and phase figures |
| VS | Vectorscope — L/R correlation in polar form. Mono = vertical line, out-of-phase = horizontal |
| FS | Spectrum analyzer — 64 logarithmic frequency bars from 20 Hz to 20 kHz |
Two independent input channels with full scope controls:
- V/DIV — 50mV to 5V in 11 steps
- Position — vertical offset with reset
- Coupling — AC (removes DC offset), DC (raw signal), GND (zero line)
- Timebase — 1µs to 10s/div
- H-Position — horizontal scroll through the capture buffer
- Trigger mode — Auto, Normal, Single
- Trigger source — CH1 or CH2
- Trigger edge — Rising or Falling
- Trigger level — adjustable with the knob, shown as an arrow marker on the right edge
A readout strip along the bottom of the display shows live measurements updated every 10 frames:
Vpp • Vmax • Vmin • Vrms • Vavg • frequency • period
A second status strip under the scope shows channel state, timebase, trigger, signal frequency, source, and live BPM estimated from the beat detector.
Toggle measurements with M or the measurements checkbox.
DSO-1 accepts three signal sources:
Drag and drop any audio file onto the drop zone, or click to browse. Supported formats: WAV, MP3, OGG, FLAC, AAC, M4A, OPUS, AIFF. Full transport controls — play, pause, stop, scrub, and volume.
Click the MIC button for live input. The scope shows your voice, instrument, or room audio in real time. Works with any system input device.
Built-in dual-channel oscillator — see the Signal Generator section below.
When nothing is connected, a test signal keeps the beam alive so you can configure effects without audio.
The signal generator creates two independent oscillators — one for the left (X) channel and one for the right (Y) channel. In XY mode this produces Lissajous figures whose shape is determined by the frequency ratio, phase offset, and waveform type.
| Control | Range | Effect |
|---|---|---|
| Freq L | 1–2000 Hz | X-axis oscillator frequency |
| Freq R | 1–2000 Hz | Y-axis oscillator frequency |
| Phase | 0–360° | Phase offset between L and R |
| Waveform | Sine / Square / Triangle / Sawtooth | Waveshape — changes figure geometry |
| Amplitude | 0–1 | Output level |
Six L:R ratio shortcuts: 1:1 · 1:2 · 2:3 · 3:4 · 3:5 · 5:8 — clicking a ratio button keeps L fixed and updates R to match.
Rather than hard-coded shape buttons, the generator panel includes a personal OBJ file library. Drop any .obj file onto the OBJ drop zone to load it — it renders as a phosphor wireframe in place of the signal waveform. Add files to the library with + ADD and they persist across sessions by path reference (no copying).
Lissajous quick-reference:
| Shape | Ratio | Phase | Wave |
|---|---|---|---|
| Circle | 1:1 | 90° | Sine |
| Figure 8 | 1:2 | 0° | Sine |
| Heart | 1:2 | 55° | Triangle |
| Star | 1:2.5 | 90° | Sine |
| Spiral | 1:1.007 | 90° | Sine |
| Diamond | 1:1 | 90° | Triangle |
| Web | 1:1.75 | 0° | Sine |
| Chaos | 1:π/2 | 37° | Sawtooth |
| Flower | 1:1.5 | 90° | Sine |
| Bowtie | 1:0.5 | 0° | Sine |
All effects run on the GPU in the WebGL composite shader — no Canvas 2D involved.
12 preset phosphor colors plus a custom hex picker:
| 🟢 Classic Green | 🟡 Amber | 🔵 Cyan | 💙 Blue |
| 🟣 Indigo | 🟣 Violet | 🟣 Magenta | 🔴 Red |
| 🟠 Orange | 🟡 Yellow | 🟢 Lime | ⚪ White |
Scenes (OBJ/image) can have an independent color separate from the waveform beam.
| Effect | What it does | Tunable |
|---|---|---|
| Reactive | Beam width and glow expand with audio RMS amplitude | Strength |
| Beat Flash | Burst of color brightness on detected beat | Strength, sensitivity |
| Beat Invert | Beam flips to white on beat | — |
| Bloom | Wide multi-pass glow halo rendered in 3 blur layers | Strength |
| Afterglow | Phosphor persistence trails with configurable decay | Persistence, hue-shift speed |
| Mirror X | Horizontal flip copy drawn alongside the original | — |
| Mirror Y | Vertical flip copy | — |
| Rotation | Slow continuous spin around the canvas center | Speed |
Afterglow replaces the standard phosphor trail with a hue-shifting version. Each frame the phosphor buffer is rotated through the color wheel by a tunable amount — at higher speeds the trails become full rainbow streaks.
Mirror X and Y each add a flipped copy of the current frame. Enable both for quad symmetry. Combined with Rotation, this produces kaleidoscope-style patterns.
A two-pole biquad band-pass filter (high-pass + low-pass in series) applied to the audio signal before rendering. Isolating frequency bands dramatically changes the shape and movement of the waveform.
Quick presets:
| Preset | Range | Character |
|---|---|---|
| Bass | 20–250 Hz | Low fundamental, slow swings |
| Mid | 250–2000 Hz | Vocals, guitars |
| Treble | 2–6 kHz | Presence, harmonics |
| Highs | 6–20 kHz | Air, cymbals |
| Full | 20–20k Hz | Unfiltered |
Manual Lo/Hi sliders for precise control between 20 Hz and 20 kHz.
The filter affects both the oscilloscope waveform and the signal fed to scene Warp mode — a bass-only filter makes Warp react to kick drums, a treble filter makes it react to hi-hats.
Scenes replace or overlay the waveform with geometry derived from a 3D model or image. Toggle with the 3D button or press 3.
Load any Wavefront .obj file. The parser extracts edges from face definitions, normalizes the geometry to fit the viewport, and projects it with full 3D rotation math (Euler angles, Rz → Rx → Ry order).
Controls:
| Control | Description |
|---|---|
| Scale | Model size (0.1–2.0) |
| Position X/Y | Canvas offset |
| Rot X / Y / Z | Manual rotation angles |
| Auto-Rotate X/Y/Z | Per-axis continuous spin with independent speed |
| Draw Power | How much of the edge list is drawn (0–100%) |
| Auto Ramp | Animate Draw Power from 0→1 automatically |
| Render Mode | Wire / Sparse / Shimmer / Dots — controls how edges are sampled per frame |
| Density | 5–100% subsampling of the edge list — reduces lag on heavy models |
Heavy .obj files are automatically decimated at load time (capped at 12k edges). The renderer also runs an adaptive frame-skipping watchdog: if a frame takes too long, the next is scheduled via setTimeout(0) instead of requestAnimationFrame so the control panel stays responsive. When sustained slowness is detected, density is automatically reduced and restored as headroom returns.
Images are rasterized at configurable density, then converted into phosphor trace paths. Three sampling modes:
Traces the alpha-channel boundary of the image. Pixels that are opaque but have at least one transparent neighbor are included. Best for PNG/SVG with transparent backgrounds — produces a clean silhouette edge.
Runs a Sobel gradient operator across the image luminance. Detects intensity discontinuities — works on any image regardless of transparency. Picks up facial features, textures, object boundaries. Points are sorted into a continuous path via greedy nearest-neighbor to produce connected line traces rather than scattered dots.
Includes every pixel above a brightness threshold. The threshold slider controls how much of the image fills in — lower values include shadows, higher values show only bright highlights. Points are rendered in scan order (no path sort) for clean horizontal runs.
True 3D rotation is available for images: the image plane can be rotated around X and Y axes with perspective divide, making the flat image appear to tilt in 3D space.
Repeat the scene in a grid — up to 5 columns × 5 rows. The total segment budget is automatically capped (80,000 max) and the source is downsampled uniformly if needed to maintain performance.
Arrange 1–8 rotated copies of the scene in a ring around the canvas center. At 6 copies with a model that has left-right symmetry, this produces mandala-style patterns.
Scroll the tiled grid continuously in X and/or Y. The offset wraps at exactly one tile period so the grid appears infinite with no seam. Speed is in tile-widths per second.
Four post-projection movement effects applied to the final screen-space geometry. They stack — all four can run simultaneously. Two shared controls drive every active effect:
- Amt — displacement intensity (0.05–1.0)
- Speed — animation rate multiplier (0.1–5.0)
Sinusoidal XY drift using two independent phase oscillators running at slightly different rates (X and Y are offset by ~golden ratio). The object bobs gently and non-repetitively, never looping in a predictable pattern.
Expands concentric ring waves outward from the canvas center. Each screen-space point is displaced radially based on sin(distance × rings − time). Three simultaneous rings travel through the geometry, creating a pond-ripple distortion across the entire visible field (including tiled copies).
Rotates each point by an angle proportional to its distance from center. Points near center barely move; outer points swing through larger arcs. The twist phase winds continuously — the geometry spirals, unwinds, and re-spirals over time. Amt controls the peak rotation angle (up to ±270°).
Pushes every point radially outward from center using an ease-in/ease-out curve — a fast burst that settles. Without Loop Explode the geometry drifts to its maximum extent and holds. With Loop Explode checked it resets and fires again continuously. Enabling Explode always resets the phase so the burst starts immediately.
These modes make the scene geometry respond directly to the audio signal.
| Mode | How it works |
|---|---|
| Beat Pulse | Scale spikes briefly on each detected beat, then decays smoothly |
| Breathe | Scale continuously follows RMS amplitude — loud audio = bigger object |
| Shake | Position jitters randomly on beat detection, decays between beats |
| Warp | Each edge endpoint is displaced radially using a sample from the audio waveform buffer mapped to the point's angle — the object appears to stretch and distort with the music |
| Audio Sketch | (Image mode) Trace density follows amplitude — loud sections draw fully, quiet sections go sparse |
| Show Audio | Adds the waveform on top of the scene instead of replacing it |
Draw Power slices the edge/trace array — at 0.0 nothing is drawn, at 1.0 all geometry is visible. Combined with Auto Ramp, the scene animates from blank to fully drawn in real time. Loop restarts the ramp automatically for a continuous write-on effect. Rate controls how fast the ramp travels.
This works identically for both OBJ wireframes and image traces.
The WebGL pipeline simulates a real phosphor CRT:
- Persistence — the phosphor buffer decays each frame by a configurable factor. Low values = fast fade, high values = long glowing trails
- CRT Curve — a radial vignette darkens the corners, mimicking the curved glass of a vintage scope
- Grid — fine subdivision lines + major division lines + center crosshair, all batched into single draw calls
- Beam width — controls the core line thickness
- Glow — controls the Gaussian blur radius around the beam
Save complete oscilloscope snapshots — channels, timebase, trigger, beam effects, scene settings, motion FX, display parameters — all stored in a single preset slot.
- 8 save slots with custom names
- 3 built-in presets: Classic · Neon Glow · Amber Retro
- Export any preset or all slots as a
.jsonfile - Import presets from JSON to share setups between machines
- Click a slot to recall it instantly; click during SAVE mode to overwrite it
Curated one-click looks for the wave display — they only affect the beam (color, glow, FX, mode, signal generator), so you can mix-and-match with any UI theme.
Synthwave · Vintage CRT · Lissajous · Beat Drop · Trails · Minimal · Glitch · Wireframe
Panels can be dragged between four drop zones and collapsed or expanded. Four built-in rigs:
| Rig | Description |
|---|---|
| Classic | All panels in a horizontal strip below the scope |
| Studio | Balanced split — channels left, effects right, audio centered |
| Perform | Minimal controls, effects prominent, scope maximized |
| Minimal | Everything collapsed to a narrow right sidebar |
Rig controls live in a compact ⋯ dropdown menu in the topbar. The dropdown exposes: Save, Update, Delete, Edit Mode, and Toggle Layout. Built-in rigs cannot be updated or deleted — those actions are only available for custom rigs you save. All layout state (panel positions, collapsed state, active rig) persists in localStorage.
10 built-in visual themes, each with a distinct aesthetic personality. Select via the theme picker dropdown in the topbar — the choice persists in localStorage.
| Theme | Character |
|---|---|
| Classic Lab | Default green phosphor on dark panels |
| Tektronix Blue | 80s bench equipment aesthetic — chunky beveled panels, scanline overlay |
| Analog Amber | Vintage CRT terminal, brushed metal, serif typography |
| MIL-SPEC | Military equipment styling — monospace stencil font, zero border-radius, cross-hatch texture |
| Modern Minimal | Light theme — white panels, pill buttons, hairline borders |
| Synthwave | Neon glow borders, perspective grid, chrome knobs |
| Wooden Rack | Studio rack aesthetic — wood grain texture, brass knobs, serif font |
| OLED Dark | True black background, white trace, minimal UI chrome |
| Nixie Tube | Warm orange glow, glass-tube bezel, vignette |
| Frosted Glass | Glassmorphism — translucent panels with backdrop blur |
| Liquid Glass | Heavy crystal blur over vivid animated wallpaper — macOS Tahoe energy |
The record button is a split-button with a dropdown to select the recording mode — the choice persists in localStorage.
Standard — records to WebM (VP8/Opus). Click to start, click again to stop and save. Files go to your downloads folder with a timestamp filename. Audio is captured along with the video, so file playback, mic, and signal generator output are all included in the recording.
Transparent (α) — records WebM with VP8 and a real alpha channel. The composite shader outputs luminance as alpha, so the phosphor beam is transparent against black. Files get an _alpha.webm suffix. Ready for compositing in After Effects or Premiere without any chroma keying.
The camera button saves the current frame as a timestamped PNG to your downloads folder.
Click ⤢ POP OUT to open a second window containing only the scope display — no controls, no panels. Resize it independently, drag it to a second monitor, and run the controls from the main window. The pop-out renders its own isolated canvas at whatever resolution you set.
Press K (or click the 🎹 button in the topbar) to turn your computer keyboard into a polyphonic synth that drives the scope. The scope auto-switches to XY mode so harmonic intervals draw as Lissajous figures — chords appear as layered geometry.
FL Studio-style. Z-row is white keys, A/S-row is black keys.
S D _ G H J _ L ;
Z X C V B N M , . /
C major scale starts on Z. Press = and - to shift octaves up/down. Hold multiple keys for chords — each held note runs its own oscillator pair, so a triad shows three Lissajous figures simultaneously.
The synth has an L:R interval ratio that controls the ratio between left and right oscillators. In XY mode, the ratio determines the Lissajous shape. Pick a ratio from the synth panel:
| Ratio | Interval | Shape |
|---|---|---|
| 1:1 | unison | line / circle |
| 3:2 | perfect fifth | pretzel (3 lobes) |
| 4:3 | perfect fourth | 3-loop figure |
| 5:4 | major third | 4-loop figure |
| 2:1 | octave | figure-8 |
So you literally play music as visual geometry.
Anti-click envelopes (5ms attack, 40ms release) keep chord transitions smooth. Each voice is amplitude-limited so a 6-note chord doesn't clip.
Esc exits synth mode; previous scope state is restored.
| Key | Action |
|---|---|
Space |
Play / Pause audio |
Esc |
Stop audio |
G |
Toggle grid |
C |
Toggle CRT curve |
M |
Toggle measurements |
F / F11 |
Toggle fullscreen |
1 |
YT mode |
2 |
XY mode |
3 |
Toggle scene mode on/off |
4 |
Vectorscope mode |
5 |
Spectrum analyzer mode |
Tab |
Switch OBJ / Image scene |
R |
Run / Stop scope |
S |
Single trigger |
A |
Auto Set (auto-fit V/DIV + timebase) |
K |
Toggle keyboard synth mode |
? |
Show shortcut help overlay |
Hover any knob, slider, or button for an inline tooltip showing its current value and what it does.
Right-click the scope canvas for quick actions: save screenshot, copy frame to clipboard, toggle measurements / grid / CRT curve, pop out display, toggle fullscreen, run / stop.
Keyboard shortcuts can be remapped to MIDI input via the InputMapper.
Enter the Konami code (↑ ↑ ↓ ↓ ← → ← → B A) to launch a Snake game rendered through the WaveGL beam pipeline — the snake and food are drawn as phosphor geometry on the scope display. Arrow keys to steer, Space to restart, Esc to exit.
git clone https://github.com/HesNotTheGuy/Oscilloscope.git
cd Oscilloscope
npm install
npm start# Windows NSIS installer
npm run dist:installer
# Portable executable (no install)
npm run dist:portableOutputs go to the dist/ folder.
| Component | Technology |
|---|---|
| Framework | Electron 33 |
| Rendering | WebGL — GPU Gaussian blur, phosphor persistence, GLSL shaders |
| Fallback | Canvas 2D with composite blend modes |
| Audio | Web Audio API — AnalyserNode, real-time FFT, biquad filter chain |
| Recording | MediaRecorder API — VP8/WebM encoding, real-alpha transparent capture |
| 3D | Custom Wavefront .obj parser, Euler rotation, perspective projection |
| Image Trace | Sobel edge detection, greedy nearest-neighbor path sort, alpha boundary |
| Layout | HTML5 Drag and Drop API, localStorage persistence |
| Architecture | Modular ES modules — domain controllers under src/ui/ |
| Testing | Vitest — 134 unit tests |
| Themes | 10 built-in CSS themes with scope parameter presets |
- Node.js 18+ (build from source only)
- Windows 10 / 11 (primary platform)
- GPU with WebGL support — any modern integrated or discrete GPU
CC BY-NC 4.0 — free for personal, educational, and non-commercial use. Attribution required. Commercial use requires permission.
Built with Electron • Rendered with WebGL • Driven by music





























