Adding Sound to Breakout

Thu, Jun 16, 2011

If, like me, you have no musical ability, you might ignore the sound requirements of your game completely. While, that is tempting, I couldn’t really consider my Breakout game done until I at least add some basic sound effects.

For a serious game, I would try to find a professional to provide audio effects, but for a simple demonstration such as Breakout, I turned to freesound.org to look for a couple of boing sound effects for the ball hitting the brick and the paddle. I also picked up some simple voice-over samples for go, game over and level up.

With my previous pong game, I used the standard html5 <audio> tag to provide the sound, but found out it was not really ready for prime time, certainly not in the Chrome browser at least.

So, for Breakout, I am turning to a 3rd party library, soundmanager2 to provide a simple interface for using a hidden flash object to manage the audio.

The common way to include the soundmanager2 library is with a normal <script> tag in the page along with some inline script to initialize it.

Since I am, partially, trying to build a generic Game.Runner library, I wanted to avoid having to add anything to the HTML page, and instead simply provide configuration options and have the Game.Runner take responsibility for loading and initializing the library.

Loading Scripts

In order to get the soundmanager2.js file loaded, I need to be able to dynamically load a <script> and know when it has completed loading. You can do this by inserting a <script> tag into the <head> and setting its onload callback (or onreadystatechange for IE support).

  loadScript: function(src, cb) {
    var head = document.getElementsByTagName('head')[0];
    var s = document.createElement('script');
    head.appendChild(s);
    s.onreadystatechange = function(e) { if (e.srcElement.readyState == 'loaded') cb(e.srcElement); }
    s.onload = function(e) { cb(e.currentTarget); }
    s.type = 'text/javascript';
    s.src = src;
  },

Loading SoundManager

Once I have a generic script loader, I can use it to load the appropriate soundmanager.js file, and once that script has loaded, I can create all of the sounds requested:

  loadSounds: function(cfg) {
    cfg = cfg || {};
    if (typeof soundManager == 'undefined') {
      var path = cfg.path || 'sound/soundmanager2-nodebug-jsmin.js';
      var swf  = cfg.swf  || 'sound/swf';
      window.SM2_DEFER = true;
      Game.loadScript(path, function() {
        window.soundManager = new SoundManager();
        soundmanager.usehighperformance = true;
        soundmanager.usefastpolling = true;
        soundManager.url = swf;
        soundManager.defaultOptions.volume = 50;
        soundManager.onready(function() {
          Game.loadSounds(cfg);
        });
        soundManager.beginDelayedInit();
      });
    }
    else {
      for(var id in cfg.sounds) {
        soundManager.createSound({id: id, url: cfg.sounds[id]});
      }
    }
  },

Playing Sounds

Once soundmanager has created the sounds, they can be played easily given a known ID:

  soundManager.play('id');

Configuring Breakout

By building this initialization code into the generic Game.Runner, it keeps the breakout.js code simple:

  • loadSounds during initialization
  • playSound when appropriate event occurs
  Defaults: {

    // ...

    sounds: {
      brick:    'sound/breakout/brick.mp3',
      paddle:   'sound/breakout/paddle.mp3',
      go:       'sound/breakout/go.mp3',
      levelup:  'sound/breakout/levelup.mp3',
      loselife: 'sound/breakout/loselife.mp3',
      gameover: 'sound/breakout/gameover.mp3'
    }

  },

  initialize: function(runner, cfg) {

    // ...

    Game.loadSounds({sounds: cfg.sounds});
  },

  onlose: function() {
    soundManager.play('gameover');
  },

NOTE: whilst many of the sound effects available on freesound.org are provided in .wav format. The soundmanager2 library does not support this format. I used the open source Audacity application to convert them to .mp3 and trim the sounds.

Performance

In a game like Breakout, its important that the sounds play in sync with the ball bouncing. I found that there was a slight delay (worse in some browsers than others) when I used soundmanager2 that can be improved with a couple of soundmanager settings:

  soundmanager.usehighperformance = true;
  soundmanager.usefastpolling = true;

It’s mostly good enough, but its still not quite instant, so for a more serious game I would want to do further research into the audio performance. I think any delay stands out seriously in an arcade style game like Breakout, but might be less obvious in other game styles.

I always thought that graphics were my weakest spot, but I’m coming to realize that sound is actually harder. Both the creative side of it and the technical aspects as well.

If I ever get as far as making a commercial game I think sound will be the first thing I try to find help with.

You can find the game here and the code is here