lowLag.js: responsive html5 audio
"a simple wrapper for low-latency, high-compatibility,
html5-friendly audio"
Audio on the web is tricky. The <audio> tag is gaining popularity and support, but it is often slow for things that demand quick response (such as games) and on some systems has weak support for playing multiple sounds at once (like in games) . SoundManager2 is comprehensive and awesome (a js/Flash library that falls back to the <audio> tag if it has to) but the lag is notable, especially on Chrome. Finally, WebKit audio is terrific -- on the browsers that support it.
lowLag.js is a wrapper for 3 different best practices:
- WebKit Audio (for Chrome and recent/upcoming versions of Safari),
- the <audio> tag (for Firefox)
- SoundManager2 (for Flash support in IE, and as an audio tag fallback for everything else)
Download
You need two files:
(You also need jQuery, or a link to it.) I recommend unzipping that into the directory your html file is in. (So that the relative paths sm2/js/ and sm2/swf are available to the script... otherwise you will have to do some configuration, see below.)Usage
Simplest usage:
lowLag.init(); lowLag.load("pluck.mp3"); lowLag.play("pluck.mp3");That would be (pretty much) the whole story, except... Firefox doesn't support MP3s. So lowLag.js borrows the concept of a sound group - a set of identical but different format sound URLs that you label and then play via a tag. All 3 modes support this (more or less: Webkit just plays the first one in the group, but you don't have to change any code.) Sample:
lowLag.load(["pluck.mp3","pluck.ogg"],"pluck"); lowLag.play("pluck");Including an mp3 and ogg version gets you very high percentage coverage across browsers.
Live Demo
In the $(document).ready(), we have run the following:
lowLag.init({'urlPrefix':'plucks/'});(If we had delayed on the init step, sm2 would have tried to run uninitialized, and panicked if it couldn't find its supporting files.) The urlPrefix tells lowLag to look in the "plucks/" directory for its sound files. We then loaded 2 sound groups and assigned a tag to each one...
lowLag.load(['pluck.mp3','pluck.ogg'],'pluck1'); lowLag.load(['pluck2.mp3','pluck2.ogg'],'pluck2');We can now play either clip or both of them together:
I have also created a playground page. This page allows you to force different modes (sm2, audio tag, etc) as well as test playback inside a javascript timeout(), and on page load. It also includes a nifty little drum machine demo.
Installation
Unzip the sm2.zip file and move things so its contents can be accessed by the html5 page. Do similar for the file lowLag.js.
Add this to the top of your html file:
<script src="/path/to/lowLag.js"></script> <script src="/path/to/soundmanager2.js"></script> <script src="//code.jquery.com/jquery-1.8.0.min.js"></script>(You don't need the final line if you are already using jQuery on your page.) Finally, if the swf directory is something other than "sm2/swf" relative to your html file, you sill have to set the sm2url parameter when you call lowLag.init().
Command Options
lowLag.init() takes one optional argument, a hash of configuration options:- force
- Value can be 'webkitAudio', 'audioTag', or 'sm2'. With no argument, lowLag.js will pick the best for the current browser (webkit for Browser that support that as a feature, the <audio> tag if it detects Firefox (preferred over SoundManager2 for performance reasons) and finally SoundManager2, which will first try Flash and fallback to the <audio> tag.)
- urlPrefix
- The value of this key will be directly prefixed to URLs when loading sound files. (Default "", i.e. same directory as the html file)
- audioTagTimeToLive
- To allow for simultaneous sounds, <audio> tags are cloned and then destroyed. This value is the time (in millis) before the file is destroyed. Default is 5000, or 5 seconds: if your clips are longer than that you may wish to adjust this value accordingly.
- sm2url
- Location of the directory for Soundmanager2's .swf files... defaults to 'sm2/swf/'
- debug
- Values can be 'console', 'page', 'both' or 'none'-- determines where information gets sent. Default to 'console'.
lowLag.load(urls,tag) "urls" can be a string (containing a single sound file URL) or an array (containing a group of the same sound in different file formats) Tag is optional: if present, it will be used by the "play()" command. If absent, "urls" as a string will be used, or the first entry in the urls array. (Note: webkit Audio will only use the first file in a urls group, so order things accordingly. (i.e. start with .mp3))
lowLag.play(tag) Play the sound associated with this tag.
Caveats
- You might have trouble with certain browser/mode combinations if you are trying to read the sound files from your local filesystem, rather than from a server... Google Chrome in particular has some stringent requirements for data, but so does IE... one workaround is to use Python (OSX should have it pre-installed) and run a micro webserver as described here.
- If your sound clips are longer than 5 seconds, be sure to set audioTagTimeToLive to something more than 5000 in lowLag.init()!
- Some browsers decline to play audio if it is triggered by the "onLoad()" or similar, without user intervention. You may wish to test across browsers, especially if you are auto-playing something.
History/Rationale
As you may have guess from the introduction, I wanted sound effects for my HTML5 games,and was frustrated by a lack of a one-stop-shopping low-lag audio solution.
Anyway, here is a table showing browsers I've tested various audio solutions on, and what file formats they support:
<audio> | SM2 | webAudio | Format: | .mp3 | .ogg | .m4a | .wav | |
---|---|---|---|---|---|---|---|---|
Chrome | GOOD* | SLOW | GOOD | (webAudio) | Y | Y | Y | Y |
Firefox | GOOD | SLOW | -- | (audioTag) | N | Y | N | Y |
IE9 | SLOW | OK | -- | (sm2 flash) | Y | N | N | N |
Safari (OSX) | SLOW | OK | GOOD | (webkit) | Y | N | Y | Y |
iOS 5 | TERRIBLE | TERRIBLE | -- | (sm2 html5) | Y | N | Y | Y |
iOS 6 | GOOD | SLOW | GOOD | (webkit) | Y | N | Y | Y |
Acknowledgements
Thanks to Darius Kazemi for pointing me to SoundManager2 and providing example Webkit code and Kabir Hemrajani for running a test on an iOS6 simulator for me. I got the implementation of <audio> cloning from http://html5doctor.com/native-audio-in-the-browser/. And of course many thanks to the SoundManager 2 project for being so cool.