Archive for October, 2012

A Field Guide To Mobile App Testing


  

Testers are often thought of as people who find bugs, but have you ever considered how testers actually approach testing? Do you ever wonder what testers actually do, and how they can add value to a typical technology project?

I’d like to take you through the thought process of testers and discuss the types of things they consider when testing a mobile app. The intention here is to highlight their thought processes and to show the coverage and depth that testers often go to.

Testers Ask Questions

At the heart of testing is the capability to ask challenging and relevant questions. You are on your way to becoming a good tester if you combine investigative and questioning skills with knowledge of technology and products.

For example, testers might ask:

  • What platforms should this product work on?
  • What is the app supposed to do?
  • What happens if I do this?

And so forth.

Testers find questions in all sorts of places. It could be from conversations, designs, documentation, user feedback or the product itself. The options are huge.

So, let’s dive in.

Where To Start Testing

In an ideal world, testers would all have up-to-date details on what is being built. In the real world, this is rare. So, like everyone else, testers make do with what they have. Don’t let this be an excuse not to test! Information used for testing can be gathered from many different sources, internally and externally.

At this stage questions, testers might ask these questions:

  • What information exists? Specifications? Project conversations? User documentation? Knowledgeable team members? Could the support forum or an online company forum be of help? Is there a log of existing bugs?
  • What OS, platform and device should this app work on and be tested on?
  • What kind of data is processed by the application (i.e. personal, credit cards, etc.)?
  • Does the application integrate with external applications (APIs, data sources)?
  • Does the app work with certain mobile browsers?
  • What do existing customers say about the product?
  • How much time is available for testing?
  • What priorities and risks are there?
  • Who is experiencing pain, and why?
  • How are releases or updates made?

Based on the information gathered, testers can put together a plan on how to approach the testing. Budgets often determine how testing is approached. You would certainly approach testing differently if you had one day instead of a week or a month. Predicting outcomes gets much easier as you come to understand the team, its processes and the answers to many of these types of questions.

Example: Social Commentary on the Facebook App


Facebook’s iPhone App has a lot of negative reviews.

I love using the Facebook app as an example when I’m gathering information as a tester. Complaints of it are everywhere. Just check out the comments in the iTunes App Store for some of the frustrations users are facing. Plenty more are dotted across the Web.

If I were challenged to test the Facebook app, I would definitely take this feedback into consideration. I would be daft not to!

The Creativity Of Testers

You probably know what the app is meant to do, but what can it do? And how will people actually use it? Testers are great at thinking outside of the box, trying out different things, asking “What if� and “Why� constantly.

For example, mobile testers will often adopt the mindset of different types of people — not literally, of course, but the ability to think, analyze and visualize themselves as different users can be quite enlightening.

Testers might put themselves in these shoes:

  • Novice user,
  • Experienced user,
  • Fan,
  • Hacker,
  • Competitor.

Many more personalities could be adopted; much of this really depends on what you are building. But it’s not just about personalities, but about behavior and workflows, too. People use products in strange ways. For example, they:

  • Go back when they are not supposed to,
  • Are impatient and hit keys multiple times,
  • Enter incorrect data,
  • Can’t figure out how to do something,
  • Might not have the required setup,
  • Might assume they know what they are doing (neglecting to read instructions, for example).

Testers look for these situations, often discovering unexpected results along the way. Sometimes the bugs initially found can appear small and insignificant, whereupon deeper investigation uncovers bigger problems.

