About The Author

Co Founder of Starfish Web Consulting — a Web design and development studio based in Belfast, Northern Ireland. More about Eoin McGrath

How To Design A Mobile Game With HTML5

        Care to make a cross-platform mobile game with HTML5? No need to dabble in Java or Objective-C? Bypass the app stores? Sounds like an instant win! A handful of game developers are pushing the envelope of mobile HTML5 games at the moment. Check out the likes of Nutmeg and Lunch Bug for some shining examples.

        The great thing about these titles is that they work equally well on both mobile and desktop using the same code. Could HTML5 finally fulfill the holy grail of “write once, run anywhere”?

        Getting Started

        Before you start sketching the next Temple Run or Angry Birds, you should be aware of a few things that could dampen your excitement:

        • Performance. Mobile browsers are not traditionally known for their blazing JavaScript engines. With iOS 6 and Chrome beta for Android, though, things are improving fast.
        • Resolution. A veritable cornucopia of Android devices sport a wide range of resolutions. Not to mention the increased resolution and pixel density of the iPhone 4 and iPad 3.
        • Audio. Hope you enjoy the sound of silence. Audio support in mobile browsers is poor, to say the least. Lag is a major problem, as is the fact that most devices offer only a single channel. iOS won’t even load a sound until the user initiates the action. My advice is to hold tight and wait for browser vendors to sort this out.

        Now, as a Web developer you’re used to dealing with the quirks of certain browsers and degrading gracefully and dealing with fragmented platforms. So, a few technical challenges won’t put you off, right? What’s more, all of these performance and audio problems are temporary. The mobile browser landscape is changing so quickly that these concerns will soon be a distant memory.

        In this tutorial, we’ll make a relatively simple game that takes you through the basics and steers you away from pitfalls. The result will look like this:

        Screenshot of finished demo

        It’s a fairly simple game, in which the user bursts floating bubbles before they reach the top of the screen. Imaginatively, I’ve titled our little endeavour Pop.

        We’ll develop this in a number of distinct stages:

        1. Cater to the multitude of viewports and optimize for mobile;
        2. Look briefly at using the canvas API to draw to the screen;
        3. Capture touch events;
        4. Make a basic game loop;
        5. Introduce sprites, or game “entities”;
        6. Add collision detection and some simple maths to spice things up;
        7. Add a bit of polish and some basic particle effects.

        1. Setting The Stage

        Enough of the background story. Fire up your favorite text editor, pour a strong brew of coffee, and let’s get our hands dirty.

        As mentioned, there is a plethora of resolution sizes and pixel densities across devices. This means we’ll have to scale our canvas to fit the viewport. This could come at the price of a loss in quality, but one clever trick is to make the canvas small and then scale up, which provides a performance boost.

        Let’s kick off with a basic HTML shim:

        <!DOCTYPE HTML>
        <html lang="en">
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, 
            user-scalable=no, initial-scale=1, maximum-scale=1, user-scalable=0" />
        <meta name="apple-mobile-web-app-capable" content="yes" />
        <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
        <style type="text/css">
        body { margin: 0; padding: 0; background: #000;}
        canvas { display: block; margin: 0 auto; background: #fff; }
        <canvas> </canvas>
        // all the code goes here

        The meta viewport tag tells mobile browsers to disable user scaling and to render at full size rather than shrink the page down. The subsequent apple- prefixed meta tags allow the game to be bookmarked. On the iPhone, bookmarked apps do not display the toolbar at the bottom of the page, thus freeing up valuable real estate.

        Take a look at the following:

        // namespace our game
        var POP = {
            // set up some initial values
            WIDTH: 320, 
            HEIGHT:  480, 
            // we'll set the rest of these
            // in the init function
            RATIO:  null,
            currentWidth:  null,
            currentHeight:  null,
            canvas: null,
            ctx:  null,
            init: function() {
                // the proportion of width to height
                POP.RATIO = POP.WIDTH / POP.HEIGHT;
                // these will change when the screen is resized
                POP.currentWidth = POP.WIDTH;
                POP.currentHeight = POP.HEIGHT;
                // this is our canvas element
                POP.canvas = document.getElementsByTagName('canvas')[0];
                // setting this is important
                // otherwise the browser will
                // default to 320 x 200
                POP.canvas.width = POP.WIDTH;
                POP.canvas.height = POP.HEIGHT;
                // the canvas context enables us to 
                // interact with the canvas api
                POP.ctx = POP.canvas.getContext('2d');
                // we're ready to resize
            resize: function() {
                POP.currentHeight = window.innerHeight;
                // resize the width in proportion
                // to the new height
                POP.currentWidth = POP.currentHeight * POP.RATIO;
                // this will create some extra space on the
                // page, allowing us to scroll past
                // the address bar, thus hiding it.
                if (POP.android || POP.ios) {
                    document.body.style.height = (window.innerHeight + 50) + 'px';
                // set the new canvas style width and height
                // note: our canvas is still 320 x 480, but
                // we're essentially scaling it with CSS
                POP.canvas.style.width = POP.currentWidth + 'px';
                POP.canvas.style.height = POP.currentHeight + 'px';
                // we use a timeout here because some mobile
                // browsers don't fire if there is not
                // a short delay
                window.setTimeout(function() {
                }, 1);
        window.addEventListener('load', POP.init, false);
        window.addEventListener('resize', POP.resize, false);

        First, we create the POP namespace for our game. Being good developers, we don’t want to pollute the global namespace. In keeping good practice, we will declare all variables at the start of the program. Most of them are obvious: canvas refers to the canvas element in the HTML, and ctx enables us to access it via the JavaScript canvas API.

        In POP.init, we grab a reference to our canvas element, get its context and adjust the canvas element’s dimensions to 480 × 320. The resize function, which is fired on resize and load events, adjusts the canvas’ style attribute for width and height accordingly while maintaining the ratio. Effectively, the canvas is still the same dimensions but has been scaled up using CSS. Try resizing your browser and you’ll see the canvas scale to fit.

        If you tried that on your phone, you’ll notice that the address bar is still visible. Ugh! We can fix this by adding a few extra pixels to the document and then scrolling down to hide the address bar, like so:

        // we need to sniff out Android and iOS
        // so that we can hide the address bar in
        // our resize function
        POP.ua = navigator.userAgent.toLowerCase();
        POP.android = POP.ua.indexOf('android') > -1 ? true : false;
        POP.ios = ( POP.ua.indexOf('iphone') > -1 || POP.ua.indexOf('ipad') > -1  ) ? 
            true : false;

        The code above sniffs out the user agent, flagging for Android and iOS if present. Add it at the end of POP.init, before the call to POP.resize().

        Then, in the resize function, if android or ios is true, we add another 50 pixels to the document’s height — i.e. enough extra space to be able to scroll down past the address bar.

        // this will create some extra space on the
        // page, enabling us to scroll past
        // the address bar, thus hiding it.
        if (POP.android || POP.ios) {
            document.body.style.height = (window.innerHeight + 50) + 'px';

        Notice that we do this only for Android and iOS devices; otherwise, nasty scroll bars will appear. Also, we need to delay the firing of scrollTo to make sure it doesn’t get ignored on mobile Safari.

        2. A Blank Canvas

        Now that we’ve scaled our canvas snuggly to the viewport, let’s add the ability to draw some shapes.

        Note: In this tutorial, we’re going to stick with basic geometric shapes. iOS 5 and Chrome beta for Android can handle a lot of image sprites at a high frame rate. Try that on Android 3.2 or lower and the frame rate will drop exponentially. Luckily, there is not much overhead when drawing circles, so we can have a lot of bubbles in our game without hampering performance on older devices.

        Below, we’ve added a basic Draw object that allows us to clear the screen, draw a rectangle and circle, and add some text. Nothing mind-blowing yet. Mozilla Developers Network has excellent resources as always, replete with examples for drawing to the canvas.

        // abstracts various canvas operations into
        // standalone functions
        POP.Draw = {
            clear: function() {
                POP.ctx.clearRect(0, 0, POP.WIDTH, POP.HEIGHT);
            rect: function(x, y, w, h, col) {
                POP.ctx.fillStyle = col;
                POP.ctx.fillRect(x, y, w, h);
            circle: function(x, y, r, col) {
                POP.ctx.fillStyle = col;
                POP.ctx.arc(x + 5, y + 5, r, 0,  Math.PI * 2, true);
            text: function(string, x, y, size, col) {
                POP.ctx.font = 'bold '+size+'px Monospace';
                POP.ctx.fillStyle = col;
                POP.ctx.fillText(string, x, y);

        Our Draw object has methods for clearing the screen and drawing rectangles, circles and text. The benefit of abstracting these operations is that we don’t have to remember the exact canvas API calls, and we can now draw a circle with one line of code, rather than five.

        Let’s put it to the test:

        // include this at the end of POP.init function
        POP.Draw.rect(120,120,150,150, 'green');
        POP.Draw.circle(100, 100, 50, 'rgba(255,0,0,0.5)');
        POP.Draw.text('Hello World', 100, 100, 10, '#000');

        Include the code above at the end of the POP.init function, and you should see a couple of shapes drawn to the canvas.

        3. The Magic Touch

        Just as we have the click event, mobile browsers provide methods for catching touch events.

        The interesting parts of the code below are the touchstart, touchmove and touchend events. With the standard click event, we can get the coordinates from e.pageX and e.pageY. Touch events are slightly different. They contain a touches array, each element of which contains touch coordinates and other data. We only want the first touch, and we access it like so: e.touches[0].

        Note: Android provides JavaScript access to multi-touch actions only since version 4.

        We also call e.preventDefault(); when each event is fired to disable scrolling, zooming and any other action that would interrupt the flow of the game.

        Add the following code to the POP.init function.

        // listen for clicks
        window.addEventListener('click', function(e) {
        }, false);
        // listen for touches
        window.addEventListener('touchstart', function(e) {
            // the event object has an array
            // named touches; we just want
            // the first touch
        }, false);
        window.addEventListener('touchmove', function(e) {
            // we're not interested in this,
            // but prevent default behaviour
            // so the screen doesn't scroll
            // or zoom
        }, false);
        window.addEventListener('touchend', function(e) {
            // as above
        }, false);

        You probably noticed that the code above passes the event data to an Input object, which we’ve yet to define. Let’s do that now:

        // + add this at the bottom of your code,
        // before the window.addEventListeners
        POP.Input = {
            x: 0,
            y: 0,
            tapped :false,
            set: function(data) {
                this.x = data.pageX;
                this.y = data.pageY;
                this.tapped = true; 
                POP.Draw.circle(this.x, this.y, 10, 'red');

        Now, try it out. Hmm, the circles are not appearing. A quick scratch of the head and a lightbulb moment! Because we’ve scaled the canvas, we need to account for this when mapping the touch to the screen’s position.

        First, we need to subtract the offset from the coordinates.

        var offsetTop = POP.canvas.offsetTop,
            offsetLeft = POP.canvas.offsetLeft;
        this.x = data.pageX - offsetLeft;
        this.y = data.pageY - offsetTop;

        Diagram showing canvas offset relative to the browser window

        Then, we need to take into account the factor by which the canvas has been scaled so that we can plot to the actual canvas (which is still 320 × 480).

        var offsetTop = POP.canvas.offsetTop,
            offsetLeft = POP.canvas.offsetLeft;
            scale = POP.currentWidth / POP.WIDTH;
        this.x = ( data.pageX - offsetLeft ) / scale;
        this.y = ( data.pageY - offsetTop ) / scale;

        Difference between CSS scaled canvas and actual dimensions

        If your head is starting to hurt, a practical example should provide some relief. Imagine the player taps the 500 × 750 canvas above at 400,400. We need to translate that to 480 × 320 because, as far as the JavaScript is concerned, those are the dimensions of the canvas. So, the actual x coordinate is 400 divided by the scale; in this case, 400 ÷ 1.56 = 320.5.

        Rather than calculating this on each touch event, we can calculate them after resizing. Add the following code to the start of the program, along with the other variable declarations:

        // let's keep track of scale
            // along with all initial declarations
            // at the start of the program
            scale:  1,
            // the position of the canvas
            // in relation to the screen
            offset = {top: 0, left: 0},

        In our resize function, after adjusting the canvas’ width and height, we make note of the current scale and offset:

        // add this to the resize function.
        POP.scale = POP.currentWidth / POP.WIDTH;
        POP.offset.top = POP.canvas.offsetTop;
        POP.offset.left = POP.canvas.offsetLeft;

        Now, we can use them in the set method of our POP.Input class:

        this.x = (data.pageX - POP.offset.left) / POP.scale;
        this.y = (data.pageY - POP.offset.top) / POP.scale;

        4. In The Loop

        A typical game loop goes something like this:

        1. Poll user input,
        2. Update characters and process collisions,
        3. Render characters on the screen,
        4. Repeat.

        We could, of course, use setInterval, but there’s a shiny new toy in town named requestAnimationFrame. It promises smoother animation and is more battery-efficient. The bad news is that it’s not supported consistently across browsers. But Paul Irish has come to the rescue with a handy shim.

        Let’s go ahead and add the shim to the start of our current code base.

        // http://paulirish.com/2011/requestanimationframe-for-smart-animating
        // shim layer with setTimeout fallback
        window.requestAnimFrame = (function(){
          return  window.requestAnimationFrame       || 
                  window.webkitRequestAnimationFrame || 
                  window.mozRequestAnimationFrame    || 
                  window.oRequestAnimationFrame      || 
                  window.msRequestAnimationFrame     || 
                  function( callback ){
                    window.setTimeout(callback, 1000 / 60);

        And let’s create a rudimentary game loop:

        // Add this at the end of POP.init;
        // it will then repeat continuously
        // Add the following functions after POP.init:
        // this is where all entities will be moved
        // and checked for collisions, etc.
        update: function() {
        // this is where we draw all the entities
        render: function() {
        // the actual loop
        // requests animation frame,
        // then proceeds to update
        // and render
        loop: function() {
            requestAnimFrame( POP.loop );

        We call the loop at the end of POP.init. The POP.loop function in turn calls our POP.update and POP.render methods. requestAnimFrame ensures that the loop is called again, preferably at 60 frames per second. Note that we don’t have to worry about checking for input in our loop because we’re already listening for touch and click events, which is accessible through our POP.Input class.

        The problem now is that our touches from the last step are immediately wiped off the screen. We need a better approach to remember what was drawn to the screen and where.

        5. Spritely Will Do It

        First, we add an entity array to keep track of all entities. This array will hold a reference to all touches, bubbles, particles and any other dynamic thing we want to add to the game.

        // put this at start of program
        entities: [],

        Let’s create a Touch class that draws a circle at the point of contact, fades it out and then removes it.

        POP.Touch = function(x, y) {
            this.type = 'touch';    // we'll need this later
            this.x = x;             // the x coordinate
            this.y = y;             // the y coordinate
            this.r = 5;             // the radius
            this.opacity = 1;       // initial opacity; the dot will fade out
            this.fade = 0.05;       // amount by which to fade on each game tick
            this.remove = false;    // flag for removing this entity. POP.update
                                    // will take care of this
            this.update = function() {
                // reduce the opacity accordingly
                this.opacity -= this.fade; 
                // if opacity if 0 or less, flag for removal
                this.remove = (this.opacity < 0) ? true : false;
            this.render = function() {
                POP.Draw.circle(this.x, this.y, this.r, 'rgba(255,0,0,'+this.opacity+')');

        The Touch class sets a number of properties when initiated. The x and y coordinates are passed as arguments, and we set the radius this.r to 5 pixels. We also set an initial opacity to 1 and the rate by which the touch fades to 0.05. There is also a remove flag that tells the main game loop whether to remove this from the entities array.

        Crucially, the class has two main methods: update and render. We will call these from the corresponding part of our game loop.

        We can then spawn a new instance of Touch in the game loop, and then move them via the update method:

        // POP.update function
        update: function() {
            var i;
            // spawn a new instance of Touch
            // if the user has tapped the screen
            if (POP.Input.tapped) {
                POP.entities.push(new POP.Touch(POP.Input.x, POP.Input.y));
                // set tapped back to false
                // to avoid spawning a new touch
                // in the next cycle
                POP.Input.tapped = false;
            // cycle through all entities and update as necessary
            for (i = 0; i < POP.entities.length; i += 1) {
                // delete from array if remove property
                // flag is set to true
                if (POP.entities[i].remove) {
                    POP.entities.splice(i, 1);

        Basically, if POP.Input.tapped is true, then we add a new instance of POP.Touch to our entities array. We then cycle through the entities array, calling the update method for each entity. Finally, if the entity is flagged for removal, we delete it from the array.

        Next, we render them in the POP.render function.

        // POP.render function
        render: function() {
            var i;
           POP.Draw.rect(0, 0, POP.WIDTH, POP.HEIGHT, '#036');
            // cycle through all entities and render to canvas
            for (i = 0; i < POP.entities.length; i += 1) {

        Similar to our update function, we cycle through the entities and call their render method to draw them to the screen.

        So far, so good. Now we’ll add a Bubble class that will create a bubble that floats up for the user to pop.

        POP.Bubble = function() {
            this.type = 'bubble';
            this.x = 100;
            this.r = 5;                // the radius of the bubble
            this.y = POP.HEIGHT + 100; // make sure it starts off screen
            this.remove = false;
            this.update = function() {
                // move up the screen by 1 pixel
                this.y -= 1;
                // if off screen, flag for removal
                if (this.y < -10) {
                    this.remove = true;
            this.render = function() {
                POP.Draw.circle(this.x, this.y, this.r, 'rgba(255,255,255,1)');

        The POP.Bubble class is very similar to the Touch class, the main differences being that it doesn’t fade but moves upwards. The motion is achieved by updating the y position, this.y, in the update function. Here, we also check whether the bubble is off screen; if so, we flag it for removal.

        Note: We could have created a base Entity class that both Touch and Bubble inherit from. But, I’d rather not open another can of worms about JavaScript prototypical inheritance versus classic at this point.

        // Add at the start of the program
        // the amount of game ticks until
        // we spawn a bubble
        nextBubble: 100,
        // at the start of POP.update
        // decrease our nextBubble counter
        POP.nextBubble -= 1;
        // if the counter is less than zero
        if (POP.nextBubble < 0) {
            // put a new instance of bubble into our entities array
            POP.entities.push(new POP.Bubble());
            // reset the counter with a random value
            POP.nextBubble = ( Math.random() * 100 ) + 100;

        Above, we have added a random timer to our game loop that will spawn an instance of Bubble at a random position. At the start of the game, we set nextBubble with a value of 100. This is subtracted on each game tick and, when it reaches 0, we spawn a bubble and reset the nextBubble counter.

        6. Putting It Together

        First of all, there is not yet any notion of collision detection. We can add that with a simple function. The math behind this is basic geometry, which you can brush up on at Wolfram MathWorld.

        // this function checks if two circles overlap
        POP.collides = function(a, b) {
                var distance_squared = ( ((a.x - b.x) * (a.x - b.x)) + 
                                        ((a.y - b.y) * (a.y - b.y)));
                var radii_squared = (a.r + b.r) * (a.r + b.r);
                if (distance_squared < radii_squared) {
                    return true;
                } else {
                    return false;
        // at the start of POP.update, we set a flag for checking collisions
            var i,
                checkCollision = false; // we only need to check for a collision
                                        // if the user tapped on this game tick
        // and then incorporate into the main logic
        if (POP.Input.tapped) {
            POP.entities.push(new POP.Touch(POP.Input.x, POP.Input.y));
            // set tapped back to false
            // to avoid spawning a new touch
            // in the next cycle
            POP.Input.tapped = false;
            checkCollision = true;
        // cycle through all entities and update as necessary
        for (i = 0; i < POP.entities.length; i += 1) {
            if (POP.entities[i].type === 'bubble' && checkCollision) {
                hit = POP.collides(POP.entities[i], 
                                    {x: POP.Input.x, y: POP.Input.y, r: 7});
                POP.entities[i].remove = hit;
            // delete from array if remove property
            // is set to true
            if (POP.entities[i].remove) {
                POP.entities.splice(i, 1);

        The bubbles are rather boring; they all travel at the same speed on a very predictable trajectory. Making the bubbles travel at random speeds is a simple task:

        POP.Bubble = function() {
            this.type = 'bubble';
            this.r = (Math.random() * 20) + 10;
            this.speed = (Math.random() * 3) + 1;
            this.x = (Math.random() * (POP.WIDTH) - this.r);
            this.y = POP.HEIGHT + (Math.random() * 100) + 100;
            this.remove = false;
            this.update = function() {
                this.y -= this.speed;
            // the rest of the class is unchanged

        And let’s make them oscillate from side to side, so that they are harder to hit:

        // the amount by which the bubble
            // will move from side to side
            this.waveSize = 5 + this.r;
            // we need to remember the original
            // x position for our sine wave calculation
            this.xConstant = this.x;
            this.remove = false;
            this.update = function() {
                // a sine wave is commonly a function of time
                var time = new Date().getTime() * 0.002;
                this.y -= this.speed;
                // the x coordinate to follow a sine wave
                this.x = this.waveSize * Math.sin(time) + this.xConstant;
            // the rest of the class is unchanged

        Again, we’re using some basic geometry to achieve this effect; in this case, a sine wave. While you don’t need to be a math whiz to make games, basic understanding goes a long way. The article “A Quick Look Into the Math of Animations With JavaScript” should get you started.

        Let’s also show some statistics on screen. To do this, we will need to track various actions throughout the game.

        Put the following code, along with all of the other variable declarations, at the beginning of the program.

        // this goes at the start of the program
        // to track players's progress
        POP.score = {
            taps: 0,
            hit: 0,
            escaped: 0,
            accuracy: 0

        Now, in the Bubble class we can keep track of POP.score.escaped when a bubble goes off screen.

        // in the bubble class, when a bubble makes it to
        // the top of the screen
            if (this.y < -10) {
                POP.score.escaped += 1; // update score
                this.remove = true;

        In the main update loop, we increase POP.score.hit accordingly:

        // in the update loop
        if (POP.entities[i].type === 'bubble' && checkCollision) {
            hit = POP.collides(POP.entities[i], 
                                {x: POP.Input.x, y: POP.Input.y, r: 7});
            if (hit) {
                POP.score.hit += 1;
            POP.entities[i].remove = hit;

        In order for the statistics to be accurate, we need to record all of the taps the user makes:

        // and record all taps
        if (POP.Input.tapped) {
            // keep track of taps; needed to 
            // calculate accuracy
            POP.score.taps += 1;

        Accuracy is obtained by dividing the number of hits by the number of taps, multiplied by 100, which gives us a nice percentage. Note that ~~(POP.score.accuracy) is a quick way (i.e. a hack) to round floats down to integers.

        // Add at the end of the update loop
        // to calculate accuracy
        POP.score.accuracy = (POP.score.hit / POP.score.taps) * 100;
        POP.score.accuracy = isNaN(POP.score.accuracy) ?
            0 :
            ~~(POP.score.accuracy); // a handy way to round floats

        Lastly, we use our POP.Draw.text to display the scores in the main update function.

        // and finally in the draw function
        POP.Draw.text('Hit: ' + POP.score.hit, 20, 30, 14, '#fff');
        POP.Draw.text('Escaped: ' + POP.score.escaped, 20, 50, 14, '#fff');
        POP.Draw.text('Accuracy: ' + POP.score.accuracy + '%', 20, 70, 14, '#fff');

        7. Spit And Polish

        There’s a common understanding that a playable demo can be made in a couple of hours, but a polished game takes days, week, months or even years!

        We can do a few things to improve the visual appeal of the game.

        Particle Effects

        Most games boast some form of particle effects, which are great for explosions. What if we made a bubble explode into many tiny bubbles when it is popped, rather than disappear instantly?

        Take a look at our Particle class:

        POP.Particle = function(x, y,r, col) {
            this.x = x;
            this.y = y;
            this.r = r;
            this.col = col;
            // determines whether particle will
            // travel to the right of left
            // 50% chance of either happening
            this.dir = (Math.random() * 2 > 1) ? 1 : -1;
            // random values so particles do not
            // travel at the same speeds
            this.vx = ~~(Math.random() * 4) * this.dir;
            this.vy = ~~(Math.random() * 7);
            this.remove = false;
            this.update = function() {
                // update coordinates
                this.x += this.vx;
                this.y += this.vy;
                // increase velocity so particle
                // accelerates off screen
                this.vx *= 0.99;
                this.vy *= 0.99;
                // adding this negative amount to the
                // y velocity exerts an upward pull on
                // the particle, as if drawn to the
                // surface
                this.vy -= 0.25;
                // off screen
                if (this.y < 0) {
                    this.remove = true;
            this.render = function() {
                POP.Draw.circle(this.x, this.y, this.r, this.col);

        It’s fairly obvious what is going on here. Using some basic acceleration so that the particles speed up as the reach the surface is a nice touch. Again, this math and physics are beyond the scope of this article, but for those interested, Skookum Digital Works explains it in depth.

        To create the particle effect, we push several particles into our entities array whenever a bubble is hit:

        // modify the main update function like so:
        if (hit) {
            // spawn an explosion
            for (var n = 0; n < 5; n +=1 ) {
                POP.entities.push(new POP.Particle(
                    // random opacity to spice it up a bit
            POP.score.hit += 1;


        Given the underwater theme of the game, adding a wave effect to the top of the screen would be a nice touch. We can do this by drawing a number of overlapping circles to give the illusion of waves:

        // set up our wave effect;
        // basically, a series of overlapping circles
        // across the top of screen
        POP.wave = {
            x: -25, // x coordinate of first circle
            y: -40, // y coordinate of first circle
            r: 50, // circle radius
            time: 0, // we'll use this in calculating the sine wave
            offset: 0 // this will be the sine wave offset
        // calculate how many circles we need to 
        // cover the screen's width
        POP.wave.total = Math.ceil(POP.WIDTH / POP.wave.r) + 1;

        Add the code above to the POP.init function. POP.wave has a number of values that we’ll need to draw the waves.

        Add the following to the main update function. It uses a sine wave to adjust the position of the waves and give the illusion of movement.

        // update wave offset
        // feel free to play with these values for
        // either slower or faster waves
        POP.wave.time = new Date().getTime() * 0.002;
        POP.wave.offset = Math.sin(POP.wave.time * 0.8) * 5;

        All that’s left to be done is to draw the waves, which goes into the render function.

        // display snazzy wave effect
        for (i = 0; i < POP.wave.total; i++) {
                        POP.wave.x + POP.wave.offset +  (i * POP.wave.r), 

        Here, we’ve reused our sine wave solution for the bubbles to make the waves move gently to and fro. Feeling seasick yet?

        Final Thoughts

        Phew! That was fun. Hope you enjoyed this short forage into tricks and techniques for making an HTML5 game. We’ve managed to create a very simple game that works on most smartphones as well as modern browsers. Here are some things you could consider doing:

        • Store high scores using local storage.
        • Add a splash screen and a “Game over” screen.
        • Enable power-ups.
        • Add audio. Contrary to what I said at the beginning of this article, this isn’t impossible, just a bit of a headache. One technique is to use audio sprites (kind of like CSS image sprites); Remy Sharp breaks it down.
        • Let your imagination run wild!

        Further Reading on SmashingMag:

        If you are interested in further exploring the possibilities of mobile HTML5 games, I recommend test-driving a couple of frameworks to see what works for you. Juho Vepsäläinen offers a useful summary of most game engines. If you’re willing to invest a little cash, then Impact is a great starting point, with thorough documentation and lively helpful forums. And the impressive X-Type demonstrates what is possible. Not bad, eh?

        (al) (jc)