Audio visualer simple html css js​

·

In this tutorial, we will build a simple Audio visualer simple html css js​ web project. Copy the source below or open it in the editor to try your own changes.

What you will build

This audio visualizer turns music into animated visuals using HTML, CSS, and JavaScript.
It displays moving bars that react to the sound in real time.
The project combines a simple interface with engaging audio effects.

HTML source

<!doctype html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta name="theme-color" content="#111310"><meta name="description" content="Frequency — a beautiful, private audio visualizer. Upload a track and see your sound."><title>Frequency — See your sound</title></head>
<body>
<header class="header"><a class="logo" href="./" aria-label="Frequency home"><span class="logo-icon">▥</span> frequency<span class="logo-dot">.</span></a><span class="header-note">A little space for your sound</span><a class="github" href="#about">About <span>↗</span></a></header>
<main>
<section class="intro"><div><div class="eyebrow"><span></span> SOUND, IN A DIFFERENT LIGHT</div><h1>See your sound<span>.</span></h1><p>Drop a track. Press play. Get lost in the frequency.</p></div><div class="local-badge"><span>◈</span><div>Just you and your music<small>Your audio stays on your device.</small></div></div></section>
<section class="studio" aria-label="Audio visualizer">
<div class="visualizer" id="visualizer"><canvas id="canvas" aria-label="Animated audio visualization"></canvas><div class="visualizer-top"><span class="live-tag"><i></i> <span id="visual-status">READY TO PLAY</span></span><button class="icon-btn" id="fullscreen" title="Toggle fullscreen" aria-label="Toggle fullscreen">⛶</button></div><div class="visualizer-bottom"><span id="mode-label">SPECTRUM / 01</span><span>FEEL EVERY FREQUENCY</span></div></div>
<div class="player"><div class="track"><div class="cover">♫</div><div><strong id="track-title">Midnight Motion</strong><span id="track-subtitle">Frequency Originals · Demo track</span></div><span class="demo-badge" id="demo-badge">DEMO</span></div><div class="transport"><button class="icon-btn" id="restart" aria-label="Restart track" title="Restart">↺</button><button id="play" class="play-btn" aria-label="Play">▶</button><button class="icon-btn" id="loop" aria-label="Loop track" aria-pressed="true" title="Loop">↻</button></div><div class="volume"><button class="icon-btn" id="mute" aria-label="Mute">♫</button><input type="range" id="volume" min="0" max="1" step="0.01" value="0.7" aria-label="Volume"></div><div class="timeline"><span id="elapsed">0:00</span><input type="range" id="seek" min="0" max="100" step="0.1" value="0" aria-label="Seek in track"><span id="duration">1:00</span></div></div>
</section>
<section class="workspace"><div class="settings"><div class="section-heading"><h2>Make it yours</h2><span>THE CONTROLS</span></div><div class="setting-row"><label>Visual style</label><div class="segmented" id="modes"><button class="selected" data-mode="spectrum">▥ <span>Spectrum</span></button><button data-mode="wave">∿ <span>Wave</span></button><button data-mode="orbit">◎ <span>Orbit</span></button></div></div><div class="setting-row"><label>Color palette</label><div class="palettes"><button class="swatch selected" data-color="#d2ff66" style="--swatch:#d2ff66" aria-label="Electric lime" aria-pressed="true"></button><button class="swatch" data-color="#8c9cff" style="--swatch:#8c9cff" aria-label="Soft violet" aria-pressed="false"></button><button class="swatch" data-color="#ff927d" style="--swatch:#ff927d" aria-label="Coral" aria-pressed="false"></button><button class="swatch" data-color="#69dfe3" style="--swatch:#69dfe3" aria-label="Ice blue" aria-pressed="false"></button><span id="color-name">Electric lime</span></div></div><div class="setting-row"><label for="sensitivity">Sensitivity</label><div class="sensitivity"><span>Low</span><input id="sensitivity" type="range" min="0.5" max="2" step="0.1" value="1"><span>High</span></div></div></div>
<div class="upload" id="dropzone"><div class="upload-icon">↥</div><h2>Bring your own sound</h2><p>Drag &amp; drop your audio file here<br>or choose something from your library.</p><button class="upload-btn" id="upload-button">Choose audio file <span>+</span></button><input type="file" id="file" accept="audio/*,.mp3,.wav,.ogg,.m4a,.flac" hidden=""><span class="file-types">MP3, WAV, OGG, M4A · up to 100 MB</span><p id="message" role="status"></p></div></section>
<footer id="about"><span><span class="footer-mark">▥</span> Made for the love of sound.</span><span>NO UPLOADS. NO ACCOUNTS. JUST MUSIC. <span class="footer-dot">●</span></span></footer>
</main><audio id="audio" loop=""></audio>