Many of these issues can be identified up front with testing. When it comes to testing mobile apps, these might not all be relevant, but perhaps try asking questions such as these:

  • Does it do what it says on the tin?
  • Does the app perform the tasks it was designed to do?
  • Does the app perform tasks that it wasn’t designed to do?
  • How does the app perform when being used consistently or under a load? Is it sluggish? Does it crash? Does it update? Does it give feedback?
  • Do crash reports give clues about the app?
  • How can one navigate creatively, logically or negatively around the app?
  • Does the user trust your brand?
  • How secure is the user’s data?
  • Is it possible to break or hack the app?
  • What happens when you push the app to its limits?
  • Does the app ask to turn on related services? (e.g. GPS, Wifi)? What if the user does? Or doesn’t?
  • Where does the app redirect me? To the website? From website to app? Does it cause problems?
  • Is communication and marketing consistent with the app’s function, design and content?
  • What is the sign-up process like? Can it be done on the app? On a website?
  • Does sign-up integrate with other services such as Facebook and Twitter?

Example: RunKeeper’s Buggy Update

RunKeeper, an app to track your fitness activities, recently released an update with new “Goal Setting� features. I was interested in giving it a try, a bit from a testing perspective, but also as a genuinely interested user. I discovered a few problems.

  1. It defaulted to pounds. I wanted weights in kilograms.
  2. Switching between pounds and kilograms just didn’t work properly.
  3. This ended up causing confusion and causing incorrect data and graphs to be shown when setting my goals.
  4. Because of that, I wanted to delete the goals, but found there was no way to do it in the mobile app.
  5. To work around this, I had to change my weight so that the app would register the goal as being completed.
  6. I could then try adding the goal again.
  7. Because of all of this confusion, I played around with it a bit more to see what other issues I could find.

Below are some screenshots of some of the issues found.

RunKeeper Date Bug
A recent update of RunKeeper included a new “Goals� section. Playing around with its dates, I discovered start and end dates could be set from the year 1 A.D. Also, why two years with “1�?

Run Keeper Typo Bug
Another RunKeeper bug. This one is a typo in the “Current Weight� section. This happened when removing the data from the field. Typos are simple bugs to fix but look very unprofessional if ignored.

Run Keeper Goals Bug
Here is the confusion that happened as a result of trying to switch between pounds and kilograms. If I want to lose 46 pounds, the bar actually shows 21 pounds.

There is no quick way to identify issues like these. Every app and team faces different challenges. However, one defining characteristic of testers is that they want to go beyond the limits, do the unusual, change things around, test over a long period of time — days, weeks or months instead of minutes — do what they have been told is not possible. These are the types of scenarios that often bring up bugs.

Where’s All The Data?

Testers like to have fun with data, sometimes to the frustration of developers. The reality is that confusing either the user or the software can be easy in the flow of information. This is ever more important with data- and cloud-based services; there is so much room for errors to occur.

Perhaps you could try checking out what happens in the following scenarios:

  • The mobile device is full of data.
  • The tester removes all of the data.
  • The tester deletes the app. What happens to the data?
  • The tester deletes then reinstalls the app.
  • Too much or too little content causes the design or layout to change.
  • Working with different times and time zones.
  • Data does not sync.
  • Syncing is interrupted.
  • Data updates affect other services (such as websites and cloud services).
  • Data is processed rapidly or in large amounts.
  • Invalid data is used.

Example: Soup.me Is Wrong

I was trying out Soup.me, a Web service that sorts your Instagram photos by map and color, but I didn’t get very far. When I tried to sign up, it said that I didn’t have enough Instagram photos. This is a lie not true because I have published over 500 photos on my Instagram account. It’s not clear what the problem was here. It could have been a data issue. It could have been a performance issue. Or perhaps it was a mistake in the app’s error messages.

SoupMe

Another Example: Quicklytics

Quickytics is a Web analytics iPad app. In my scenario, a website profile of mine still exists despite my having deleted it from my Google Analytics account. My questions here are:

  • I have deleted this Web profile, so why is this still being displayed?
  • The left panel doesn’t appear to have been designed to account for no data. Could this be improved to avoid confusing the user?

Quicklytics

Testers like to test the limits of data, too. They will often get to know the app as a typical user would, but pushing the limits doesn’t take them long. Data is messy, and testers try to consider the types of users of the software and how to test in many different scenarios.

