The other half of how audio visualizers work: requestAnimationFrame, reading AnalyserNode, and drawing bars, particles and tunnels on an HTML Canvas every frame.

An FFT without a drawing surface is a spreadsheet. A Canvas without audio is a screensaver. How audio visualizers work in the browser is the marriage of the two: once per animation frame, read the analyser, then paint. The FFT half is How FFT Turns Audio Into Visualizer Bands. This post is the paint half — requestAnimationFrame, CanvasRenderingContext2D (and WebGL where a scene needs it), and why a visualizer that ignores the clock looks drunk even when the FFT is correct.
Novus engines all implement that loop against one composition document. The engine reference is the per-engine control table; the glossary defines frame rate, fixed-step clocks and deterministic export. What follows is the browser platform those pages assume.
requestAnimationFrame asks the browser to call you just before the next paint — typically 60 times a second, or whatever the display’s refresh rate is. That callback is the visualizer’s heartbeat. Inside it you: (1) pull fresh frequency and waveform data from AnalyserNode, (2) derive bands, RMS and onset, (3) clear or fade the canvas, (4) draw this engine’s geometry, (5) schedule the next frame. Skip step 1 and you get a still. Skip step 3 and you get a smear. Skip scheduling and the scene freezes after one picture.
rAF is vsync, not a metronome
The callback interval follows the display, not the song. A 120 Hz laptop and a 60 Hz TV will invoke you at different rates. That is why export cannot simply “record the preview”: a fixed-step clock steps audio and draw at a chosen frame rate so two renders of the same project match. Preview is allowed to be display-timed; the file you upload is not.
The HTML canvas element is a bitmap with a drawing context. 2D context (getContext("2d")) is enough for bars, waveforms, particles, trails and most lyric typography: fillRect, beginPath, arc, fillText, gradients, compositing. WebGL / WebGPU enter when a scene is a mesh, a tunnel of instanced streaks, or a full-screen shader. Novus’s V3 engines share one runtime so a mode can stay on Canvas2D without pretending to be a game engine.
clearRect every frame for a crisp EQ. A translucent black fill for trails and tunnels that need memory of the last frame.canvas.width is not CSS pixels. Visualizers that ignore devicePixelRatio look soft on retina screens and overdraw on low-DPI ones.shadowBlur are the usual ways a 60 fps preview falls to 20. The render-load label on each engine is the honest ceiling, not marketing.None of this is Novus-specific. It is why a “music visualizer in JavaScript” tutorial always starts with a <canvas>, an AudioContext, and a requestAnimationFrame loop. The product work is mapping this engine’s controls onto that loop without drifting from export.
The analyser must be read in the frame, not cached from click-time. getByteFrequencyData and getByteTimeDomainData fill arrays you own; they do not allocate. Typical pattern: allocate the Uint8Arrays once when the scene starts, then reuse them every rAF. Allocating a new array each frame is a GC hitch waiting to happen, and hitches look like missed beats.
From those arrays you derive the same measurements the FFT article described: bins → bands, waveform → RMS / peak, bass jump → onset. Then each engine maps those numbers onto its vocabulary — bar height, particle emission, tunnel speed, glyph scatter. Same inputs; different drawings. That is why Choosing the Right Visualizer Engine is a drawing choice, not an audio-math choice.

Once the numbers exist, the engine is a style of painting:
fillRect (or a trapezoid) proportional to magnitude. Mirror modes draw twice.Open those four on the same track from the template gallery and you are watching four Canvas programs consume one analyser. The engine reference then tells you which controls each program actually reads — not a brochure list, the live schema both editors use.
A preview canvas is coupled to the display: rAF timing, tab visibility, prefers-reduced-motion, GPU load, even the size of the panel. An export has to be deterministic — same project, same audio, same file. So the offline path replays the analyser at a fixed frame rate and draws into an offscreen canvas (or encoder input) instead of recording whatever was on screen. If preview and export ever disagree, it is almost always because one path read a control the other ignored — which is exactly what the control-wiring tests exist to catch.
Reduced motion is a preview concern
Marketing canvases (homepage hero, engine cards) read prefers-reduced-motion and stop the rAF loop after one composed frame. The editor canvas does not silently freeze your composition because of an OS setting — that would be editing someone else’s document without asking. Preview stop ≠ export change. Details are on the accessibility page.
How audio visualizers work, end to end: decode audio → AnalyserNode → FFT bins and time-domain samples → bands / RMS / onset → requestAnimationFrame → Canvas draw → (on export) the same draw on a fixed clock. The glossary is the vocabulary; the engine reference is the per-engine map; How FFT Turns Audio Into Visualizer Bands is the measurement chapter. Open the editor and watch one frame of that loop happen sixty times a second.
Reviewed by the editorial team. Report a correction.
Anonymous. Only this page’s address and your yes/no are recorded, and only if you allowed analytics cookies.
Ready to make your own?
Open the editor, drop in a track, and export a watermark-free video — free, no account.
Open the Editor