SoundJS: Mobile Safe Approach
Synopsis: An approach to playing audio that also works on mobile devices
Topics: sound, play, mobile safe approaches, best practices
Target: SoundJS 0.5.2+
This tutorial is part of the SoundJS GitHub repository.
Check out the repository for more tutorials and a handful of helpful samples.
Introduction
This tutorial will outline techniques for playing audio that work on the majority of devices and browsers, and specifically allows playback on mobile devices (phones/tablets) where security limitations often cause problems. It assumes you have read the Basics and Best Practices tutorial and uses the HTML built with it as a starting point. The end result will be similar to the MobileSafe.html example. Your starting html document should look like this:
Playing on Touch
A limitation that most mobile devices share is requiring media playback to be launched by a user initiated event, such as click, touch, or key press. Reportedly, this is to protect users from annoying sites and to prevent sites from automatically using bandwidth for users with limited data plans. If all you need to do is play audio in response to user interaction, then this is trivial. Just play each sound in a function that is triggered by user events.
For our first example, we'll play audio in direct response to a user initiated touch event. This is simple to execute, and works everywhere we've tested. First, we want add a message to our <body> before all the scripts are loaded to give ourselves and anyone else using the code some feedback.
Sending a Message
The next thing we do is get a JavaScript reference to the html message so we can alter it. We add a
variable displayMessage
to our inline script, and inside of the init function use the DOM
method getElementByID()
to assign it a value. It is a best practice to store off references to
the DOM that you plan to reuse because getting values from the DOM, indeed any form of walking the DOM, is
expensive.
Wait for Touch
Now we want to change our handleLoad
function to add a click listener to our display message and
to update the message to suggest the user touch the screen. We add the click listener to the
displayMessage
element and not right to the document because document click does not work on iOS
devices (iPhone, iPad, and iPod).
Handle Click
You may have already noticed the handleClick
function being passed with the click listener.
Unsurprisingly, we now want to create a handleClick
function. In this function we will again update
the feedback message for the user, which can be especially useful when things are not working on mobile devices
with no error console. Next we play sounds using our old friend
Sound.play
. Remember back in the Basics tutorial
when we passed in ids when registering our audio? Now is when that pays off. Instead of trying to remember the
big long audio path and name string, or store it off as a global variable, we can simple pass in the
easy-to-remember ids we set. Fabulous, easy, and readable!
Ready to Rock
And that's all there is to it, you can now make sound on mobile devices. However, this only works in response to user events, so your code has to be geared towards responding to user initiated events. If that's all you need, this is the easy way to do it.
Pro-tip: It's worth noting that as long as you have a user initiated event in your immediate call stack, you can play sound. This helps lead to the next more flexible solution. This is what your code should look like at this point.
Playing After Touch
From this point on, things are going to get a little more complex. The payoff is that you will have an easier to maintain codebase that will work virtually everywhere. As you now know, if you have a touch event in your call stack, sounds will play on mobile devices. We are going to take advantage of this by launching our entire application (the JavaScript code) from a touch event. You may be asking "can it really be that easy?" Well, no, but that's why we have this tutorial!
Namespace and Closure
The first change we want to make is to set up our code more like an application. If you are building something more complex, hopefully you have already taken these steps.
- create a namespace that wraps all your code
- move the business logic into an "application" object
- wrap the application object in a function closure
Some of you might be thinking "Whoa! Namespace? Closure? Wait, what?" Don't worry, when you see the code it will make more sense. And this is a good excuse to learn about namespaces and closures, both of which are useful in JavaScript application development. Another best practice would be to move this code into it's own JavaScript file to separate it from the HTML file, but also so it can be cached by browsers - but as stated previously, inline code makes our demonstration easier. Please note that there are lots of ways to do namespacing, and many different approaches to application formatting. The way we have advocated is inline with CreateJS coding standards.
After our code has been migrated, we need to change the syntax to make the functions members of our application.
Change Scope
Previously, our displayMessage
variable and functions were global, declared on the window object.
Our scope has changed, because they are now part of the MyApp
object, so we want to reference them
as object properties using this
syntax. For example, instead of assigning a value to displayMessage
,
we now assign a value to this.displayMessage
.
We'll also add feedback to let users know when we are waiting for audio to load. We change handleLoad
to play the loaded sounds, and provide some feedback to the user about what we are playing. Note that for this
technique to work on iOS devices, you need to initialize the Sound plugins in the init
function. We
no longer use the handleClick
function in our new MyApp
Object, so we remove it.
Launch App
Next, we make a new init
function to kick off our app, which will still be called by our <body>
tag onload
function. Our new kickoff code is going to launch the app inside of a click event.
Obviously, we will need to add a new handleClick
function, which will remove the click listener and
start our app. Removing listeners when you don't plan to use them again is a best practice, and assists with
garbage collection in your application.
Apply Application Scope
So now our application will be launch inside of a touch scope, which allows audio to play. We need to do one
more essential step: Currently, our handleLoad
function is not called in the MyApp scope. That
means it will not have access to the MyApp object (this), and that the audio is not played inside of the touch
scope. We need this function to execute in our application scope to fix these problems, which we do using the
handy createjs.proxy
method. Createjs.proxy uses
Function.apply
to set the scope that a function executes in, setting the reference of "this".
Function scope is a very important topic of JavaScript application development, so it can be worth investing
some time in research to understand it better.
Ready to Rock Everywhere
And that's all there is to it, you can now play audio on most mobile devices! If you already have an amazing application using SoundJS that you want to work on mobile devices, you can just launch it inside of a touch event and make sure sound playback is called in your application scope.
To improve this approach further, you can add browser agent sniffing to determine the browser and device you are on, and only require a touch to start when on mobile devices. Adding some style to the user feedback message would also be a nice touch. Your final code should look like the following:
Conclusion
This wraps up the tutorial on approaches you can use to play audio on the broadest possible range of devices and browsers. The above example has been successfully tested on Mobile Safari and Android Chrome, Firefox, Opera, and Stock Android browsers, as well as all major desktop browsers. Good places to learn more are the SoundJS online documentation and GitHub examples. A great place to get help or discuss SoundJS is the SoundJS Community page.
Hope that helps!
Related Links
- Download CreateJS from the Adobe CDN.
- Get the SoundJS source code, including minified versions of SoundJS and FlashAudioPlugin from GitHub.
- Read more about SoundJS in the online docs (also available in GitHub).
- Get involved in active community discussion.