Sound effects in JavaScript / HTML5

Sound effects in JavaScript / HTML5

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

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

Solutions/Answers:

Solution 1:

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.

Solution 2:

WebAudio API by W3C

As of July 2012, the WebAudio API is now supported in Chrome, and at least partly supported in Firefox, and is slated to be added to IOS as of version 6.

Although it is robust enough to be used programatically for basic tasks, the Audio element was never meant to provide full audio support for games, etc. It was designed to allow a single piece of media to be embedded in a page, similar to an img tag. There are a lot of issues with trying to use the Audio tag for games:

  • Timing slips are common with Audio elements
  • You need an Audio element for each instance of a sound
  • Load events aren’t totally reliable, yet
  • No common volume controls, no fading, no filters/effects

I used this Getting Started With WebAudio article to get started with the WebAudio API. The FieldRunners WebAudio Case Study is also a good read.

Solution 3:

howler.js

For game authoring, one of the best solutions is to use a library which solves the many problems we face when writing code for the web, such as howler.js. howler.js abstracts the great (but low-level) Web Audio API into an easy to use framework. It will attempt to fall back to HTML5 Audio Element if Web Audio API is unavailable.

var sound = new Howl({
  urls: ['sound.mp3', 'sound.ogg']
}).play();
// it also provides calls for spatial/3d audio effects (most browsers)
sound.pos3d(0.1,0.3,0.5);

wad.js

Another great library is wad.js, which is especially useful for producing synth audio, such as music and effects. For example:

var saw = new Wad({source : 'sawtooth'})
saw.play({
    volume  : 0.8,
    wait    : 0,     // Time in seconds between calling play() and actually triggering the note.
    loop    : false, // This overrides the value for loop on the constructor, if it was set. 
    pitch   : 'A4',  // A4 is 440 hertz.
    label   : 'A',   // A label that identifies this note.
    env     : {hold : 9001},
    panning : [1, -1, 10],
    filter  : {frequency : 900},
    delay   : {delayTime : .8}
})

Sound for Games

Another library similar to Wad.js is “Sound for Games“, it has more focus on effects production, while providing a similar set of functionality through a relatively distinct (and perhaps more concise feeling) API:

function shootSound() {
  soundEffect(
    1046.5,           //frequency
    0,                //attack
    0.3,              //decay
    "sawtooth",       //waveform
    1,                //Volume
    -0.8,             //pan
    0,                //wait before playing
    1200,             //pitch bend amount
    false,            //reverse bend
    0,                //random pitch range
    25,               //dissonance
    [0.2, 0.2, 2000], //echo array: [delay, feedback, filter]
    undefined         //reverb array: [duration, decay, reverse?]
  );
}

Summary

Each of these libraries are worth a look, whether you need to play back a single sound file, or perhaps create your own html-based music editor, effects generator, or video game.

Solution 4:

You may also want to use this to detect HTML 5 audio in some cases:

http://diveintohtml5.ep.io/everything.html

HTML 5 JS Detect function

function supportsAudio()
{
    var a = document.createElement('audio'); 
    return !!(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
}

Solution 5:

Here’s one method for making it possible to play even same sound simultaneously. Combine with preloader, and you’re all set. This works with Firefox 17.0.1 at least, haven’t tested it with anything else yet.

// collection of sounds that are playing
var playing={};
// collection of sounds
var sounds={step:"knock.ogg",throw:"swing.ogg"};

// function that is used to play sounds
function player(x)
{
    var a,b;
    b=new Date();
    a=x+b.getTime();
    playing[a]=new Audio(sounds[x]);
    // with this we prevent playing-object from becoming a memory-monster:
    playing[a].onended=function(){delete playing[a]};
    playing[a].play();
}

Bind this to a keyboard key, and enjoy:

player("step");

Solution 6:

Sounds like what you want is multi-channel sounds. Let’s suppose you have 4 channels (like on really old 16-bit games), I haven’t got round to playing with the HTML5 audio feature yet, but don’t you just need 4 <audio> elements, and cycle which is used to play the next sound effect? Have you tried that? What happens? If it works: To play more sounds simultaneously, just add more <audio> elements.

I have done this before without the HTML5 <audio> element, using a little Flash object from http://flash-mp3-player.net/ – I wrote a music quiz (http://webdeavour.appspot.com/) and used it to play clips of music when the user clicked the button for the question. Initially I had one player per question, and it was possible to play them over the top of each other, so I changed it so there was only one player, which I pointed at different music clips.