For example, they might try to do the following:

  • Test the limits of user input,
  • Play around with duplicate data,
  • Test on brand new clean phone,
  • Test on an old phone,
  • Pre-populate the app with different types of data,
  • Consider crowd-sourcing the testing,
  • Automate some tests,
  • Stress the app with some unexpected data to see how it copes,
  • Analyze how information and data affects the user experience,
  • Always question whether what they see is correct,

Creating Errors And Messages

I’m not here to talk about (good) error message design. Rather, I’m approaching this from a user and tester’s point of view. Errors and messages are such common places for testers to find problems.

Questions to Ask About Error Messages

Consider the following questions:

  • Is the UI for errors acceptable?
  • Are error messages accessible?
  • Are error messages consistent?
  • Are they helpful?
  • Is the content appropriate?
  • Do errors adhere to good practices and standards?
  • Are the error messages security-conscious?
  • Are logs and crashes accessible to user and developer?
  • Have all errors been produced in testing?
  • What state is the user left in after an error message?
  • Have no errors appeared when they should have?

Error messages quite often creep into the user experience. Bad and unhelpful errors are everywhere. Trying to stop users from encountering error messages would be ideal, but this is probably impossible. Errors can be designed for and implemented and verified against expectations, but testers are great at finding unexpected bugs and at carefully considering whether what they see could be improved.

Some Examples of Error Messages

I like the example below of an error message in the Facebook app on the iPhone. Not only is the text somewhat longwinded and sheepishly trying to cover many different scenarios, but there is also the possibility that the message gets lost into the ether.

 

Perhaps the messages below are candidates for the Hall of Fame of how not to write messages?

A badly written message. A badly written message.

What about this one from The Guardian’s app for the iPad? What if I don’t want to “Retry�?

The Guardian's

Platform-Specific Considerations

Becoming knowledgeable about the business, technology and design constraints of relevant platforms is crucial for any project team member.

So, what types of bugs do testers look for in mobile apps?

  • Does it follow the design guidelines for that particular platform?
  • How does the design compare with designs by competitors and in the industry?
  • Does the product work with peripherals?
  • Does the touchscreen support gestures (tap, double-tap, touch and hold, drag, shake, pinch, flick, swipe)?
  • Is the app accessible?
  • What happens when you change the orientation of the device?
  • Does it make use of mapping and GPS?
  • Is there a user guide?
  • Is the email workflow user-friendly?
  • Does the app work smoothly when sharing through social networks? Does it integrate with other social apps or websites?
  • Does the app behave properly when the user is multitasking and switching between apps?
  • Does the app update with a time stamp when the user pulls to refresh?
  • What are the app’s default settings? Have they been adjusted?
  • Does audio make a difference?

Example: ChimpStats

ChimpStats is an iPad app for viewing details of email campaigns. I first started using the app in horizontal mode. I got a bit stuck as soon as I wanted to enter the API key. I couldn’t actually enter any content into the API field unless I rotated it vertically.

ChimpStats

ChimpStats

Connectivity Issues And Interruption

Funny things can happen when connections go up and down or you get interrupted unexpectedly.

Have you tried using the app in the following situations:

  • Moving about?
  • With Wi-Fi connectivity?
  • Without Wi-Fi?
  • On 3G?
  • With intermittent connectivity?
  • Set to airplane mode?
  • When a phone call comes in?
  • While receiving a text message?
  • When receiving an app notification?
  • With low or no battery life?
  • When the app forces an update?
  • When receiving a voicemail?

These types of tests are a breeding ground for errors and bugs. I highly recommend testing your app in these conditions — not just starting it up and checking to see that it works, but going through some user workflows and forcing connectivity and interruptions at particular intervals.

  • Does the app provide adequate feedback?
  • Does data get transmitted knowingly?
  • Does it grind to a halt and then crash?
  • What happens when the app is open?
  • What happens midway through a task?
  • Is it possible to lose your work?
  • Can you ignore a notification? What happens?
  • Can you respond to a notification? What happens?
  • Is any (error) messaging appropriate when something goes wrong?
  • What happens if your log-in expires or times out?

