Mode: Pulse Rings

How this music visualizer page is made

This page is a complete browser-based JavaScript music visualizer. It uses ordinary HTML for the controls, CSS for the dark glass-style interface, the Canvas API for drawing, and the Web Audio API for reading sound from an audio file or the microphone.

HTML

Creates the sidebar controls, audio file picker, microphone button, range sliders, statistics panel and canvas drawing area.

CSS

Creates the two-column layout, dark background, rounded cards, responsive mobile layout and high-contrast focus states.

Canvas

Draws rings, waves, spirals, radial bars and particles for every animation frame.

Web Audio API

Converts sound into frequency and waveform arrays that JavaScript can use for animation.

1. Page structure

The page starts with the usual document structure: <!DOCTYPE html>, <html>, <head> and <body>. The head contains the title, description, Open Graph tags, Twitter Card tags, analytics, advertisement script, CSS and structured data. The body contains the header placeholder, the visualizer app and the footer placeholder.

2. The control panel

The left sidebar is an <aside>. It contains the audio controls and settings. The user can choose microphone input, stop the input, choose a file, change the visualizer mode, change sensitivity, change trail/fade, change zoom and save the current canvas frame as a PNG image.

<button id="micBtn">Use Microphone</button>
<input id="fileInput" type="file" accept="audio/*">
<select id="mode">...</select>
<input id="sens" type="range" min="0.5" max="3" step="0.1">

3. The canvas area

The main visual area is a <canvas>. Canvas is like a programmable drawing board. JavaScript gets the drawing context with canvas.getContext("2d"). After that, it can draw lines, circles, arcs, particles and backgrounds.

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

4. Why the canvas is resized

The function resizeCanvas() reads the visible size of the canvas and multiplies it by the device pixel ratio. This makes the animation sharper on high-resolution screens. Without this step, the canvas can look blurry.

const dpr = Math.max(1, window.devicePixelRatio || 1);
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);

5. Creating the audio system

The Web Audio API begins with an AudioContext. Inside it, this page creates an AnalyserNode. The analyser does not understand music emotionally. It simply gives numerical data: frequency values and waveform values.

audioCtx = new (window.AudioContext || window.webkitAudioContext)();
analyser = audioCtx.createAnalyser();
analyser.fftSize = 2048;
analyser.smoothingTimeConstant = 0.82;

fftSize controls how much audio data is analyzed at once. A larger value gives more detailed frequency data. smoothingTimeConstant makes the movement less jumpy.

6. Reading audio from a file

When the user chooses an audio file, the page creates a temporary browser URL with URL.createObjectURL(file). The audio player plays that file, and JavaScript connects it to the analyser. The file stays inside the browser session.

const url = URL.createObjectURL(file);
audioPlayer.src = url;
audioPlayer.play();

7. Reading audio from the microphone

When the user clicks the microphone button, the browser asks for permission. If permission is granted, navigator.mediaDevices.getUserMedia() gives a microphone stream. That stream is connected to the analyser. The page uses the data for drawing only.

micStream = await navigator.mediaDevices.getUserMedia({
  audio: true,
  video: false
});
sourceNode = audioCtx.createMediaStreamSource(micStream);
sourceNode.connect(analyser);

8. Frequency data and waveform data

The page uses two arrays. dataArray stores frequency data. Low indexes roughly represent low sounds, and higher indexes represent higher sounds. timeArray stores waveform data, which is useful for drawing wave lines.

analyser.getByteFrequencyData(dataArray);
analyser.getByteTimeDomainData(timeArray);

9. Calculating volume, bass, mid and treble

The page calculates volume by averaging the frequency array. Bass, mid and treble are calculated by averaging different sections of that array. This is not studio-grade audio engineering, but it is perfect for a learning visualizer.

volume = average of all frequency values
bass   = average of low frequency values
mid    = average of middle frequency values
treble = average of higher frequency values

10. Drawing pulse rings

The ring visualizer draws circles from the center of the canvas. Louder sound increases the circle radius and line width. Bass makes the rings feel heavier. Treble changes brightness.

ctx.beginPath();
ctx.arc(cx, cy, radius, 0, Math.PI * 2);
ctx.stroke();

11. Drawing the wave field

The wave visualizer uses waveform data. Each waveform value becomes a point on a line. The result looks like a moving sound wave across the screen.

const centered = (waveformValue - 128) / 128;
const y = h / 2 + centered * amplitude;

12. Drawing radial bars

The radial bars visualizer places many small lines around a circle. Each line uses one part of the frequency array. Higher frequency values create longer bars.

const angle = (i / bars) * Math.PI * 2;
const x1 = cx + Math.cos(angle) * radius;
const y1 = cy + Math.sin(angle) * radius;

13. Drawing particles

The particles are small circles with positions and velocities. Music energy pushes them around. When they leave one side of the canvas, they re-enter from the opposite side.

p.x += p.vx;
p.y += p.vy;
if (p.x < 0) p.x = width;

14. Animation loop

The function render() is the heart of the page. It reads fresh audio metrics, updates the visible statistics, fades the old frame, draws the selected visualizer and then asks the browser to call it again using requestAnimationFrame().

function render() {
  const m = getMetrics();
  drawBackground(fade);
  drawSelectedVisualizer(m);
  requestAnimationFrame(render);
}

15. Saving a frame

The save button converts the current canvas picture into a PNG data URL. Then JavaScript creates a temporary download link and clicks it.

const a = document.createElement("a");
a.href = canvas.toDataURL("image/png");
a.download = "visualizer-frame.png";
a.click();

16. Full screen mode

Modern browsers usually require a real user click before entering fullscreen. That is why this page uses clear Full Screen buttons instead of forcing fullscreen automatically after a timer. One button is in the sidebar and another is placed over the canvas so it remains easy to find. In fullscreen mode, an Exit Full Screen button appears over the canvas.

17. What students should learn from this page

18. Practice tasks