</body></html>

CSS source

@import url("https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;450;500;550;600;650;700&family=Space+Grotesk:wght@400;500;600;700&display=swap");
:root{color-scheme:dark;--bg:#111310;--panel:#191c17;--border:#30342b;--text:#f2f3ed;--muted:#94998b;--accent:#d2ff66}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font:14px 'DM Sans',sans-serif}button,input,a{-webkit-tap-highlight-color:transparent}button{font:inherit;cursor:pointer}button:focus-visible,a:focus-visible,input:focus-visible{outline:2px solid var(--accent);outline-offset:5px}a{color:inherit;text-decoration:none}.header{height:94px;border-bottom:1px solid #262a23;display:flex;align-items:center;justify-content:space-between;padding:0 6.3%}.logo{font-family:'Space Grotesk',sans-serif;font-size:26px;font-weight:700;letter-spacing:-1px;display:flex;align-items:center;gap:9px}.logo-icon{color:var(--accent);font-size:33px}.logo-dot{color:var(--accent);margin-left:-8px}.header-note{font-size:12px;color:var(--muted)}.github{font-size:13px}.github span{margin-left:12px;color:var(--muted)}main{max-width:1240px;padding:0 40px;margin:auto}.intro{display:flex;align-items:center;justify-content:space-between;padding:53px 0 34px}.eyebrow{font-size:10px;letter-spacing:2px;color:#b3bba7;display:flex;align-items:center;gap:9px}.eyebrow span{height:5px;width:5px;border-radius:50%;background:var(--accent)}h1{font-family:'Space Grotesk',sans-serif;font-size:52px;font-weight:500;letter-spacing:-2.8px;line-height:1.1;margin:15px 0 12px}h1 span{color:var(--accent)}.intro p{color:var(--muted);font-size:14px;margin:0}.local-badge{display:flex;align-items:center;gap:12px;font-size:12px;margin-top:27px}.local-badge>span{color:var(--accent);font-size:26px}.local-badge small{display:block;color:var(--muted);font-size:11px;margin-top:5px}.studio{border:1px solid var(--border);border-radius:14px;overflow:hidden;background:var(--panel)}.visualizer{height:355px;position:relative;background:radial-gradient(ellipse at 50% 85%,#26321c55,transparent 65%),#141710}.visualizer:fullscreen{height:100vh;width:100vw}.visualizer canvas{width:100%;height:100%;display:block}.visualizer-top,.visualizer-bottom{position:absolute;left:25px;right:25px;display:flex;align-items:center;justify-content:space-between}.visualizer-top{top:20px}.live-tag{font-size:9px;letter-spacing:1.5px;display:flex;gap:8px;align-items:center;border:1px solid #38412e;border-radius:5px;padding:8px 10px;background:#1d2417}.live-tag i{background:var(--accent);width:5px;height:5px;border-radius:50%}.icon-btn{color:#a4ab9a;background:none;border:0;font-size:23px;padding:7px;line-height:1}.visualizer-bottom{bottom:19px;font:9px monospace;letter-spacing:1.8px;color:#7d876e}.player{padding:23px 25px 19px;border-top:1px solid var(--border);display:grid;grid-template-columns:1fr auto 1fr;align-items:center;gap:19px}.track{display:flex;align-items:center;gap:12px;min-width:0}.cover{width:44px;height:44px;flex-shrink:0;border:1px solid #495435;background:linear-gradient(135deg,#465c28,#26321b);color:var(--accent);border-radius:8px;display:grid;place-items:center;font-size:23px}.track strong{display:block;font-size:13px;font-weight:550;max-width:280px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.track span:not(.demo-badge){display:block;font-size:10px;color:var(--muted);margin-top:5px}.demo-badge{font-size:8px;letter-spacing:1px;background:#2b3123;color:#b9c8a1;border:1px solid #39422d;border-radius:4px;padding:4px 6px;margin-left:4px}.transport{display:flex;gap:17px;align-items:center}.play-btn{height:43px;width:43px;border-radius:50%;background:var(--accent);border:0;color:#202911;font-size:16px;padding-left:7px}.play-btn:hover,.upload-btn:hover{filter:brightness(1.1)}#loop[aria-pressed=true]{color:var(--accent)}.volume{justify-self:end;display:flex;align-items:center;gap:9px}.volume input{width:90px}.timeline{grid-column:1/-1;display:flex;align-items:center;gap:13px;font:10px monospace;color:var(--muted)}input[type=range]{appearance:none;height:3px;accent-color:var(--accent);background:#3c4334;border-radius:10px;cursor:pointer}input[type=range]::-webkit-slider-thumb{appearance:none;width:10px;height:10px;border-radius:50%;background:var(--accent)}input[type=range]::-moz-range-thumb{width:10px;height:10px;border:0;border-radius:50%;background:var(--accent)}#seek{width:100%;height:3px}.workspace{display:grid;grid-template-columns:1.2fr 1fr;gap:22px;margin-top:24px}.settings{border:1px solid var(--border);border-radius:12px;background:#181b16;padding:25px 26px}.section-heading{display:flex;justify-content:space-between;align-items:center;margin-bottom:23px}h2{font-size:15px;font-weight:550;margin:0}.section-heading>span{font-size:8px;letter-spacing:1.5px;color:#747d68}.setting-row{display:flex;align-items:center;justify-content:space-between;gap:20px;margin-top:20px}.setting-row label{font-size:12px;color:#b7bdaf;min-width:80px}.segmented{display:flex;background:#11150f;border:1px solid #2e3527;padding:4px;border-radius:7px;gap:3px}.segmented button{border:0;border-radius:4px;background:none;color:#89937c;padding:9px 12px;font-size:13px;white-space:nowrap}.segmented button span{font-size:11px;margin-left:5px}.segmented button.selected{color:var(--accent);background:#2d3820}.palettes{display:flex;align-items:center;gap:11px}.swatch{height:18px;width:18px;border:3px solid var(--panel);outline:1px solid transparent;border-radius:50%;background:var(--swatch);padding:0}.swatch.selected{outline-color:var(--swatch)}.palettes>span{font-size:10px;color:var(--muted);width:70px;margin-left:5px}.sensitivity{display:flex;align-items:center;gap:12px;width:260px}.sensitivity span{font-size:10px;color:#7d8771}.sensitivity input{width:100%}.upload{border:1px dashed #3b4431;border-radius:12px;background:#171b14;text-align:center;padding:23px 20px 20px;transition:.2s}.upload.dragging{border-color:var(--accent);background:#242e1b}.upload-icon{font-size:24px;color:#a5b58b;margin-bottom:11px}.upload h2{font-size:15px}.upload p{font-size:11px;line-height:1.8;color:var(--muted);margin:8px 0 14px}.upload-btn{border:1px solid #455333;color:#d2e5b1;background:#2a3520;border-radius:6px;font-size:11px;padding:10px 15px}.upload-btn span{margin-left:20px}.file-types{display:block;font-size:9px;color:#707c62;margin-top:13px}#message:empty{display:none}#message{color:var(--accent);margin-bottom:0}footer{display:flex;justify-content:space-between;align-items:center;padding:29px 0 26px;color:#747e68;font-size:10px}footer>span:first-child{display:flex;align-items:center;gap:9px}.footer-mark{font-size:20px;color:#9bab82}footer>span:last-child{font-size:8px;letter-spacing:1.2px}.footer-dot{color:var(--accent);margin-left:12px;font-size:7px}@media(min-width:1500px){main{max-width:1360px}.visualizer{height:420px}.intro{padding-top:65px}}@media(max-width:900px){main{padding:0 25px}.header{padding:0 25px}.workspace{grid-template-columns:1fr 1fr}.settings{padding:22px 18px}.setting-row{flex-wrap:wrap;gap:10px}.segmented,.sensitivity{width:100%}.segmented button{flex:1}.palettes{width:100%}.demo-badge{display:none}.track strong{max-width:170px}}@media(max-width:600px){.header{height:72px;padding:0 20px}.logo{font-size:23px}.header-note,.local-badge{display:none}main{padding:0 16px}.intro{padding:35px 3px 27px}h1{font-size:42px}.intro p{font-size:12px}.eyebrow{font-size:8px}.visualizer{height:290px}.visualizer-top,.visualizer-bottom{left:17px;right:17px}.visualizer-bottom{font-size:7px;letter-spacing:1px}.player{padding:18px 16px;grid-template-columns:1fr auto;gap:17px}.track strong{max-width:155px}.track span:not(.demo-badge){font-size:9px}.cover{width:37px;height:37px}.transport{gap:5px}.transport .icon-btn{font-size:20px}.play-btn{height:38px;width:38px}.volume{display:none}.workspace{grid-template-columns:1fr;gap:16px;margin-top:16px}.settings{padding:24px}.setting-row{flex-wrap:nowrap}.segmented{width:auto}.segmented button{padding:9px 8px}.segmented button span{font-size:10px}.palettes{width:auto;gap:9px}.sensitivity{width:210px}.upload{padding:25px}footer{gap:16px;padding:24px 3px;align-items:flex-start}footer>span:last-child{max-width:145px;text-align:right;line-height:1.8;font-size:7px}}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}
[hidden]{display:none!important}

JavaScript source

'use strict';
const $ = id => document.getElementById(id);
const audio = $('audio'), canvas = $('canvas'), ctx = canvas.getContext('2d');
let context, analyser, frequencies, waveform, mode = 'spectrum', color = '#d2ff66', objectURL, demoURL, loading = false;
let width = 0, height = 0, playing = false;
const reducedMotion = matchMedia('(prefers-reduced-motion: reduce)');
audio.volume = 0.7;
function setupAudio() {
  if (!context) {
    context = new (window.AudioContext || window.webkitAudioContext)();
    analyser = context.createAnalyser(); analyser.fftSize = 2048; analyser.smoothingTimeConstant = 0.82;
    context.createMediaElementSource(audio).connect(analyser); analyser.connect(context.destination);
    frequencies = new Uint8Array(analyser.frequencyBinCount); waveform = new Uint8Array(analyser.fftSize);
  }
  return context.resume();
}
// A self-contained, synthesized 60-second demo: no network or bundled music needed.
function createDemo() {
  const sampleRate = 22050, length = sampleRate * 60, buffer = new ArrayBuffer(44 + length * 2), view = new DataView(buffer);
  function str(offset, value) { for(let i=0;i<value.length;i++) view.setUint8(offset+i,value.charCodeAt(i)); }
  str(0,'RIFF'); view.setUint32(4,36+length*2,true); str(8,'WAVE'); str(12,'fmt '); view.setUint32(16,16,true); view.setUint16(20,1,true); view.setUint16(22,1,true); view.setUint32(24,sampleRate,true); view.setUint32(28,sampleRate*2,true); view.setUint16(32,2,true); view.setUint16(34,16,true); str(36,'data'); view.setUint32(40,length*2,true);
  const notes = [130.81,155.56,174.61,116.54];
  for(let i=0;i<length;i++) {
    const t=i/sampleRate, beat=t%0.5, note=notes[Math.floor(t/4)%4], step=t%0.25;
    const kick=Math.sin(2*Math.PI*(48*beat+10*(1-Math.exp(-beat*35))))*Math.exp(-beat*18)*0.44;
    const bass=(Math.sin(2*Math.PI*note*t)+0.25*Math.sin(2*Math.PI*note*2*t))*Math.exp(-beat*6)*0.22;
    const arp=Math.sin(2*Math.PI*note*[2,3,4,3][Math.floor(t*4)%4]*t)*Math.exp(-step*16)*0.1;
    const hat=(Math.random()*2-1)*Math.exp(-step*95)*0.07;
    const pad=(Math.sin(2*Math.PI*note*t)+Math.sin(2*Math.PI*note*1.5*t))*0.055;
    const fade=Math.min(t/2,1,(60-t)/2);
    view.setInt16(44+i*2,Math.max(-1,Math.min(1,(kick+bass+arp+hat+pad)*fade))*32767,true);
  }
  demoURL=URL.createObjectURL(new Blob([buffer],{type:'audio/wav'})); audio.src=demoURL;
}
function setPlaying(value) { playing=value; $('play').textContent=value?'Ⅱ':'▶'; $('play').setAttribute('aria-label',value?'Pause':'Play'); $('visual-status').textContent=value?'NOW PLAYING':'READY TO PLAY'; }
$('play').addEventListener('click',async()=>{
  if(loading)return;
  if(!audio.paused){audio.pause();return;}
  loading=true;
  try{await setupAudio();if(!audio.src)createDemo();await audio.play();$('message').textContent='';}
  catch(error){$('message').textContent='Unable to play this audio. Please choose a supported file.';}
  finally{loading=false;}
});
audio.addEventListener('play',()=>setPlaying(true));audio.addEventListener('pause',()=>setPlaying(false));audio.addEventListener('ended',()=>setPlaying(false));
audio.addEventListener('error',()=>{$('message').textContent='This audio format could not be played. Try an MP3 or WAV file.';setPlaying(false);});
const time = value => {value=Number.isFinite(value)?value:0;return `${Math.floor(value/60)}:${String(Math.floor(value%60)).padStart(2,'0')}`;};
audio.addEventListener('loadedmetadata',()=>{$('duration').textContent=time(audio.duration);});
audio.addEventListener('timeupdate',()=>{$('elapsed').textContent=time(audio.currentTime);$('seek').value=audio.duration?audio.currentTime/audio.duration*100:0;});
$('seek').addEventListener('input',e=>{if(Number.isFinite(audio.duration))audio.currentTime=e.target.value/100*audio.duration;});
$('restart').addEventListener('click',()=>{audio.currentTime=0;});
$('loop').addEventListener('click',()=>{audio.loop=!audio.loop;$('loop').setAttribute('aria-pressed',audio.loop);});
$('volume').addEventListener('input',e=>{audio.volume=Number(e.target.value);audio.muted=false;$('mute').textContent='♫';$('mute').setAttribute('aria-label','Mute');});
$('mute').addEventListener('click',()=>{audio.muted=!audio.muted;$('mute').textContent=audio.muted?'×':'♫';$('mute').setAttribute('aria-label',audio.muted?'Unmute':'Mute');});
$('modes').addEventListener('click',e=>{const button=e.target.closest('[data-mode]');if(!button)return;mode=button.dataset.mode;document.querySelectorAll('[data-mode]').forEach(b=>{b.classList.toggle('selected',b===button);b.setAttribute('aria-pressed',b===button);});$('mode-label').textContent=`${mode.toUpperCase()} / ${mode==='spectrum'?'01':mode==='wave'?'02':'03'}`;});
document.querySelectorAll('[data-mode]').forEach(b=>b.setAttribute('aria-pressed',b.classList.contains('selected')));
document.querySelectorAll('.swatch').forEach(button=>button.addEventListener('click',()=>{color=button.dataset.color;document.documentElement.style.setProperty('--accent',color);$('color-name').textContent=button.getAttribute('aria-label');document.querySelectorAll('.swatch').forEach(b=>{b.classList.toggle('selected',b===button);b.setAttribute('aria-pressed',b===button);});}));
$('fullscreen').addEventListener('click',async()=>{try{if(document.fullscreenElement)await document.exitFullscreen();else if($('visualizer').requestFullscreen)await $('visualizer').requestFullscreen();else $('message').textContent='Fullscreen is not supported by this browser.';}catch{$('message').textContent='Fullscreen is unavailable in this window.';}});
function loadFile(file){
  if(!file)return;
  if(!file.type.startsWith('audio/')&&!/\.(mp3|wav|ogg|m4a|flac|aac|webm)$/i.test(file.name)){$('message').textContent='Please choose an audio file.';return;}
  if(file.size>100*1024*1024){$('message').textContent='Please choose a file smaller than 100 MB.';return;}
  audio.pause();if(objectURL)URL.revokeObjectURL(objectURL);objectURL=URL.createObjectURL(file);audio.src=objectURL;
  $('track-title').textContent=file.name.replace(/\.[^.]+$/,'');$('track-subtitle').textContent=`Your library · ${(file.size/1024/1024).toFixed(1)} MB`;$('demo-badge').hidden=true;$('seek').value=0;$('elapsed').textContent='0:00';$('duration').textContent='0:00';$('message').textContent='Your track is ready. Press play.';
}
$('upload-button').addEventListener('click',()=>$('file').click());$('file').addEventListener('change',e=>{loadFile(e.target.files[0]);e.target.value='';});
['dragenter','dragover'].forEach(name=>$('dropzone').addEventListener(name,e=>{e.preventDefault();$('dropzone').classList.add('dragging');}));
$('dropzone').addEventListener('dragleave',e=>{if(!$('dropzone').contains(e.relatedTarget))$('dropzone').classList.remove('dragging');});
$('dropzone').addEventListener('drop',e=>{e.preventDefault();$('dropzone').classList.remove('dragging');loadFile(e.dataTransfer.files[0]);});
window.addEventListener('dragover',e=>e.preventDefault());window.addEventListener('drop',e=>e.preventDefault());
function resize(){const rect=canvas.getBoundingClientRect();width=rect.width;height=rect.height;const dpr=Math.min(devicePixelRatio||1,2);canvas.width=width*dpr;canvas.height=height*dpr;ctx.setTransform(dpr,0,0,dpr,0,0);}
new ResizeObserver(resize).observe(canvas);
function draw(timestamp){
  requestAnimationFrame(draw);if(document.hidden)return;
  const t=reducedMotion.matches?0:timestamp/1000, sensitivity=Number($('sensitivity').value);
  ctx.clearRect(0,0,width,height);
  ctx.strokeStyle='#72835412';ctx.lineWidth=1;
  for(let x=width/2%40;x<width;x+=40){ctx.beginPath();ctx.moveTo(x,0);ctx.lineTo(x,height);ctx.stroke();}
  for(let y=height%40;y<height;y+=40){ctx.beginPath();ctx.moveTo(0,y);ctx.lineTo(width,y);ctx.stroke();}
  if(analyser&&playing){analyser.getByteFrequencyData(frequencies);analyser.getByteTimeDomainData(waveform);}
  ctx.fillStyle=color;ctx.strokeStyle=color;
  ctx.shadowColor=color;ctx.shadowBlur=playing?12:6;
  if(mode==='spectrum'){
    const count=76,gap=4,total=Math.min(width*0.79,870),bar=(total-gap*(count-1))/count,base=height*0.73;
    for(let i=0;i<count;i++){
      const shape=Math.pow(Math.sin(i/(count-1)*Math.PI),1.5);
      const idle=(0.2+shape*0.72)*(0.65+0.25*Math.sin(i*0.7+t*1.5)+0.1*Math.cos(i*1.8-t));
      const energy=playing?frequencies[Math.floor(3+i*2.6)]/255:idle;
      const h=Math.max(4,Math.min(height*0.58,energy*sensitivity*height*0.53));
      const x=(width-total)/2+i*(bar+gap);ctx.globalAlpha=0.65+shape*0.35;ctx.fillRect(x,base-h,Math.max(bar,1),h);
      ctx.shadowBlur=0;const reflection=ctx.createLinearGradient(0,base+6,0,base+70);reflection.addColorStop(0,color+'25');reflection.addColorStop(1,color+'00');ctx.fillStyle=reflection;ctx.fillRect(x,base+6,Math.max(bar,1),Math.min(h*0.35,70));ctx.fillStyle=color;
    }
  }else if(mode==='wave'){
    for(let layer=0;layer<3;layer++){ctx.globalAlpha=1-layer*0.3;ctx.lineWidth=layer===0?2:1;ctx.beginPath();for(let i=0;i<=400;i++){const x=width*0.1+i/400*width*0.8,envelope=Math.sin(i/400*Math.PI);const sample=playing?(waveform[Math.floor(i/401*waveform.length)]-128)/128:Math.sin(i*0.085+t*2+layer)*Math.cos(i*0.017-t);const y=height/2+sample*height*0.24*sensitivity*envelope*(1-layer*0.2);i?ctx.lineTo(x,y):ctx.moveTo(x,y);}ctx.stroke();}
  }else{
    const radius=Math.min(width,height)*0.24;for(let i=0;i<100;i++){const a=i/100*Math.PI*2-Math.PI/2,energy=playing?frequencies[3+i*2]/255:0.22+0.2*Math.sin(i*0.3+t*2)**2,len=8+energy*radius*0.85*sensitivity;ctx.lineWidth=3;ctx.beginPath();ctx.moveTo(width/2+Math.cos(a)*radius,height/2+Math.sin(a)*radius);ctx.lineTo(width/2+Math.cos(a)*(radius+len),height/2+Math.sin(a)*(radius+len));ctx.stroke();}ctx.globalAlpha=0.25;ctx.lineWidth=1;ctx.beginPath();ctx.arc(width/2,height/2,radius-12,0,Math.PI*2);ctx.stroke();
  }
  ctx.globalAlpha=1;ctx.shadowBlur=0;
}
requestAnimationFrame(draw);
window.addEventListener('pagehide',()=>{if(objectURL)URL.revokeObjectURL(objectURL);if(demoURL)URL.revokeObjectURL(demoURL);});

How it works

HTML creates the page structure, and CSS styles the visualizer.
JavaScript uses the Web Audio API to analyse the audio’s frequency data.
The frequency values continuously update the bars, making them move with the music.

Try a change

Choose Edit code, make a small change, then press Run to preview it.

Use the source Copy buttons, choose Edit code to open the editor, or Download ZIP to save the project after a six-second countdown.

Discussion (0)

Leave a comment