Maintaining The App

Speeding up the process of testing an app is so easy. Test it once and it will be OK forever, right?

Think again.

One problem I’m facing at the moment with some apps on my iPad is that they won’t download after being updated. As a user, this is very frustrating.

Perhaps this is out of the control of the app’s developer. Who knows? All I know is that it doesn’t work for me as a user. I’ve tried removing the app and then reinstalling, but the problem still occurs. I’ve done a bit of searching; no luck with any of my questions, aside from suggestions to update my OS. Perhaps I’ll try that next… when I have time.

The point is, if the app was tested once and only once (or over a short period of time), many problems could have gone undetected. Your app might not have changed, but things all around it could make it break.

When things are changing constantly and quickly, how does it affect your app? Ask yourself:

  • Can I download the app?
  • Can I download and install an update?
  • Does the app still work after updating?
  • Can I update the app when multiple updates are waiting?
  • What happens if the OS is updated?
  • What happens if the OS is not updated?
  • Does the app automatically sync downloading to other devices via iTunes?
  • Is it worth automating some tasks or tests?
  • Does the app communicate with Web services? How would this make a difference?

Testing your mobile app after each release would be wise. Define a set of priority tests to cover at each new release, and make sure the tests are performed in a variety of conditions — perhaps on the most popular platforms. Over time, it might be worth automating some tests — but remember that automated tests are not a magic bullet; some problems are spotted only by a human eye.

Example: Analytics App on the iPhone

I’ve had this app for two years now. It’s worked absolutely fine until recently; now, it has been showing no data for some of my websites (yes, more than one person has visited my website over the course of a month!). A quick look at the comments in the app store showed that I wasn’t the only one with this problem.

Here is another example from the Twitter app for the iPhone. After updating and starting up the app, I saw this message momentarily (Note: I have been an active tweeter for five years). I got a bit worried for a second! Thankfully, the message about having an empty timeline disappeared quickly and of its own accord.

Testing Is Not Clear-Cut

We’ve covered some ground of what mobile testing can cover, the basis of it being: with questions, we can find problems.

All too often, testing is thought of as being entirely logical, planned and predictable, full of processes, test scripts and test plans, passes and fails, green and red lights. This couldn’t be further from the truth.

Sure, we can have these processes if and when necessary, but this shouldn’t be the result of what we do. We’re not here just to create test cases and find bugs. We’re here to find the problems that matter, to provide information of value that enables other project members to confidently decide when to release. And the best way we get there is by asking questions!

(al)


© Rosie Sherry for Smashing Magazine, 2012.


40 Must-See Portfolio-Websites Of Photographers, Illustrators and Photoshop-Artists


  
Portfolios can be a great way to prove how multifaceted an artist is. But this is only true while it is true for the artist and for the website. You might be a superb photographer, but if your website is not representing these skills you might have a hard time acquiring clients this way. Today we show you portfolios of artists who understood the necessity to show their skills in a skilled way. Get inspired and learn from these best practices.

BootMetro: Framework for Websites in Windows 8 Design


  

We know you like tiled websites. Our article featuring Sergey Pimenov's Metro UI CSS drew loads of attention. So we thought you might also be interested in what the Italian frontend-developer Marcello Palmitessa created. By the name of BootMetro he just introduced a whole framework for building tiled websites in the Metro-style of the forthcoming Windows 8. Palmitessa took Twitter's Bootstrap and changed its looks to mimic Redmond's next-generation operating system. And yes, he not only got inspired by Pimenov's Metro UI CSS.


Tutorial: 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">
<head>
<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; }
</style>

</head>

<body>

<canvas> </canvas>
<script>
// all the code goes here
</script>

