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.

  1. create a namespace that wraps all your code
  2. move the business logic into an "application" object
  3. 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

  1. Download CreateJS from the Adobe CDN.
  2. Get the SoundJS source code, including minified versions of SoundJS and FlashAudioPlugin from GitHub.
  3. Read more about SoundJS in the online docs (also available in GitHub).
  4. Get involved in active community discussion.