Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Add Music, Sounds, and Other Audio to a Phaser Game

TwitterFacebookRedditLinkedInHacker News

If you’re building a game, you’re probably going to need audio such as sound effects and background music at some point in time. Without audio, the gameplay experience can feel quite boring, which no one wants their game to be labeled as.

I’ve recently written quite a few tutorials on the topic of game development with Phaser. We’ve seen everything from adding and animating sprites, to handling collisions between game objects.

In this tutorial, we’re going to see how to add audio to our Phaser 3.x game in the form of sound effects triggered by collisions.

To get a better idea of what we want to accomplish, take a look at the following video:

The above video takes the topics that we explored in previous tutorials and adds audio to them. If you didn’t hear any bell noises or explosion noises, make sure your speakers are on and that your browser allows for the output of sound.

We’re going to slim the example down a bit to avoid any unnecessary complexities in the tutorial. What we’re going to do is have an animated player sprite and two other game objects. When the player collides with each game object, a different sound will play. We’re not going to worry about object pooling and similar.

Create a New Phaser 3.x Game with Boilerplate Code

Before we get into the audio side of things, we need to have a project to work from. On your computer, create a new directory with an index.html file that contains the following HTML markup:

<!DOCTYPE html>
<html>
    <head>
        <script src="//cdn.jsdelivr.net/npm/phaser@3.24.1/dist/phaser.min.js"></script>
    </head>
    <body>
        <div id="game"></div>
        <script>

            const phaserConfig = {
                type: Phaser.AUTO,
                parent: "game",
                width: 1280,
                height: 720,
                backgroundColor: "#5DACD8",
                physics: {
                    default: "arcade",
                    arcade: {
                        debug: false
                    }
                },
                scene: {
                    init: initScene,
                    preload: preloadScene,
                    create: createScene,
                    update: updateScene
                }
            };

            const game = new Phaser.Game(phaserConfig);

            function initScene() { }

            function preloadScene() {
                this.load.spritesheet("plane", "plane.png", { frameWidth: 512, frameHeight: 512 });
                this.load.image("bitcoin", "bitcoin.png");
                this.load.image("obstacle", "obstacle.png");
            }

            function createScene() {

                this.anims.create({
                    key: "fly",
                    frameRate: 7,
                    frames: this.anims.generateFrameNumbers("plane", { start: 3, end: 5 }),
                    repeat: -1
                });

                this.anims.create({
                    key: "explode",
                    frameRate: 7,
                    frames: this.anims.generateFrameNumbers("plane", { start: 0, end: 2 }),
                    repeat: 2
                });

                this.plane = this.physics.add.sprite(256, 360, "plane");
                this.plane.setScale(0.3);
                this.plane.setDepth(1);
                this.plane.play("fly");

                this.bitcoin = this.physics.add.sprite(800, 360, "bitcoin");
                this.bitcoin.setScale(0.1);

                this.obstacle = this.physics.add.sprite(1100, 360, "obstacle");
                this.obstacle.setScale(0.4);

                this.physics.add.collider(this.plane, this.bitcoin, function (plane, bitcoin) {
                    bitcoin.destroy();
                });

                this.physics.add.collider(this.plane, this.obstacle, function (plane, obstacle) {
                    if (plane.anims.getCurrentKey() != "explode") {
                        plane.play("explode")
                        plane.once(Phaser.Animations.Events.SPRITE_ANIMATION_COMPLETE, () => {
                            plane.destroy();
                        })
                    }
                });

            }

            function updateScene() {
                this.bitcoin.x -= 3;
                this.obstacle.x -= 3;
            }

        </script>
    </body>
</html>

The above markup contains more than just Phaser 3.x boilerplate code. The above code is responsible for creating three sprites with arcade physics, one of which is an animated spritesheet.

If you’d like to use the game assets that I’m using, download the plane, coin, and the obstacle image files.

When the plane collides with either of the two other sprites, an event happens. Our goal will be to include audio during each of those events.

Loading and Playing Sound within the Game

Now that we have a foundation to work with in regards to our project, we can focus on the audio, which isn’t any more difficult to use than sprites in a game.

If you’d like to use my sound effects, you can download the ding which I created by tapping a bell, and the explosion which I created by whispering nonsense into a microphone. Feel free to make your own sound effects or even looping background music.

Within the preloadScene function of the index.html file, change it to look like the following:

function preloadScene() {
    this.load.spritesheet("plane", "plane.png", { frameWidth: 512, frameHeight: 512 });
    this.load.image("bitcoin", "bitcoin.png");
    this.load.image("obstacle", "obstacle.png");
    this.load.audio("ding", ["ding.mp3"]);
    this.load.audio("explosion", ["explosion.mp3"]);
}

The above code assumes that all media assets, images and audio, exist at the root of the project next to the index.html file. The audio loader accepts a file array so that you can provide the same audio file in multiple formats for compatibility. In the above example we are only providing an MP3, which should have decent compatibility, but it could always be better.

With the audio files loaded, we need to add them to our scene.

Within the createScene function of the index.html file, add the following:

function createScene() {

    // Animations ...

    ding = this.sound.add("ding", { loop: false });
    explosion = this.sound.add("explosion", { loop: false });

    // Player sprite ...
    // Coin sprite ...
    // Obstacle sprite ...

    this.physics.add.collider(this.plane, this.bitcoin, function (plane, bitcoin) {
        ding.play();
        bitcoin.destroy();
    });

    this.physics.add.collider(this.plane, this.obstacle, function (plane, obstacle) {
        if (plane.anims.getCurrentKey() != "explode") {
            explosion.play();
            plane.play("explode")
            plane.once(Phaser.Animations.Events.SPRITE_ANIMATION_COMPLETE, () => {
                plane.destroy();
            })
        }
    });

}

You’ll notice that I’ve commented out the code for animations and the adding of our sprites because we’ve already seen it. However, for both audio files, we’ve added them to our scene and defined that they should not be looped when played.

Adding the audio to the scene does not automatically play it. Instead it needs to be triggered kind of like how an animation is played on a sprite. If you look at each of the collider methods, you’ll notice that we are calling ding.play() and explosion.play() to play our sounds.

If you had background music, you could just play the audio on loop in the createScene function.

Conclusion

You just saw how to add audio to your Phaser 3.x game. While we only saw how to add short sound effects to our game that played on a trigger event, we could easily extend what we’ve done to background music.

If you were interested in extending the example to what was seen in the movie towards the top of the tutorial, check out how to pool objects in the tutorial, Object Pooling Sprites in a Phaser Game for Performance Gains.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in C#, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Unity. Nic writes about his development experiences related to making web and mobile development easier to understand.