Audio Buffering Visualizer — Equalizer Bars for Audio Loading States
Audio Buffering Visualizer · Loaders · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="ab-player">
<div class="ab-art">♪</div>
<div class="ab-info">
<div class="ab-track">Midnight Static</div>
<div class="ab-artist">Aurora Fields</div>
<div class="ab-bars" id="abBars">
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
</div>
<div class="ab-status" id="abStatus">Buffering…</div>
</div>
</div>*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0b0d17;color:#e2e8f0;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px}
.ab-player{display:flex;gap:16px;align-items:center;background:#141728;border:1px solid #262b45;border-radius:16px;padding:16px 18px;width:100%;max-width:360px}
.ab-art{width:56px;height:56px;border-radius:12px;flex-shrink:0;background:linear-gradient(150deg,#6366f1,#ec4899);display:flex;align-items:center;justify-content:center;font-size:22px;color:#fff}
.ab-info{flex:1;min-width:0}
.ab-track{font-size:14px;font-weight:800}
.ab-artist{font-size:12px;color:#8b90ab;margin-bottom:9px}
.ab-bars{display:flex;align-items:flex-end;gap:3px;height:26px}
.ab-bars span{flex:1;background:linear-gradient(180deg,#818cf8,#6366f1);border-radius:2px;height:20%}
/* Buffering: bars pulse gently in a "waiting" pattern — narrow height range,
slow, irregular per-bar timing, distinct from the confident full-swing
equalizer motion used once playback is ready. */
.ab-bars.ab-buffering span{animation:abWait 1.1s ease-in-out infinite}
.ab-bars.ab-buffering span:nth-child(1){animation-delay:0s;animation-duration:1.0s}
.ab-bars.ab-buffering span:nth-child(2){animation-delay:.12s;animation-duration:1.3s}
.ab-bars.ab-buffering span:nth-child(3){animation-delay:.05s;animation-duration:.9s}
.ab-bars.ab-buffering span:nth-child(4){animation-delay:.22s;animation-duration:1.2s}
.ab-bars.ab-buffering span:nth-child(5){animation-delay:.08s;animation-duration:1.1s}
.ab-bars.ab-buffering span:nth-child(6){animation-delay:.18s;animation-duration:1.4s}
.ab-bars.ab-buffering span:nth-child(7){animation-delay:.03s;animation-duration:1.0s}
.ab-bars.ab-buffering span:nth-child(8){animation-delay:.15s;animation-duration:1.25s}
@keyframes abWait{0%,100%{height:20%;opacity:.5}50%{height:45%;opacity:.9}}
/* Ready/playing: confident, fuller-swing equalizer motion, faster and taller. */
.ab-bars.ab-ready span{animation:abPlay .7s ease-in-out infinite}
.ab-bars.ab-ready span:nth-child(1){animation-delay:0s}
.ab-bars.ab-ready span:nth-child(2){animation-delay:.08s}
.ab-bars.ab-ready span:nth-child(3){animation-delay:.16s}
.ab-bars.ab-ready span:nth-child(4){animation-delay:.04s}
.ab-bars.ab-ready span:nth-child(5){animation-delay:.2s}
.ab-bars.ab-ready span:nth-child(6){animation-delay:.1s}
.ab-bars.ab-ready span:nth-child(7){animation-delay:.24s}
.ab-bars.ab-ready span:nth-child(8){animation-delay:.06s}
@keyframes abPlay{0%,100%{height:25%}50%{height:100%}}
.ab-status{font-size:11px;font-weight:700;letter-spacing:.03em;color:#818cf8;margin-top:7px}
.ab-status.ab-ok{color:#34d399}var bars = document.getElementById('abBars');
var status = document.getElementById('abStatus');
// Start buffering; after the audio has "loaded enough", switch to the ready/
// playing equalizer pattern — a distinct visual state, not just a re-colored
// version of buffering.
bars.classList.add('ab-buffering');
setTimeout(function () {
bars.classList.remove('ab-buffering');
bars.classList.add('ab-ready');
status.textContent = 'Now playing';
status.classList.add('ab-ok');
}, 2600);Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Audio Buffering Visualizer — A Waiting Pattern That Resolves Into Playback

Audio players need a loading state that fits their context — an equalizer-style bar visual rather than a generic spinner, since bars already read as "sound" to anyone who's seen a music app. This snippet gives an audio player two distinct bar-animation states: a gentle, irregular "buffering" pattern while audio loads, and a confident, fuller-swing "playing" pattern once it's ready — deliberately different motions, not the same animation just recolored.
Two states, two different keyframes
Buffering uses abWait, a shallow oscillation between 20% and 45% height with each of the 8 bars given its own animation-delay (0.03s–0.22s) and its own animation-duration (0.9s–1.4s) — genuinely irregular per-bar timing rather than an evenly-staggered sequence, so the bars pulse somewhat chaotically, like they're waiting rather than performing. Once ready, abReady's abPlay keyframe swings each bar between 25% and a full 100% height on a snappier, uniform 0.7s cycle with tighter, evenly-spaced delays — a confident, rhythmic equalizer motion that reads as active music, not uncertainty.
Why irregular timing matters for "waiting"
Uniform timing (every bar identical except for a linear stagger) reads as controlled and purposeful — appropriate for playback, wrong for buffering. Giving buffering bars mismatched durations and delays that don't follow a clean sequence is what makes the pattern feel tentative and provisional, distinct from the wave loader's clean, evenly-staggered wave or the dots loader's wave variant — those communicate general-purpose "loading," while this specifically communicates "audio, buffering."
A real state transition, not a fixed animation
JavaScript adds the .ab-buffering class on load and, after a timeout standing in for the audio actually having buffered enough to play, swaps it for .ab-ready while updating the status label from "Buffering…" to "Now playing." In a real player you'd trigger that swap from the media element's own canplaythrough or waiting/playing events rather than a fixed delay.
Audio-specific, not video-shaped
Unlike a video buffering overlay (a full-frame spinner over the video canvas), this is built for an audio player's compact, persistent UI — album art, track/artist text, and inline equalizer bars that fit a mini-player or now-playing bar. Pair it with a music player shell or an audio waveform visualizer for a real, signal-driven playing state.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA mini music player renders with bars pulsing in the buffering pattern.
- 2Watch the buffering barsNotice each bar has its own timing — a gentle, irregular "waiting" motion.
- 3Wait for the transitionAfter ~2.6s the status changes to "Now playing" and the bars switch patterns.
- 4Compare the two motionsThe ready state swings taller and faster with tighter, uniform timing.
- 5Wire to real media eventsTrigger the class swap from your <audio> element's waiting/canplaythrough/playing events.
- 6Restyle the playerChange the album art gradient, bar colors, or bar count.
Real-world uses
Common Use Cases
animation-delay and animation-duration.Got questions?
Frequently Asked Questions
They use separate CSS keyframes: buffering (abWait) oscillates each bar in a shallow 20-45% height range with mismatched, non-sequential per-bar durations and delays, giving it an irregular, tentative feel. Playing (abPlay) swings bars in a much taller 25-100% range on a faster, uniform 0.7s cycle with evenly-spaced delays, reading as confident, rhythmic motion. They are genuinely different animations, not the same keyframe recolored or resized.
Uniform, evenly-staggered motion reads as controlled and purposeful, which is the wrong signal for "still loading, please wait." Giving each buffering bar its own slightly mismatched duration and delay — rather than a clean linear stagger — makes the motion feel provisional and uncertain, distinguishing it from a confident playback visualizer or a generic wave loader.
Listen for your <audio> or <video> element's waiting event to add the buffering class, and its canplaythrough or playing event to swap to the ready class — replace the fixed setTimeout with these real event listeners so the visual accurately reflects actual buffering state rather than a guessed delay.
A video buffering indicator is typically a full-frame spinner overlaid on the video canvas itself, since video has a large visual area to darken and overlay. This snippet is built for an audio player's compact, always-visible mini-UI — album art plus inline equalizer bars — which stays visible in a persistent now-playing bar rather than overlaying full-screen content.
Track a status state ('buffering' or 'ready') and derive the bars container's class from it, updating it inside your audio element's event handlers (onWaiting, onCanPlayThrough/onPlaying in React's <audio> props, or the equivalent native event listeners in Vue/Angular). The keyframes and per-bar delay/duration CSS need no changes.