If you are making a game with Phaser 3 then you'll have to deal with audio eventually.
Every game needs music and sound effects to show off their best self.
But Web Audio rules and best practices can be confusing for those new to working with audio on the web.
It is not quite as simple as just calling APIs to play audio files like most other platforms.
And once you get Web Audio figured out for the desktop browser… iOS comes along like the second stage of a final boss. 😨
But here's the good news: it can all be solved.
And this article will show you a system that works for both desktop and mobile browsers!
Web Audio Basics
You probably know the basics of Web Audio but we will go over some just in case.
Web Audio is usually started in a suspended
state. The browser generally won't allow audio to play without some kind of user interaction indicating that the user wanted to play the game.
This is a good thing. It prevents overeager ads hidden away in a tab you've forgotten about from suddenly starting to play audio while you are on a video call.
Phaser automatically creates an AudioContext
when your game starts even though no sounds will be played.
You've probably seen a warning like this:
The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.
Phaser will automatically resume this context when the game is given focus from user interaction.
Then later, when the player clicks on another window or switches tabs, Phaser will automatically suspend the AudioContext
. Upon regaining focus, Phaser will also automatically resume the context.
Things generally work fine at the most basic level. 👏
Detour into Playing Music Immediately
Almost all games on other platforms will start playing background music as soon as it can regardless of whether the player is ready or not.
But you cannot do this with a web game. There's no way around it and if you did find one then rest assured that the browser makers will release a fix to correct you. 😉
The best solution is to design your game so that it receives user input before it is important for audio to be playing.
Some problems should be fixed by design instead of code. This is one of those problems.
Avoiding Multiple AudioContext Warnings
It is normal and expected to see 1 AudioContext was not allowed to start.
warning in the Browser Console.
Seeing more than one is also generally okay but it is possible to get rid of them. Some among us are made uncomfortable by too many warnings.
The reason for multiple AudioContext
warnings is likely because the game is trying to play audio before the user has interacted with it.
You may have something like this in your Scene's create()
method:
|
|
Seems innocent enough and everything will likely work fine. The music will play as soon as the user interacts with your game.
But that is the cause for the extra warnings that keep you up at night. 😅
The fix is to listen for the Phaser.Sound.Events.UNLOCKED
event before calling this.music.play()
.
|
|
Doing it this way should leave you with a nice and tidy Console. 👍
The Difference with iOS
On a desktop browser, clicking on a different window will stop your game's audio. Then clicking back will resume it.
You may find that this is not the case on iOS. 😭
You can tap on your game and music will start. Go to a different tab and music will stop. So far so good. Working like a desktop browser.
But when you go back to the tab with your game and tap it… nothing happens. The music does not resume.
You can log the state
of the AudioContext
and it will report: suspended
. 🤔
If your game doesn't fill the entire browser window then you may notice that tapping on empty space outside of the game will resume audio. You can also tap on the address bar and then cancel or some other menu item and cancel to get the music to resume.
Seems like voodoo doesn't it?
This can be solved with a hack where you get a reference to the AudioContext
and then manually resume it on a POINTER_DOWN
event.
For a quick fix, this hack might be fine. But there is another approach that's cleaner and more user-friendly.
Pause when Lose Focus
One user-friendly approach is to stop the music and pause the game with a pause screen when the game loses focus.
You can use a modal or an entire Paused
Scene that runs when focus is lost.
Detecting when focus is lost is where the challenge comes in.
The standard blur
and focus
events don't fire on iOS as they do on the desktop as we described above.
Instead, we can use the visibilitychange
event with the blur
event like this:
|
|
This will let us know when our game loses focus on both desktop and mobile browsers.
One potential drawback to this approach is that you will have to manually control starting and stopping music when losing and gaining focus instead of allowing Phaser to handle it.
Phaser won't know when visibilitychange
happens and will not be able to automatically resume audio.
You can call the undocumented onBlur()
and onFocus()
methods of the WebAudioSoundManager
but we don't recommend using private APIs.
Instead, you can set pauseOnBlur
to false
like this:
|
|
This means Phaser will no longer try to pause audio on the blur
event or resume audio on the focus
event.
Instead, we will have to handle it manually like this:
|
|
The above example is assuming the existence of a Paused
Scene that can be shown when the game loses focus.
The Scene would have a Resume button that calls the onResume
callback on click. The callback is passed as data to the Scene.
The concept is to have something like this 👇
Next Steps
Now you have the basics of a system that can manage Web Audio properly across desktop and mobile!
We hope this saves you a bunch of time! 🤗
Be sure to sign up for our newsletter so you don't miss any future Phaser 3 game development tips and techniques!
Drop your email into the box below.
Don't miss any Phaser 3 game development content
Subscribers get exclusive tips & techniques not found on the blog!
Join our newsletter. It's free. We don't spam. Spamming is for jerks.