Taking Web Audio Offline in iOS 6 Safari

Playing cached audio for offline use on iOS Safari has long been a challenge that has proved to be mission impossible. But with the advent of the (WebKit-only) Web Audio API, it is now finally achievable — although you still need to jump through a few hoops.

The bad news is that you still can’t cache an MP3 file using Application Cache and then simply load it using an XmlHttpRequest. Safari on iOS 6 will cache the MP3 but then refuse to play it and fail silently (how useful!).

Because the Web Audio API offers developers direct control over the AudioBuffer, you can now convert data formats on-the-fly and feed them directly to the Web Audio API for playback. For example, if you encode an MP3 file as a Base64 string, you can then decode it to an ArrayBuffer and convert the raw audio data.

In order to decode the Base64 string back into an ArrayBuffer, you’ll need to use a custom method. Check out Daniele Guerrero’s base64-binary.js as a good script that can be used exactly for this purpose. It decodes a Base64 string into a Uint8Array typed array and stores it in an ArrayBuffer.

Once this is done, you can simply decode the audio data using the Web Audio API’s decodeAudioData() method:

Currently the demo works in Safari 6, Chrome Desktop, and iOS 6 Safari. The technique has potential to work in any browser that supports Web Audio API, so hopefully Chrome Mobile can soon add support as well.

Category

Tags

This article was written by Alex Gibson. Alex is a front end developer living in Newcastle upon Tyne, United Kingdom. In his spare time, Alex maintains several open source projects on GitHub, and also help out as a co-maintainer on Mobile Boilerplate. You can also follow him on Twitter.

Great, you’ve just solved my problem. Thanks! I had spent a few hours banging my head against the wall on this one…

I have a button that sends an ajax request, and the confirmation sound needed to play on a successful response. I just put a false sound on the button click:
onclick="mySource.noteOn(1);mySource.noteOff();"

I was working on an all purpose in browser audio player recently, and I did something similar with the fileReader api. Using a file input box , I passed the data url into an audio element as source and it played just fine.
OpenSSL to do the encoding never occurred to me, great idea.

Hey, it does work perfectly under Safari, but I have trouble to encode my own mp3 file and make it work. openssl base64 encoding outputs is a several lines of fixed length, which is recognised by browser as EOF. How exactly did you encode mp3 file to base64?
ps. copy pasting that base64 value and decoding this (back into binary file) also is not working for me. Am I missing something?

Thank you! This article was super useful on a project about a year ago when I got the nasty surprise that audio wouldn’t play offline.

Revisiting the project again this year (and this article), I wanted to use kirilloid’s suggestion above but was having issues in webkit with the returned value not being a true ArrayBuffer. For whatever reason declaring the Uint8Array and ArrayBuffer separately appears to work in Chrome and Mobile Safari.