🎧 Audio Visualizer Gear

Hardware and books for audio-visual projects. Affiliate links help support this site.

Audio-Technica ATH-M50x Headphones

You need accurate audio monitoring when building visualizers. These are the industry standard.

Web Audio API (O'Reilly)

The definitive reference for everything in this article and beyond.

">

🎧 Audio Visualizer Gear

Hardware and books for audio-visual projects. Affiliate links help support this site.

Audio-Technica ATH-M50x Headphones

You need accurate audio monitoring when building visualizers. These are the industry standard.

Web Audio API (O'Reilly)

The definitive reference for everything in this article and beyond.

">Building Audio Visualizers - KryoMix

Building Audio Visualizers with the Web Audio API

Audio visualizers are mesmerizing—bars pulsing to beats, colors flowing with frequency content, shapes morphing in sync with sound. The Web Audio API makes it possible to create these experiences directly in the browser, tapping into the user's audio input and transforming it into visual art.

Understanding the Web Audio API

The Web Audio API is a powerful framework for processing and synthesizing audio in web applications. For visualization, the key feature is the AnalyserNode, which provides real-time frequency and time-domain data.

Here's the basic setup:

// Create audio context
const audioContext = new (window.AudioContext || window.webkitAudioContext)();

// Get user audio input
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const source = audioContext.createMediaStreamSource(stream);

// Create analyser
const analyser = audioContext.createAnalyser();
analyser.fftSize = 256;  // Fast Fourier Transform size
source.connect(analyser);

// Get frequency data
const dataArray = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(dataArray);

The fftSize determines frequency resolution. Higher values (up to 32768) give more detailed frequency breakdown but use more processing power. 256 or 512 is usually sufficient for visualizations.

Frequency vs. Time Domain Data

The Analyser provides two types of data:

Frequency Domain (Spectrum): Shows the intensity of different frequencies. It's perfect for classic equalizer-style visualizers with bars for each frequency band.

Time Domain (Waveform): Shows the audio signal's raw amplitude over time. This creates waveform-style visualizers and is useful for detecting beat onsets.

// Get frequency data
const frequencyData = new Uint8Array(analyser.frequencyBinCount);
analyser.getByteFrequencyData(frequencyData);

// Get time domain data
const timeDomainData = new Uint8Array(analyser.fftSize);
analyser.getByteTimeDomainData(timeDomainData);

Building a Simple Bar Visualizer

Let's create a classic frequency-based bar visualizer:

const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');

function drawVisualizer() {
  requestAnimationFrame(drawVisualizer);

  // Clear canvas
  ctx.fillStyle = 'rgba(10, 10, 15, 0.2)';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  // Get frequency data
  const data = new Uint8Array(analyser.frequencyBinCount);
  analyser.getByteFrequencyData(data);

  const barWidth = canvas.width / data.length;
  let x = 0;

  for (let i = 0; i < data.length; i++) {
    const barHeight = (data[i] / 255) * canvas.height;

    // Use color based on frequency
    const hue = (i / data.length) * 360;
    ctx.fillStyle = 'hsl(' + hue + ', 100%, 50%)';
    ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight);

    x += barWidth + 1;
  }
}

drawVisualizer();

Detecting Beats

Beat detection adds interactivity. A simple approach is to monitor the low-frequency energy (bass) for peaks:

class BeatDetector {
  constructor(analyser, smoothing = 0.8) {
    this.analyser = analyser;
    this.smoothing = smoothing;
    this.history = [];
    this.threshold = 0.3;
  }

  detect() {
    const data = new Uint8Array(this.analyser.frequencyBinCount);
    this.analyser.getByteFrequencyData(data);

    // Get bass energy (first 10% of frequencies)
    let bass = 0;
    for (let i = 0; i < data.length * 0.1; i++) {
      bass += data[i];
    }
    bass /= (data.length * 0.1);

    // Smooth the value
    if (this.history.length === 0) {
      this.history.push(bass);
    } else {
      const smoothed = this.history[this.history.length - 1] * this.smoothing + bass * (1 - this.smoothing);
      this.history.push(smoothed);
    }

    // Detect peak
    if (this.history.length > 2) {
      const prev = this.history[this.history.length - 2];
      const current = this.history[this.history.length - 1];
      return current > prev && current > this.threshold * 255;
    }

    return false;
  }
}

Creating a Circular Visualizer

Transform bar data into a radial pattern for a more artistic effect:

function drawRadialVisualizer() {
  requestAnimationFrame(drawRadialVisualizer);

  ctx.fillStyle = 'rgba(10, 10, 15, 0.1)';
  ctx.fillRect(0, 0, canvas.width, canvas.height);

  const data = new Uint8Array(analyser.frequencyBinCount);
  analyser.getByteFrequencyData(data);

  const centerX = canvas.width / 2;
  const centerY = canvas.height / 2;
  const radius = 100;

  for (let i = 0; i < data.length; i++) {
    const angle = (i / data.length) * Math.PI * 2;
    const barHeight = (data[i] / 255) * 100;

    const x1 = centerX + Math.cos(angle) * radius;
    const y1 = centerY + Math.sin(angle) * radius;
    const x2 = centerX + Math.cos(angle) * (radius + barHeight);
    const y2 = centerY + Math.sin(angle) * (radius + barHeight);

    const hue = (i / data.length) * 360;
    ctx.strokeStyle = 'hsl(' + hue + ', 100%, 50%)';
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.stroke();
  }
}

drawRadialVisualizer();

Smoothing and Temporal Data

Real audio data is noisy. Smooth the values over time to create more flowing visuals:

class SmoothedAnalyser {
  constructor(analyser, smoothing = 0.85) {
    this.analyser = analyser;
    this.smoothing = smoothing;
    this.smoothedData = new Uint8Array(analyser.frequencyBinCount);
  }

  getData() {
    const data = new Uint8Array(this.analyser.frequencyBinCount);
    this.analyser.getByteFrequencyData(data);

    for (let i = 0; i < data.length; i++) {
      this.smoothedData[i] =
        this.smoothedData[i] * this.smoothing + data[i] * (1 - this.smoothing);
    }

    return this.smoothedData;
  }
}

Advanced Techniques

Particle Systems: Emit particles from visualization elements, creating a richer visual experience.

WebGL Rendering: Use Three.js or Babylon.js for 3D visualizations. The analyser data can drive vertex shaders and material properties.

Color Mapping: Instead of random colors, use a palette that evolves with the music. Map frequency to hue, amplitude to brightness.

Spatial Audio: The Web Audio API supports stereo analysis. Use left and right channel data separately for asymmetric visuals.

// Stereo analysis
const leftData = new Uint8Array(analyser.frequencyBinCount);
const rightData = new Uint8Array(analyser.frequencyBinCount);
// Note: Requires splitting stereo channels via Web Audio routing

Handling Audio Files

Visualize pre-recorded audio too:

const audio = new Audio('song.mp3');
const source = audioContext.createMediaElementAudioSource(audio);
const analyser = audioContext.createAnalyser();

source.connect(analyser);
analyser.connect(audioContext.destination);

audio.play();

// Now use analyser as before
function visualize() {
  const data = new Uint8Array(analyser.frequencyBinCount);
  analyser.getByteFrequencyData(data);
  // Draw...
  requestAnimationFrame(visualize);
}

Performance Optimization

Audio visualizations run in real time. Keep it performant:

Accessibility and User Permissions

Always request microphone permission gracefully and provide fallback content:

async function setupAudio() {
  try {
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
    // Setup analyser...
  } catch (error) {
    console.error('Microphone access denied:', error);
    // Show message to user
    document.getElementById('message').textContent =
      'This visualizer requires microphone access.';
  }
}

Conclusion

Audio visualizers showcase the Web Audio API's power. Start with simple frequency-based bars, then experiment with beat detection, radial patterns, and color mapping. Combine with Canvas or WebGL for stunning effects that respond to sound in real time. The result is immersive, engaging, and uniquely yours.

← Previous: Generative ArtNext: Creative Coding for Beginners →