Saturday, July 07, 2012

Enabling HTML5 Audio Playback on iOS Safari

I hate wearing headphones. :-P
Building off of a few things we've learned about audio on mobile devices, here's a convenient function for enabling audio on iOS devices. Essentially, as described in our first foray into audio issues on mobile devices, iOS will not play audio without it being first invoked by the user.

The following function takes a DOM element and assigns it an HTML5 audio tag to play when the DOM element is tapped. This hides the first user-invoked "audio.play()" behind another button so that it can now be invoked by code for the rest of the game without explicit user interaction. For example, in our PBS Kids games, we have a "Play!" button on the opening screen, assign our voice-over soundtrack to this button, and once a child taps "Play!", the game is thereafter able to play voice over clips without the need for additional user interaction.

function enableAudio(element, audio, onEnd){
  var callback = false,
      click    = false;

  click = function(e){
    var forceStop = function () {
          audio.removeEventListener('play', forceStop, false);
          audio.pause();
          element.removeEventListener('touchstart', click, false);
          if(onEnd) onEnd();
        },
        progress  = function () {
          audio.removeEventListener('canplaythrough', progress, false);
          if (callback) callback();
        };

    audio.addEventListener('play', forceStop, false);
    audio.addEventListener('canplaythrough', progress, false);
    try { 
      audio.play();
    } catch (e) {
      callback = function () {
        callback = false;
        audio.play();
      };
    }
  };
  element.addEventListener('touchstart', click, false);
}

To use the above, call the function giving it a clickable DOM element and an audio element to invoke as shown below:

enableAudio(element,audio);

As a bonus, if you want to make sure the audio is indeed ready before performing a next action, you can send a function as the third argument; this function will be called once the audio is ready.