</body>
</html>

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
        POP.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() {
                window.scrollTo(0,1);
        }, 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.beginPath();
        POP.ctx.arc(x + 5, y + 5, r, 0,  Math.PI * 2, true);
        POP.ctx.closePath();
        POP.ctx.fill();
    },

    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.clear();
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) {
    e.preventDefault();
    POP.Input.set(e);
}, false);

// listen for touches
window.addEventListener('touchstart', function(e) {
    e.preventDefault();
    // the event object has an array
    // named touches; we just want
    // the first touch
    POP.Input.set(e.touches[0]);
}, false);
window.addEventListener('touchmove', function(e) {
    // we're not interested in this,
    // but prevent default behaviour
    // so the screen doesn't scroll
    // or zoom
    e.preventDefault();
}, false);
window.addEventListener('touchend', function(e) {
    // as above
    e.preventDefault();
}, 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
POP.loop();

// 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() {

   POP.Draw.clear(); 
},

// the actual loop
// requests animation frame,
// then proceeds to update
// and render
loop: function() {

    requestAnimFrame( POP.loop );

    POP.update();
    POP.render();
}

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) {
        POP.entities[i].update();

        // 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) {
        POP.entities[i].render();
    }

},

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) {
    POP.entities[i].update();

    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(
            POP.entities[i].x, 
            POP.entities[i].y, 
            2, 
            // random opacity to spice it up a bit
            'rgba(255,255,255,'+Math.random()*1+')'
        )); 
    }
    POP.score.hit += 1;
}

Waves

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.Draw.circle(
                POP.wave.x + POP.wave.offset +  (i * POP.wave.r), 
                POP.wave.y,
                POP.wave.r, 
                '#fff'); 
}

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!

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)


© Eoin McGrath for Smashing Magazine, 2012.


Tutorial: 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">
<head>
<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; }
</style>

</head>

<body>

<canvas> </canvas>
<script>
// all the code goes here
</script>

</body>
</html>

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
        POP.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() {
                window.scrollTo(0,1);
        }, 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.beginPath();
        POP.ctx.arc(x + 5, y + 5, r, 0,  Math.PI * 2, true);
        POP.ctx.closePath();
        POP.ctx.fill();
    },

    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.clear();
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) {
    e.preventDefault();
    POP.Input.set(e);
}, false);

// listen for touches
window.addEventListener('touchstart', function(e) {
    e.preventDefault();
    // the event object has an array
    // named touches; we just want
    // the first touch
    POP.Input.set(e.touches[0]);
}, false);
window.addEventListener('touchmove', function(e) {
    // we're not interested in this,
    // but prevent default behaviour
    // so the screen doesn't scroll
    // or zoom
    e.preventDefault();
}, false);
window.addEventListener('touchend', function(e) {
    // as above
    e.preventDefault();
}, 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
POP.loop();

// 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() {

   POP.Draw.clear(); 
},

// the actual loop
// requests animation frame,
// then proceeds to update
// and render
loop: function() {

    requestAnimFrame( POP.loop );

    POP.update();
    POP.render();
}

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) {
        POP.entities[i].update();

        // 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) {
        POP.entities[i].render();
    }

},

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) {
    POP.entities[i].update();

    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(
            POP.entities[i].x, 
            POP.entities[i].y, 
            2, 
            // random opacity to spice it up a bit
            'rgba(255,255,255,'+Math.random()*1+')'
        )); 
    }
    POP.score.hit += 1;
}

Waves

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.Draw.circle(
                POP.wave.x + POP.wave.offset +  (i * POP.wave.r), 
                POP.wave.y,
                POP.wave.r, 
                '#fff'); 
}

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!

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)


© Eoin McGrath for Smashing Magazine, 2012.


  •   
  • Copyright © 1996-2010 BlogmyQuery - BMQ. All rights reserved.
    iDream theme by Templates Next | Powered by WordPress