Sound Visualizer
A Web Audio visualizer: 20 bars that follow the real audio spectrum — built-in synthesized demo sound, optional mic, or connected to your own audio player.
Updated
This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. Hover has 48 effects, including 5 free. Explore the category →
3 usage examples



How it works
The root .fx-0378-root holds three zones: .viz-bars (20 .viz-bar elements created by the script, decorative), .viz-controls (two real <button> elements) and .viz-status (state announcements for screen readers). CSS gives the bars an indigo-violet gradient (linear-gradient(to top, #6366f1, #d946ef)) and a resting height of 4 px.
The script initializes one AudioContext per root, with an AnalyserNode (256-point FFT, 128 frequency bins, smoothing 0.78). On each frame, getByteFrequencyData fills a 128-byte array. The 20 bars are mapped to frequency ranges on a logarithmic scale (20 Hz–Nyquist): low frequencies drive the first bars, highs drive the last — like a real spectrum analyzer. Bar height: value / 255 × 126 px, minimum 4 px.
The built-in demo sound is fully synthesized: a setTimeout scheduler schedules Web Audio events 280 ms ahead, looping an 8-step pattern at 120 BPM (one step = 0.25 s). The pattern combines: a kick drum (sine oscillator sweeping from 90 to 28 Hz with an amplitude envelope), a hi-hat (white noise filtered high-pass at 8 800 Hz), a clap (white noise filtered band-pass at 1 500 Hz) and a G4–G5 pentatonic melody (sawtooth oscillators at 659, 523, 784, 587 Hz, filtered low-pass at 3 200 Hz). The sawtooth harmonics drive the mid bars, percussion the low and high bars.
To connect your own player, add data-fx0378-source="#my-audio" to the root: the script listens for play on that <audio> or <video> element, creates a MediaElementAudioSourceNode (once per element), connects it to both the analyser and the destination, then starts drawing the bars. CORS note: a file hosted on another domain must send proper Access-Control-Allow-Origin headers, otherwise createMediaElementSource will fail.
The AudioContext suspends whenever the root leaves the viewport (IntersectionObserver) or the tab is hidden (visibilitychange), resuming on return. Clean shutdown: mic tracks are stopped (stream.getTracks().forEach(t => t.stop())), nodes disconnected, context suspended.
Accessibility
- Reduced motion: under
prefers-reduced-motion: reduce, therequestAnimationFrameloop is replaced by updates every 300 ms (3–4 Hz) with no CSS transition. The visualizer stays informative — bars reflect the real audio spectrum — without continuous animation. Sound and status messages work normally. - Accessible buttons: both buttons carry
aria-pressedupdated on each state change ("false"/"true"). They are reachable by Tab, activatable by Enter or Space, and styled with a:focus-visibleoutline. Labels toggle between « Play demo » and « Stop demo ». - Decorative bars: the
.viz-barscontainer carriesaria-hidden="true"; the bars carry no audio information — they are a visual reflection. The audio state is announced in.viz-status(aria-live="polite"). - Mic and https:
getUserMediais blocked on non-secure pages (http in production). A specific error message is shown for each error type: access denied, no mic found, insecure page. - No forced gesture: no sound starts without an explicit user click (browser autoplay policy). The mic activates only after the browser confirms permission.
Browser compatibility
Web Audio API (AudioContext, AnalyserNode, createMediaStreamSource, createMediaElementSource, IntersectionObserver). No external dependency. Measured in Chromium 151 and Firefox 153: demo sound, spectral analysis, two independent copies in one page, 0 JS errors. WebKit supports Web Audio since Safari 14.1: not verified in a real Safari.
Without JavaScript or Web Audio (very old browser), the 20 bars at 4 px are visible and the buttons are displayed but inactive. No error is thrown.
The code
HTML structure to paste into your page (the CSS + JS come with your purchase):
<div class="fx-0378-root">
<div class="viz-bars" aria-hidden="true"></div>
<div class="viz-controls">
<button class="viz-btn viz-btn--demo" type="button" aria-pressed="false">
<span class="viz-btn-icon" aria-hidden="true">▶</span>
<span class="viz-btn-label">Play demo</span>
</button>
<button class="viz-btn viz-btn--mic" type="button" aria-pressed="false">
<span class="viz-btn-icon" aria-hidden="true">🎙</span>
<span class="viz-btn-label">Microphone</span>
</button>
</div>
<div class="viz-status" aria-live="polite" aria-atomic="true"></div>
</div>
One-time payment, no subscription — the code is yours forever
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Audio source (HTML — data-fx0378-source attribute on .fx-0378-root) |
None: only the built-in synthesized demo is available | Pass the CSS selector of your <audio> or <video> element (e.g. data-fx0378-source="#my-player"). The visualizer starts automatically on play and stops on pause. CORS: if your audio file is hosted on another domain (CDN, third-party service), that domain must respond with Access-Control-Allow-Origin: * or your specific domain — otherwise createMediaElementSource will fail. |
Bar low and high color (CSS — --viz-low and --viz-high on .fx-0378-root) |
#6366f1 (indigo) → #d946ef (violet-pink) | Override both variables on the root: style="--viz-low:#f97316;--viz-high:#fbbf24". |
Number of bars (JS — NUM_BARS constant) |
20 | Increase up to 48 for finer frequency detail. Reduce to 8 for a minimal look. Bar width (flex: 1; max-width: 14px) adapts automatically. |
FFT resolution (JS — analyser.fftSize) |
256 (128 bins) | Set to 512 or 1024 for finer frequency resolution — most useful with more than 32 bars. Must be a power of two. |
Demo tempo (JS — BPM constant) |
120 | Slow to 90 for a more relaxed groove, increase to 140 for a faster feel. The 8-step pattern adapts automatically. |
Maximum bar height (JS — the 126 in the height calculation) |
126 px | Adjust to match your container. Must not exceed the .viz-bars height (height: 130px). |
FAQ
data-fx0378-source="#your-player-id" to the .fx-0378-root element. The visualizer connects on play and disconnects on pause. CORS note: if your audio is hosted on another domain (CDN, third-party service), that domain must send the Access-Control-Allow-Origin header with your domain or * — otherwise createMediaElementSource will fail and an error message will appear.getUserMedia is blocked on non-secure pages — deploy over https. (2) Permission denied: the user dismissed the browser permission dialog — they can re-allow it from the site settings. (3) No mic detected: check that a microphone is connected and recognized by the operating system.PAT_KICK, PAT_HAT, PAT_CLAP and PAT_MEL define the 8-step pattern; SCALE sets the melody frequencies; BPM controls the tempo. The functions schedKick, schedHat, schedClap and schedNote create the Audio nodes at each step.