Frequency spectrum

주파수 스펙트럼

A sound broken down into how much energy it has at each frequency, from bass to treble — the equalizer bar graph is exactly this.

Also known as: FFTEqualizer visualizationSpectrum analyzer
···
html
<div class="wrap">
  <div class="bars" id="bars"></div>
  <button class="play" id="play" type="button" aria-label="play sound"><span class="tri"></span></button>
</div>
css
.wrap{position:relative;width:92%;height:86%;display:flex;align-items:flex-end}
.bars{display:flex;align-items:flex-end;gap:3%;width:100%;height:100%;padding-bottom:4%}
.bars i{flex:1;background:linear-gradient(180deg,var(--accent),var(--accent-3));border-radius:3px 3px 0 0;height:6%}
.play{position:absolute;right:0;top:0;width:clamp(26px,14%,38px);aspect-ratio:1;border-radius:50%;border:1px solid var(--line);background:var(--surface);display:grid;place-items:center;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.12)}
.play .tri{width:0;height:0;border-style:solid;border-width:6px 0 6px 9px;border-color:transparent transparent transparent var(--fg);margin-left:2px}
.play:active{transform:scale(.92)}
js
const N = 16;
const barsEl = document.getElementById('bars');
const bars = [];
for (let i = 0; i < N; i++) { const b = document.createElement('i'); barsEl.appendChild(b); bars.push(b); }
let playing = false, analyser = null, data = null;
function idleFrame(t) {
  if (playing && analyser) {
    analyser.getByteFrequencyData(data);
    const step = Math.floor(data.length / N) || 1;
    bars.forEach((b, i) => { b.style.height = Math.max(6, (data[i * step] / 255) * 96) + '%'; });
  } else {
    bars.forEach((b, i) => {
      const v = 10 + Math.abs(Math.sin(t * 0.0016 + i * 0.5)) * 55 * Math.exp(-i * 0.045);
      b.style.height = v + '%';
    });
  }
  requestAnimationFrame(idleFrame);
}
requestAnimationFrame(idleFrame);
document.getElementById('play').addEventListener('click', () => {
  const ctx = new (window.AudioContext || window.webkitAudioContext)();
  analyser = ctx.createAnalyser();
  analyser.fftSize = 128;
  data = new Uint8Array(analyser.frequencyBinCount);
  const master = ctx.createGain();
  const now = ctx.currentTime;
  master.gain.setValueAtTime(0.0001, now);
  master.gain.linearRampToValueAtTime(0.02, now + 0.03);
  master.gain.exponentialRampToValueAtTime(0.0001, now + 1);
  master.connect(analyser).connect(ctx.destination);
  [110, 220, 330, 440, 660, 880, 1320].forEach((f) => {
    const osc = ctx.createOscillator();
    osc.type = 'sawtooth';
    osc.frequency.value = f;
    osc.connect(master);
    osc.start(now);
    osc.stop(now + 1);
  });
  playing = true;
  setTimeout(() => { playing = false; ctx.close(); }, 1050);
});

Every sound is a mix of frequency components, and the Fast Fourier Transform (FFT) decomposes that mix into "how much energy at each frequency." The Web Audio API's AnalyserNode.getByteFrequencyData() does this in real time, powering music-player bar equalizers and "listening" animations for voice assistants.

Visualizations usually group far fewer bars (16-64) than the actual analysis resolution (1024-2048 FFT bins) — drawing every bin looks like noise and wastes CPU. Keep the sound's own volume low, but give the visualization a minimum bar height so it doesn't look silent even at low volume. A live spectrum display (streaming, calls) doubles as visual confirmation that audio is active, which helps muted users too.

Used in music player equalizers, live-stream overlays, a voice assistant's "listening" indicator, and spectrograms in audio-editing tools.

When to use

Use it whenever you need to visually confirm audio is actually playing. Reduce to 16-64 bars for readability.