Web Audio API how-to: Playing audio based on user interaction

One thing the Web Audio API does particularly well is play sound. Of course, this is something you’d expect from an audio API :). That said, the API is complex and it’s not immediately obvious on the best way to do something simple like load a sound file and play it based on a button click. That task alone can involve a number of new platform features likes XHR2, FileReader API, and ArrayBuffers.

So…I threw together a quick example on how to load a audio file and play/stop it based on the user clicking a button:

<!DOCTYPE html>
<!-- Author: Eric Bidelman (ericbidelman@chromium.org) -->
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="chrome=1" />
<title>Web Audio API: Simple load + play</title>
</head>
<body>
<p>Example of using the Web Audio API to load a sound file
and start playing on user-click.</p>
<input type="file" accept="audio/*">
<button onclick="playSound()" disabled>Start</button>
<button onclick="stopSound()" disabled>Stop</button>
<script>
var context = new window.webkitAudioContext();
var source = null;
var audioBuffer = null;

function stopSound() {
if (source) {
  source.noteOff(0);
}
}

function playSound() {
// source is global so we can call .noteOff() later.
source = context.createBufferSource();
source.buffer = audioBuffer;
source.loop = false;
source.connect(context.destination);
source.noteOn(0); // Play immediately.
}

function initSound(arrayBuffer) {
context.decodeAudioData(arrayBuffer, function(buffer) {
  // audioBuffer is global to reuse the decoded audio later.
  audioBuffer = buffer;
  var buttons = document.querySelectorAll('button');
  buttons[0].disabled = false;
  buttons[1].disabled = false;
}, function(e) {
  console.log('Error decoding file', e);
});
}

// User selects file, read it as an ArrayBuffer and pass to the API.
var fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', function(e) {
var reader = new FileReader();
reader.onload = function(e) {
  initSound(this.result);
};
reader.readAsArrayBuffer(this.files[0]);
}, false);

// Load file from a URL as an ArrayBuffer.
// Example: loading via xhr2: loadSoundFile('sounds/test.mp3');
function loadSoundFile(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
  initSound(this.response); // this.response is an ArrayBuffer.
};
xhr.send();
}
</script>
</body>
</html>

Demo

As you can see, this example includes two different ways to get an audio file into the Web Audio API: via an <input type="file"> and an XMLHttpRequest. Both methods call initSound(), which is passed an ArrayBuffer containing the audio file. That method then decodes the audio and stores the result in a global variable (so it can be reused as play/stop are pressed).

That’s it! Straightforward once you see it, right!?