While putting together an HTML5 prototype for a client, we ran into an unexpected hurdle:
Apple has disabled programmatic playback of audio and video in HTML5. Essentially, the only way a webpage can play audio or video for a user, is if that user clicks a play button.
This works:
<a href="javascript:audio.play()">Play</a>
This doesn’t:
<script>
setTimeout("audio.play()", 1000);
</script>
Despite all of Apple’s statements about being pro web standards, they have crippled HTML5 as an App platform. Here is their reasoning:
In Safari on iOS (for all devices, including iPad), where the user may be on a cellular network and be charged per data unit, preload and autoplay are disabled. No data is loaded until the user initiates it.
Source
Even though they state autoplay is disabled, all JavaScript initiated play() calls are ignored in MobileSafari.
That reasoning is hogwash. Two out of the three iOS devices are sold wifi only (iPad & iPod Touch). This business decision (not a technical one, mind you) completely eliminates an entire class of web applications. Those that would mimic native Apps found in the Apple App Store.
Awesome HTML5 Games like: Bio Lab and HTML5 game engines like: Impact are now useless on iOS. A simple thing like making a click sound when a user touches a button and playing background audio at the same time will not work.
While desperately searching for workarounds, I’ve found a few possible solutions. However, as of iOS 4.2.1, even these are now all disabled by Apple. There has been surprisingly little noise about this. I suspect that it has flown under the radar so far.
Technical Details
I spent quite a bit of time hunting down possible workarounds. Here is what I found (applies to both audio and video):
- MobileSafari will initiate a audiotag.load() inline when audiotag.play() is called without audiotag.load() called first.
- MobileSafari will not allow audiotag.load() to be called by asynchronous javascript. (i.e., not initiated by a user action.)
- You must call audiotag.load() then audiotag.play() on MobileSafari. Because load() has to be user initiated, we must call load() on every audio track that will be played in the current chapter. (A.K.A., preloading)
- Preloading using existing methods does not work because MobileSafari does not send onload events. We must listen to the progress event to determine an asset as loaded. (Platform specific workaround)
- MobileSafari will give a “stalled” event when trying to preload a second audio track after the first is loaded. It will not load the second track. Calls to play() on this track will fail silently. (Likely, this is a memory issue. MobileSafari is working with 256 MB of memory, it probably can’t hold more than a few tracks in memory and, thus, will not allow more than one audio/video track loaded. We would typically just not preload the audio tracks. However, because of #2, we are now in Game Over Man)
As a last resort, I created a shell iPad App that had a single UIWebView. You can use these properties:
UIWebView.allowsInlineMediaPlayback = YES; UIWebView.mediaPlaybackRequiresUserAction = NO;
This will allow you to work around the issue. But, hey, we are no longer an HTML5 app available online now are we?






