How to Build a Web-Based Audio Sampler with Real-Time Pitch and Playback Control

If you've ever wanted to load a sample in the browser and manipulate it like an instrument — think start time, pitch shifting, and looping — the Web Audio API gives you the tools. This guide walks through building a browser-based sampler with dynamic playback control using nothing but vanilla JavaScript. Use Cases for Browser-Based Samplers Interactive music experiences Drum machines or pad samplers Real-time sound design tools Step 1: Load the Sample We’ll fetch a WAV/MP3 and decode it for playback: const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); async function loadSample(url) { const res = await fetch(url); const arrayBuffer = await res.arrayBuffer(); return await audioCtx.decodeAudioData(arrayBuffer); } Step 2: Create a Sampler Function We’ll build a function that creates a playback node each time a note is triggered: function playSample(buffer, options = {}) { const source = audioCtx.createBufferSource(); source.buffer = buffer; const gain = audioCtx.createGain(); gain.gain.value = options.volume || 1.0; source.playbackRate.value = options.pitch || 1.0; if (options.loop) { source.loop = true; source.loopStart = options.loopStart || 0; source.loopEnd = options.loopEnd || buffer.duration; } source.connect(gain).connect(audioCtx.destination); source.start(audioCtx.currentTime, options.offset || 0); return source; } Step 3: Trigger with UI or Keyboard You can hook this into pads, keys, or sequencers: let sampleBuffer; loadSample('/samples/snare.wav').then(buffer => { sampleBuffer = buffer; }); document.getElementById('trigger').addEventListener('click', () => { if (sampleBuffer) { playSample(sampleBuffer, { pitch: 1.2, // speed up slightly offset: 0.05, // trim attack volume: 0.8 }); } }); Optional: Add a UI for Looping Browser samplers can support loop regions: playSample(sampleBuffer, { loop: true, loopStart: 0.2, loopEnd: 0.8, pitch: 0.8 }); Pros and Cons ✅ Pros No dependencies, loads instantly Pitch, trim, and loop all in-browser Modular and extensible (add envelopes, filters, UI) ⚠️ Cons Limited real-time editing without AudioWorklet No time-stretching — pitch alters speed Needs care to prevent overlapping or memory leaks

Apr 24, 2025 - 02:49
 0
How to Build a Web-Based Audio Sampler with Real-Time Pitch and Playback Control

If you've ever wanted to load a sample in the browser and manipulate it like an instrument — think start time, pitch shifting, and looping — the Web Audio API gives you the tools. This guide walks through building a browser-based sampler with dynamic playback control using nothing but vanilla JavaScript.

Use Cases for Browser-Based Samplers


  • Interactive music experiences
  • Drum machines or pad samplers
  • Real-time sound design tools

Step 1: Load the Sample


We’ll fetch a WAV/MP3 and decode it for playback:

const audioCtx = new (window.AudioContext || window.webkitAudioContext)();

async function loadSample(url) {
const res = await fetch(url);
const arrayBuffer = await res.arrayBuffer();
return await audioCtx.decodeAudioData(arrayBuffer);
}

Step 2: Create a Sampler Function


We’ll build a function that creates a playback node each time a note is triggered:

function playSample(buffer, options = {}) {
const source = audioCtx.createBufferSource();
source.buffer = buffer;

const gain = audioCtx.createGain();
gain.gain.value = options.volume || 1.0;

source.playbackRate.value = options.pitch || 1.0;

if (options.loop) {
source.loop = true;
source.loopStart = options.loopStart || 0;
source.loopEnd = options.loopEnd || buffer.duration;
}

source.connect(gain).connect(audioCtx.destination);
source.start(audioCtx.currentTime, options.offset || 0);
return source;
}

Step 3: Trigger with UI or Keyboard


You can hook this into pads, keys, or sequencers:

let sampleBuffer;

loadSample('/samples/snare.wav').then(buffer => {
sampleBuffer = buffer;
});

document.getElementById('trigger').addEventListener('click', () => {
if (sampleBuffer) {
playSample(sampleBuffer, {
pitch: 1.2, // speed up slightly
offset: 0.05, // trim attack
volume: 0.8
});
}
});

Optional: Add a UI for Looping


Browser samplers can support loop regions:

playSample(sampleBuffer, {
loop: true,
loopStart: 0.2,
loopEnd: 0.8,
pitch: 0.8
});

Pros and Cons

✅ Pros


  • No dependencies, loads instantly
  • Pitch, trim, and loop all in-browser
  • Modular and extensible (add envelopes, filters, UI)

⚠️ Cons


  • Limited real-time editing without AudioWorklet
  • No time-stretching — pitch alters speed
  • Needs care to prevent overlapping or memory leaks