Thomas Fuchs
Hi, I'm Thomas Fuchs. I'm the author of Zepto.js, of script.aculo.us, and I'm a Ruby on Rails core alumnus. With Amy Hoy I'm building cheerful software, like Freckle Time Tracking and Every Time Zone and write books like Retinafy.me.
   Want me to speak at your conference? Contact me!

How to play a sound in a web browser (it ain’t easy)

March 16th, 2011

So with all the super-modern new amazing, astonishing technology at our fingertips—how hard could it be to play a single sound effect in a web browser?
The answer is: it’s hard, painful, and you can’t get it to work in all cases anyway.

Of course there’s the venerable SoundManager 2 library. But that’s 3000 lines of JavaScript code, plus a Flash file and more code, and… I just don’t think that you need 3000 lines of code just to play a sound effect. I refuse.

Why play sounds? Some user interfaces really benefit from audio feedback. In my case, it’s a feature so that people that use the Freckle Timer will get an audible ping every 15 minutes so they don’t forget that it’s still running (it’s optional of course!).

Anyway, script.aculo.us (version 1) actually came with experimental sound support, but that code is from a time when there was no HTML5, yet alone HTML5 audio yet. Let’s go with that code but update it to use HTML5 when possible.

Here’s look at various browsers and what they support:

Internet Explorer

Surprisingly, it’s pretty easy to play a mp3 on IE, even on version 6. The little known bgsound element does it for you, no questions asked. On IE9, with all the amazing HTML5 support, you can also use an audio tag, but IE9 misreports that it can play mp4 files when it can’t.

Safari

Can play mp3’s and m4a’s just fine with the audio tag, but behold: On Windows, if QuickTime is not installed also, not only will it not play any codecs, but the audio tag simply doesn’t exist, and the Audio object in JavaScript doesn’t either.

Firefox

Has support since 3.5, but it will only play OGG and WAVE files. Go figure.

Chrome

Has broad support and will play most stuff thrown at it. Also, no cross-platform differences.

Mobile devices

You can’t auto-play sounds, so forget about playing sounds as part of the UI now. On some devices (namely recent iOS devices) you can play sounds as a result of a direct user interaction (e.g. tap). It’s out of the scope of this article really.

It all boils down to:

When HTML5 is there, do this:

First, you need mp3, mp4 and ogg versions of your sound.

if("Audio" in window){
  var a = new Audio();
  if(!!(a.canPlayType && a.canPlayType('audio/ogg; codecs="vorbis"').replace(/no/, '')))
    a.src = "/sounds/ping.ogg";
  else if(!!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, '')))
    a.src = "/sounds/ping.mp3";
  else if(!!(a.canPlayType && a.canPlayType('audio/mp4; codecs="mp4a.40.2"').replace(/no/, '')))
    a.src = "/sounds/ping.m4a";
  else
    a.src = "/sounds/ping.mp3";
  
  a.autoplay = true;
  return;
}

Basically, this will attempt to play a sound through an audio tag, and knows about IE9’s problem of misreporting that it plays mp4s. If all else fails, it will still attempt to play the mp3 version of the sound.

When there’s no HTML5 around, do this

1. In case you’re on IE, use a bgsound element:

<bgsound src="/sounds/ping.mp3" loop="1" autostart="autostart">

2. Else, test for the availability of various plugins and generate object/embed tags:

// this code uses Prototype.js
if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
  Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>');
else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('Windows Media') != -1 }))
  Sound.template = new Template('<object id="sound_#{track}_#{id}" type="application/x-mplayer2" data="#{url}"></object>');
else if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('RealPlayer') != -1 }))
  Sound.template = new Template('<embed type="audio/x-pn-realaudio-plugin" style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>');

Basically, the code tries to use QuickTime, Windows Media and RealPlayer to play back the sound. You could add a Flash failover, but for that you need specially crafted Flash files, and then you get yourself into the whole Flash blocker madness. Avoid.