Web Audio API Frequency Data to SVG Paths

Real-time audio visualization in the browser can be achieved by extracting frequency data using the Web Audio API and converting those values into coordinate points for Scalable Vector Graphics (SVG). By connecting an audio source to an AnalyserNode, capturing raw frequency arrays via an animation loop, and mapping those values into an SVG <path> element’s d attribute, developers can render smooth, responsive vector visualizations without relying on HTML5 Canvas.

1. Initializing the Web Audio API and AnalyserNode

To access real-time frequency data, initialize an AudioContext and create an AnalyserNode. The analyser performs Fast Fourier Transform (FFT) algorithms on the incoming audio signal to determine the amplitude across various frequency bins.

const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const audioElement = document.querySelector('audio');
const source = audioContext.createMediaElementSource(audioElement);

const analyser = audioContext.createAnalyser();
analyser.fftSize = 64; // Determines frequencyBinCount (fftSize / 2)

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

const bufferLength = analyser.frequencyBinCount;
const dataArray = new Uint8Array(bufferLength);

2. Mapping Frequency Values to SVG Coordinates

The getByteFrequencyData() method populates a Uint8Array with amplitude values ranging from 0 to 255 for each frequency bin. To map these values to an SVG viewbox:

  1. Normalize X-Coordinates: Divide the SVG width evenly across the number of data points (bufferLength).
  2. Normalize Y-Coordinates: Scale the 0–255 frequency values to fit the SVG’s height. In SVG coordinate systems, (0, 0) is the top-left corner, meaning higher amplitudes must be inverted to rise from the bottom.
function generatePathData(dataArray, width, height) {
  const sliceWidth = width / (dataArray.length - 1);
  let pathD = '';

  for (let i = 0; i < dataArray.length; i++) {
    const x = i * sliceWidth;
    // Invert and scale the 0-255 range to the SVG height
    const y = height - (dataArray[i] / 255) * height;

    if (i === 0) {
      pathD += `M ${x} ${y}`;
    } else {
      pathD += ` L ${x} ${y}`;
    }
  }

  return pathD;
}

3. Rendering via requestAnimationFrame

Use requestAnimationFrame to create an update loop that continually reads the latest frequency data, builds the SVG path string, and updates the d attribute of the <path> element.

const svgPath = document.querySelector('#visualizer-path');
const svgWidth = 500;
const svgHeight = 200;

function render() {
  requestAnimationFrame(render);

  analyser.getByteFrequencyData(dataArray);

  const d = generatePathData(dataArray, svgWidth, svgHeight);
  svgPath.setAttribute('d', d);
}

// Start rendering after audio playback begins
audioElement.onplay = () => {
  if (audioContext.state === 'suspended') {
    audioContext.resume();
  }
  render();
};

4. Smoothing and Optimization