Revisting HTML5 Audio
Sat, Sep 17, 2011In my earlier HTML5 pong game I used the <audio>
tag, but the
browser support was mixed and it didn’t work consistently. In another game, breakout,
I avoided the issue by using the SoundManager2
library to fall back to flash audio when necessary.
Since then, browser support has matured (a little) and I’ve learned that some of my original
problems stem from re-using a single <audio>
tag to play the same sound multiple,
overlapping, times. So for snakes (coming soon - honest!)
I switched back to pure HTML5 <audio>
and it has been much more successful.
The key is knowing that for short sounds that need to repeat, and might overlap, you must
use a pool of <audio>
elements instead of trying to play the same one twice at the same time.
Introducing the AudioFX Javascript Library
Given that knowledge, I built a tiny audio-fx.js library that makes it easy to play either single instance music, or pooled instance sound fx:
var music = AudioFX('music', { formats: ['ogg','mp3'], autoplay: true, loop: true });
var fx = AudioFX('zap', { formats: ['ogg','mp3'], pool: 10 });
// ... later ...
if (player.shoots(alien))
fx.play();
How the Library Works
NOTE: This article is about how the audio-fx library works, and assumes you have a basic understanding of the HTML5
<audio>
element. If you need a refresher on HTML5 audio you can check out the following resources:
- W3C Spec - The Audio Element
- Mozilla - Using HTML5 Audio and Video
- Mozilla - The audio element
- Opera - Everything you need to know about HTML5 video and audio
- IEBlog - Unlocking the power of HTML5 Audio
- Article - Native Audio in the Browser
- Great chapter on AUDIO in the HTML5 Canvas book.
Browser Support
The library starts off with detecting browser support by creating an <audio>
element and asking if
it canPlayType()
:
var hasAudio = false, audio = document.createElement('audio'), audioSupported = function(type) { var s = audio.canPlayType(type); return (s === 'probably') || (s === 'maybe'); };
if (audio && audio.canPlayType) {
hasAudio = {
ogg: audioSupported('audio/ogg; codecs="vorbis"'),
mp3: audioSupported('audio/mpeg;'),
wav: audioSupported('audio/wav; codecs="1"'),
loop: (typeof audio.loop === 'boolean') // some browsers (FF) dont support loop yet
}
}
Creating an <AUDIO>
Element
The library includes a private helper function to create a single <audio>
element:
var create = function(src, options, onload) {
var audio = document.createElement('audio');
if (onload) {
var ready = function() {
audio.removeEventListener('canplay', ready, false);
onload();
}
audio.addEventListener('canplay', ready, false);
}
if (options.loop && !hasAudio.loop)
audio.addEventListener('ended', function() { audio.currentTime = 0; audio.play(); }, false);
audio.volume = options.volume || 0.2;
audio.loop = options.loop;
audio.src = src;
return audio;
}
Helper Functions
Next come 2 helper functions:
choose
- a supported format from an array of possible formats.find
- the first available<audio>
element from a pool of elements.
var choose = function(formats) {
for(var n = 0 ; n < formats.length ; n++)
if (hasAudio && hasAudio[formats[n]])
return formats[n];
};
var find = function(audios) {
var n, audio;
for(n = 0 ; n < audios.length ; n++) {
audio = audios[n];
if (audio.paused || audio.ended)
return audio;
}
};
The AudioFX
Wrapper
Finally, a wrapper class abstracts away the difference between a single <audio>
and a pool of them. It
also allows us to hide the raw HTML5 implementation and provide our own simplified API:
var afx = function(src, options, onload) {
options = options || {};
var formats = options.formats || [],
format = choose(formats),
pool = [];
src = src + (format ? '.' + format : '');
if (hasAudio) {
for(var n = 0 ; n < (options.pool || 1) ; n++)
pool.push(create(src, options, n == 0 ? onload : null));
}
else {
onload();
}
return {
audio: (pool.length == 1 ? pool[0] : pool),
play: function() {
var audio = find(pool);
if (audio)
audio.play();
},
stop: function() {
var n, audio;
for(n = 0 ; n < pool.length ; n++) {
audio = pool[n];
audio.pause();
audio.currentTime = 0;
}
}
};
};
Encapsulation
All of the previous code is hidden in a private javascript module that returns the private afx
wrapper
as the public AudioFX
object with a few helpful attributes (version
and supported
) attached:
AudioFX = function() {
// ... all of the above code
afx.version = '0.0.1';
afx.supported = hasAudio;
return afx;
}();
Example Usage
So using it is easy:
if (AudioFX.supported) {
alert('using AudioFX library version ' + AudioFX.version);
var music = AudioFX('music', { formats: ['ogg','mp3'], autoplay: true, loop: true });
var fx = AudioFX('zap', { formats: ['ogg','mp3'], pool: 10 });
// ... later ...
if (player.shoots(alien))
fx.play();
}
Caveats
While my HTML5 audio experiments for my (upcoming) snakes game have been fairly successful. Its really only because I am limiting my support to the main modern desktop browsers, IE9, Chrome13, FF5, Opera11.
Safari still has trouble playing short sounds (latency issues) so while the background music is supported the sound FX are not. And don’t get me started on mobile browser support!
So, lets be honest, HTML5 Audio is still not ready for prime time cross browser support, and going with the SoundManager2 library is still a good idea for commercial games. But for personal experiments like a snakes game (coming soon! dont stress me out man!) it might be good enough.
What about the buzz Library ?
After starting this project, I discovered the buzz library which also abstracts HTML5 audio functionality… and does it much more thoroughly than my audio-fx, but does not have support for creating an audio pool, which is very important for short, repeat, overlapping sounds.
… I should probably just be forking buzz and trying to add pooling support to it, but I only discovered that library late in the day, so might have to revisit this issue.
If you have any thoughts let me know !
Related Links
You can find the audio-fx.js library here.
How to use HTML5 Audio:
- W3C Spec - The Audio Element
- Mozilla - Using HTML5 Audio and Video
- Mozilla - The audio element
- Opera - Everything you need to know about HTML5 video and audio
- IEBlog - Unlocking the power of HTML5 Audio
- Article - Native Audio in the Browser
- Great chapter on AUDIO in the HTML5 Canvas book.
Is HTML5 Audio Ready for Prime Time ?
Other Libraries: