Sound effects in JavaScript / HTML5

html, html5-audio, javascript

Solution

HTML5 `Audio` objects

You don't need to bother with `<audio>` elements. HTML 5 lets you access `Audio` objects directly:

var snd = new Audio("file.wav"); // buffers automatically when created
snd.play();

There's no support for mixing in current version of the spec.

To play same sound multiple times, create multiple instances of the `Audio` object. You could also set `snd.currentTime=0` on the object after it finishes playing.

Since the JS constructor doesn't support fallback `<source>` elements, you should use

(new Audio()).canPlayType("audio/ogg; codecs=vorbis")

to test whether the browser supports Ogg Vorbis.

If you're writing a game or a music app (more than just a player), you'll want to use more advanced Web Audio API, which is now supported by most browsers.

Problem

I'm using HTML5 to program games; the obstacle I've run into now is how to play sound effects. The specific requirements are few in number: - Play and mix multiple sounds, - Play the same sample multiple times, possibly overlapping playbacks, - Interrupt playback of a sample at any point, - Preferably play WAV files containing (low quality) raw PCM, but I can convert these, of course. My first approach was to use the HTML5 `<audio>` element and define all sound effects in my page. Firefox plays the WAV files just peachy, but calling `#play` multiple times doesn't really play the sample multiple times. From my understanding of the HTML5 spec, the `<audio>` element also tracks playback state, so that explains why. My immediate thought was to clone the audio elements, so I created the following tiny JavaScript library to do that for me (depends on jQuery): ``` var Snd = { init: function() { $("audio").each(function() { var src = this.getAttribute('src'); if (src.substring(0, 4) !== "snd/") { return; } // Cut out the basename (strip directory and extension) var name = src.substring(4, src.length - 4); // Create the helper function, which clones the audio object and plays it var Constructor = function() {}; Constructor.prototype = this; Snd[name] = function() { var clone = new Constructor(); clone.play(); // Return the cloned element, so the caller can interrupt the sound effect return clone; }; }); } }; ``` So now I can do `Snd.boom();` from the Firebug console and play `snd/boom.wav`, but I still can't play the same sample multiple times. It seems that the `<audio>` element is really more of a streaming feature rather than something to play sound effects with. Is there a clever way to make this happen that I'm missing, preferably using only HTML5 and JavaScript? I should also mention that, my test environment is Firefox 3.5 on Ubuntu 9.10. The other browsers I've tried - Opera, Midori, Chromium, Epiphany - produced varying results. Some don't play anything, and some throw exceptions.

Original source