Created
September 2, 2025 14:43
-
-
Save justinledwards/0e670970919baf5e2672c79b97af6e50 to your computer and use it in GitHub Desktop.
easy to follow explanation of audio waveform code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
useEffect(() => { | |
// If we do not have audio data yet or the canvas is not available, stop here. | |
if (!lastRenderedBuffer || !waveformRef.current) return; | |
// 1) Get the canvas and its 2D drawing context. | |
const canvas = waveformRef.current; | |
const ctx = canvas.getContext("2d"); | |
// Canvas width and height in pixels. | |
const w = canvas.width; | |
const h = canvas.height; | |
// 2) Clear anything drawn before. | |
ctx.clearRect(0, 0, w, h); | |
// 3) Set up stroke style for the line we will draw. | |
ctx.lineWidth = 2; | |
ctx.strokeStyle = "#3b82f6"; | |
ctx.beginPath(); | |
// 4) Get the audio samples for the first channel. | |
// Audio samples in Web Audio are floats in the range [-1, 1]. | |
const data = lastRenderedBuffer.getChannelData(0); | |
// 5) Decide how many audio samples to skip between drawn pixels. | |
// We only have "w" pixels across the canvas, but "data.length" samples, | |
// which is usually much larger. We cannot draw every sample to every pixel, | |
// so we pick a "step" that jumps through the data. | |
// | |
// Example: if data.length is 441000 samples and the canvas is 1100 pixels wide, | |
// step will be 401. That means pixel 0 uses sample 0, pixel 1 uses sample 401, | |
// pixel 2 uses sample 802, and so on, which fits the whole sound across the width. | |
const step = Math.max(1, Math.floor(data.length / w)); | |
// 6) Choose an amplitude scale so the line does not hit the very top | |
// or bottom of the canvas. s = 0.9 means use 90% of the height. | |
const s = 0.9; | |
// 7) Draw one vertical position per pixel column. | |
for (let x = 0; x < w; x++) { | |
// Pick the sample for this column by jumping ahead "step" samples. | |
const idx = x * step; | |
// A single audio sample in [-1, 1]. | |
const v = data[idx]; | |
// 8) Map the sample to a Y position on the canvas. | |
// | |
// Audio sample range: [-1, 1] | |
// First scale the amplitude by "s": v * s is still in [-s, s] | |
// Shift into [0, 1] by adding 1 and dividing by 2: ((v * s) + 1) / 2 | |
// Canvas has origin at top, so we flip it: 1 - normalized | |
// Finally scale by canvas height "h" to get pixels. | |
let y = (1 - ((v * s + 1) / 2)) * h; | |
// Optional safety: clamp Y so it never goes outside the canvas. | |
if (y < 0) y = 0; | |
if (y > h) y = h; | |
// 9) Move the pen for the first point, then draw lines for the rest. | |
if (x === 0) ctx.moveTo(x, y); | |
else ctx.lineTo(x, y); | |
} | |
// 10) Put the line on the canvas. | |
ctx.stroke(); | |
}, [lastRenderedBuffer]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment