Boneyard / partScope

BONEYARD · part · bay

Scope

src/46-scope.js · one module, no third-party code

Focus preview for keys

ParametersFactory
How it works

Idle, the beam is computed: x = sin(2 pi r s + phase), y = sin(2 pi s), where r is the frequency ratio X:Y. Pointer X maps to r = 1.5 * 2^((x - 0.5) * span), so the middle of the screen (or no pointer at all) is exactly 3:2; within snapWidth of a simple ratio the value is pulled most of the way onto it, never all the way, so the figure still turns. The canvas is cleared every frame (no 8-bit fade, so no ghost that never quite dies); persistence is a short history of the trace, sampled every few hundredths of a second, redrawn oldest first at falling alpha with additive light, so a moving figure leaves a real trail and a still one burns brighter. The newest copy gets a wide halo, a mid glow and a thin white-hot core, and a bright beam head sweeps along it. The nearest simple ratio sets real harmonic counts around the glass, while two small rotors reuse the exact X and Y phases at the beam head.

Holding the pointer (or Space) creates or resumes an AudioContext inside that gesture (a mouse press or a key; on touch, where only the release counts as a user gesture, the first release after a hold unlocks it), then starts two OscillatorNodes at base * r and base Hz into a ChannelMerger (X left, Y right), through one GainNode envelope (soft attack to about 0.08, soft release to zero), to the speakers. A ChannelSplitter after the envelope feeds two AnalyserNodes; while the voice lives, the trace is their time-domain samples plotted X against Y. The voice waits for a short hold before starting, so a scroll swipe never chirps. Release ramps the gain down and stops the oscillators. A watchdog stops the voice if the ride stops ticking the room (switched off, hidden tab) while a hold is still down. destroy() closes the context.

Provenance
technique
a recessed XY oscilloscope: X against Y traces a Lissajous figure in a chamfered phosphor well with harmonic ticks, two live phase rotors and envelope rails; persistence is five explicit recent traces; press-and-hold starts two WebAudio oscillators and draws the actual post-envelope analyser samples
lineage
Lissajous figures (Jules Antoine Lissajous, 1857); the oscilloscope's XY mode; oscilloscope music (the 2010s practice of composing stereo sound whose two channels draw pictures on an XY scope)
original
what you hear is exactly what you see: the held trace is the post-envelope signal on its way to your speakers, so the attack grows the figure and the release shrinks it to a dot; pointer X detunes the ratio and snaps gently toward 4:3, 3:2, 5:3 and 2:1; the top and side ticks show the nearest numerator and denominator, the rotors show both channel phases, and held energy closes the amber coupling braces while the two lower rails show the measured channel levels; the site's only voice is one you hold down
not
a synthesizer, a music player, an autoplay tone. No AudioContext exists until a hold; nothing sounds without one; peak gain 0.08. If audio is blocked the trace stays computed and says so.
deps
none · Canvas 2D + Web Audio · 2026-09
budget
0.60 ms/frame held live @ 720x450 capped backing in a 1440x900 stage; 0.60 ms/frame @ 390x844; fourfold CPU slowdown, local Chromium parts harness (2026-09-30); phone hardware TBD
api
mount(canvas, params, ctx) -> { tick(dt, t, progress, pointer), resize(w, h, dpr), still(t), destroy() }
license
MIT, Desert Data Labs LLC

Budget: 0.60 ms/frame held live @ 720x450 capped backing in a 1440x900 stage; 0.60 ms/frame @ 390x844; fourfold CPU slowdown, local Chromium parts harness (2026-09-30); phone hardware TBD