Web Audio API for HTML5 Games

This code accompanies a tutorial on the Web Audio API.

Test for API support

var context;

try {
  // still needed for Safari
  window.AudioContext = window.AudioContext || window.webkitAudioContext;

  // create an AudioContext
  context = new AudioContext();
} catch(e) {
  // API not supported
  throw new Error('Web Audio API not supported.');
}

Load a sound

var sound;

/**
 * Example 1: Load a sound
 * @param {String} src Url of the sound to be loaded.
 */

function loadSound(url) {
  var request = new XMLHttpRequest();
  request.open('GET', url, true);
  request.responseType = 'arraybuffer';

  request.onload = function() {
    // request.response is encoded... so decode it now
    context.decodeAudioData(request.response, function(buffer) {
      sound = buffer;
    }, function(err) {
      throw new Error(err);
    });
  }

  request.send();
}
// loadSound('audio/BaseUnderAttack.mp3');

Testing file format support

var format = '.' + (new Audio().canPlayType('audio/ogg') !== '' ? 'ogg' : 'mp3');
// loadSound('audio/baseUnderAttack' + format);

Play a sound

/**
 * Example 2: Play a sound
 * @param {Object} buffer AudioBuffer object - a loaded sound.
 */

function playSound(buffer) {
  var source = context.createBufferSource();
  source.buffer = buffer;
  source.connect(context.destination);
  source.start(0);
}
// playSound(sound);

Load multiple sounds

var sounds = {
  laser : {
    src : 'audio/laser'
  },
  coin : {
    src : 'audio/coin'
  },
  explosion : {
    src : 'audio/explosion'
  }
};


/**
 * Example 3a: Modify loadSound fn to accept changed params
 * @param {Object} obj Object containing url of sound to be loaded.
 */

function loadSoundObj(obj) {
  var request = new XMLHttpRequest();
  request.open('GET', obj.src + format, true);
  request.responseType = 'arraybuffer';

  request.onload = function() {
    // request.response is encoded... so decode it now
    context.decodeAudioData(request.response, function(buffer) {
      obj.buffer = buffer;
    }, function(err) {
      throw new Error(err);
    });
  }

  request.send();
}
// loadSoundObj({ src : 'audio/baseUnderAttack' });


/**
 * Example 3b: Function to loop through and load all sounds
 * @param {Object} obj List of sounds to loop through.
 */

function loadSounds(obj) {
  var len = obj.length, i;

  // iterate over sounds obj
  for (i in obj) {
    if (obj.hasOwnProperty(i)) {
      // load sound
      loadSoundObj(obj[i]);
    }
  }
}
// loadSounds(sounds);

Adjusting the volume

sounds = {
  laser : {
    src : 'audio/laser',
    volume : 2
  },
  coin : {
    src : 'audio/coin',
    volume : 1.5
  },
  explosion : {
    src : 'audio/explosion',
    volume : 0.5
  }
};


/**
 * Example 4: Modify the playSoundObj function to accept volume property
 * @param {Object} obj Object containing url of sound to be loaded.
 */

function playSoundObj(obj) {
  var source = context.createBufferSource();
  source.buffer = obj.buffer;

  // create a gain node
  obj.gainNode = context.createGain();

  // connect the source to the gain node
  source.connect(obj.gainNode);

  // set the gain (volume)
  obj.gainNode.gain.value = obj.volume;

  // connect gain node to destination
  obj.gainNode.connect(context.destination);

  // play sound
  source.start(0);
}
// loadSounds(sounds);



Muting a sound

var nyan = {
  src : 'audio/nyan',
  volume : 1
};
loadSoundObj(nyan);


/**
 * Example 5: Muting a sound
 * @param  {object} obj Object containing a loaded sound buffer.
 */

function muteSoundObj(obj) {
  obj.gainNode.gain.value = 0;
}
// muteSoundObj(nyan);

Looping sounds

sounds = {
  laser : {
    src : 'audio/laser',
    volume : 1,
    loop: true
  },
  coin : {
    src : 'audio/coin',
    volume : 1,
    loop: true
  },
  explosion : {
    src : 'audio/explosion',
    volume : 1,
    loop: true
  }
};


/**
 * Example 6: Modify the playSoundObj function again to accept a loop property
 * @param {Object} obj Object containing url of sound to be loaded.
 */

function playSoundObj(obj) {
  var source = context.createBufferSource();
  source.buffer = obj.buffer;

  // loop the audio?
  source.loop = obj.loop;

  // create a gain node
  obj.gainNode = context.createGain();

  // connect the source to the gain node
  source.connect(obj.gainNode);

  // set the gain (volume)
  obj.gainNode.gain.value = obj.volume;

  // connect gain node to destination
  obj.gainNode.connect(context.destination);

  // play sound
  source.start(0);
}
// loadSounds(sounds);

Crossfading sounds

var crossfade = {
  battle : {
    src : 'audio/the-last-encounter',
    volume : 1,
    loop : true
  },
  eclipse : {
    src : 'audio/red-eclipse',
    volume : 0,
    loop : true
  }
};


/**
 * Example 7: Crossfading between two sounds
 * @param  {Object} a Sound object to fade out.
 * @param  {Object} b Sound object to fade in.
 */

function crossFadeSounds(a, b) {
  var currentTime = context.currentTime,
      fadeTime = 3; // 3 seconds fade time

  // fade out
  a.gainNode.gain.linearRampToValueAtTime(1, currentTime);
  a.gainNode.gain.linearRampToValueAtTime(0, currentTime + fadeTime);

  // fade in
  b.gainNode.gain.linearRampToValueAtTime(0, currentTime);
  b.gainNode.gain.linearRampToValueAtTime(1, currentTime + fadeTime);
}
// crossFadeSounds(crossfade.battle, crossfade.eclipse);

Audio Resources

Many thanks to OpenGameArt.Org and it's contributors.