Instruction file imported from brcodes/audio-perception-vizualizer (
.github/instructions/audio-perception-visualizer.instructions.md). Copyright stays with the author.
Audio Perception Visualizer — Coding Instructions
1. Visualizer Purpose/Design Requirement (Only Explicit Requirement)
The only explicit requirement regarding actual visualizer purpose/design is:
Make a visualizer that faithfully mimics/mathematically analogizes, as closely as possible, the sounds that people hear in a mix when hearing it from directly in front of them, for the engineering purposes of mixing/producing. "Cool" visualization is not as important as replicating heard audio.
Use this as the decision rule for all visual mapping choices.
2. Audio Engineering Guidance
- Prefer logarithmic frequency treatment (20 Hz to 20 kHz) for pitch-related analysis unless another approach better serves Section 1.
- Keep FFT and smoothing choices high enough to preserve meaningful mix detail for engineering decisions.
- Preserve true stereo analysis (separate L/R channels) so localization cues are represented accurately.
- Use
AudioContextonly on explicit user gesture (file load or play). - Revoke
objectUrlwithURL.revokeObjectURL()before assigning a new one to avoid memory leaks.
3. Code Organization & Style
Module structure (single-file, annotated)
The app is intentionally a single-module static file (app.js with type="module"). Organize code in this order:
- DOM references — all
getElementById/querySelectorcalls at top. - Constants —
DIVISION_EPSILON, thresholds,FREQ_COUNT,HALF_BIN_RATIO, MIME set, color stops. - Pure utility functions —
formatTime,interpolateColor,computeAlpha,hzToIndex,getBandEnergy,amplitudeToHeightFactor,frequencyToWidthFactor. - Derived data —
FREQUENCIES,BOTTOM_FREQS,TOP_FREQS,panSmoothed. - Audio graph setup —
ensureAudioGraph(lazy init). - Canvas / draw functions —
resizeCanvas,drawWaveform,drawVisualizer,animate. - Playback control —
startAnimation,stopAnimation,togglePlayPause. - Event listeners — file input, play/pause, seek, sliders, nudge buttons, keyboard, visibility.
Annotation standard
- Every non-obvious constant or formula gets a single-line comment explaining the why (not the what).
- Preserve psychoacoustic rationale comments when they support Section 1 fidelity goals.
- Use JSDoc for exported/public-facing functions only if the project ever splits into modules; not required for single-file closures.
Naming
logT— log-frequency parameter[0,1]over 20 Hz–20 kHz.depth— drawing-order depth[0,1]; 0 = back, 1 = front.energy— RMS-like scalar[0,1]fromgetBandEnergy.panPoint— integer[-100, 100]; negative = left, positive = right.panSmoothed— smoothed pan state array, length 100, indices matchFREQUENCIES.
4. Performance Requirements
- No per-frame allocations in the hot path (
animate→drawVisualizer→drawWaveform). Reuse typed arrays (leftData,rightDataareUint8Array). Do not create new arrays or objects insideanimate. - Canvas 2D: use
ctx.save()/ctx.restore()scoped clips. Never reset the entire transform inside a frame. - DPR scaling:
resizeCanvasmultiplies bywindow.devicePixelRatio. Always callctx.setTransform(dpr, 0, 0, dpr, 0, 0)after resizing. Draw in CSS pixel coordinates. requestAnimationFrameloop: use a singlerafIdguard. Checkaudio.pausedandisDocumentHiddenbefore starting. Stop the loop on pause or tab hide.- Avoid layout thrash: read
canvas.clientWidth/canvas.clientHeightat the top ofdrawVisualizeronce per frame, not inside the band loop. - Prefer
quadraticCurveToover point-loop waveforms (already implemented). Don't revert to point loops.
5. Security
- File validation: before creating an
objectURL, verifyfile.typeagainstMP3_MIME_TYPESand.mp3extension. Never load arbitrary binary blobs into the audio element without this check. - No
eval,innerHTML, ordocument.writeanywhere. All DOM manipulation via property assignment (textContent,value,disabled). audio.crossOrigin = 'anonymous'is set for CORS compliance. Do not remove it.- Object URL lifecycle: always
URL.revokeObjectURL(objectUrl)before reassigning. Never exposeobjectUrlbeyond the module scope. - Input sanitization: all slider/number-input values read with
parseFloatand clamped to[min, max]before use. Never pass raw user input directly to canvas drawing math. - No network requests from client-side code. This is a fully local/static tool. Do not add fetch calls, CDN script tags, or analytics without explicit approval.
- CSP-compatible: no inline event handlers (
onclick=). All listeners viaaddEventListener.
6. Extending the Visualization
When adding new visual features:
- Prioritize changes that improve fidelity to Section 1.
- New per-band state (like
panSmoothed) should be aFloat32Arrayof lengthFREQ_COUNTinitialized at module load, not allocated per frame. - New controls go in
.controls(persistent settings) or.seekbar(playback-related); use the existingmakeSliderPairhelper for any new slider+number-input pair. - Validate behavior against real-world mixes and front-listening expectations.
7. HTML & CSS Conventions
- Semantic HTML: controls in
<section class="controls">, seekbar in<section class="seekbar">, canvas asrole="img"witharia-label. - All interactive elements have
aria-labelattributes. Do not add controls without them. - Visual styling is secondary to Section 1 accuracy.
box-sizing: border-boxon*. Grid layout on.app.- No external CSS frameworks, no
!important.