Phaser Audio Delay
This has happened to me so many times. I’m facing a Phaser audio delay again! I’ve got the game ready to go! The art assets are in, the scoreboards are working and the game has to launch tomorrow! I quickly throw in some background music and put it in a loop. The game starts, but where is the music???
Wait 3 seconds…
There it is! It is an annoying delay, but we’ve got to launch so I ignore it. What else can I do?
The Problem
Now that I’m not under the gun for a deadline, let’s step back and look at what is happening. I preload the music so why the delay?
I’ve put together a sample to show you the problem. Here is the code as I normally would load music. We will place an image so you can see that the images are loading at a normal speed. Also included is a timer so you can see how many seconds pass before the delay.
var StateMain = { preload: function() { game.load.audio("music", "audio/music.mp3"); game.load.image("title", "images/Cool-Game-Title.png"); }, create: function() { this.secs = 0; //create the music var music = game.add.audio("music"); //play the music music.play(); // // // //put the title on screen var title = game.add.image(game.width / 2, game.height / 2, "title"); title.anchor.set(0.5, 0.5); //make the timer text this.timeText = game.add.text(game.width / 2, 100, "0",{fill:'#ffffff'}); this.timeText.anchor.set(0.5, 0.5); //start counting seconds game.time.events.loop(Phaser.Timer.SECOND, this.tick, this); }, tick: function() { this.secs++; this.timeText.text = this.secs; }, update: function() {} }
The Reason
So what’s going on here? What is causing this Phaser audio delay anyway? Why is it taking a few seconds to play even if it is preloaded? Well, I found some answers in this post that points out that as well as needing to be loaded, the audio also needs to be decoded. This is what the browser is doing while we are waiting for it to play.
The Solution
Phaser has the capability to detect if a music file has been decoded or not.
game.cache.isSoundDecoded('music');
That statement will return a true or false. So the solution we can use is to load everything on a separate state called stateLoad. After the preload is done, we will use the update function to check if the music is decoded. If it is, we will switch states to stateMain. It is a well-accepted fact in the game industry that the user is more willing to wait on a loading screen than they are after the game is started. Normally we would include a load bar or some messages on the loading screen but this will suffice for a technical example.
StateLoad
var StateLoad = { preload: function() { game.load.audio("music", "audio/music.mp3"); game.load.image("title", "images/Cool-Game-Title.png"); }, create: function() { //only fires after preload is done var statText = game.add.text(game.width / 2, 100, "Decoding....", { fill: '#ffffff' }); statText.anchor.set(.5, .5); }, update: function() { //check to see if the audio is decoded if (game.cache.isSoundDecoded('music')) { game.state.start("StateMain"); } } }
StateMain
var StateMain = { preload: function() { //moved loading here to stateLoad }, create: function() { this.secs = 0; //create the music var music = game.add.audio("music"); //play the music music.play(); // // // //put the title on screen var title = game.add.image(game.width / 2, game.height / 2, "title"); title.anchor.set(0.5, 0.5); //make the timer text this.timeText = game.add.text(game.width / 2, 100, "0",{fill:'#ffffff'}); this.timeText.anchor.set(0.5, 0.5); //start counting seconds game.time.events.loop(Phaser.Timer.SECOND, this.tick, this); }, tick: function() { this.secs++; this.timeText.text = this.secs; }, update: function() {} }
And here is the result. See audio text example 2 here
Results may vary, but on my localhost, the music played on zero for the second test and about 3 for the first test.
What if we have multiple sound to load?
Should we check all sounds in cache?
You certainly can. Generally, I only use it for background music. This is because the background music files are much larger and by the time I need to use a sound effect file it is already decoded and ready to go.
Dude you saved my life! My new project is all about audio (procrastination of words). It may look normal to let user wait for a sec but why not use that simple solution to make it look natural? Thanks
p.s. spelling mistake, should be ‘pronunciation’ lol
I was facing the same problem. Nice solution!
Thanks!
It seems the game.cache.isSoundDecoded() function no longer exists in Phaser3 – was it renamed, or how do we check for sounds having been fully loaded and decoded now?
Do you have a fix yet? I tried loading the assets in a loading screen and starting the game scene after load completes.. but that is working on some devices and not on some devices.. when i try the decodeAudio function mentioned in the documentation it gives me an error saying such a function doesnt exist.