Writing Games in HTML5

Last updated:

So you've decided to write a game for the Web, and you've heard about this "HTML5" thing. Maybe you've seen a few experiments using HTML5 stuff. Maybe you've just decided that Flash sucks. Whatever your reason is, there are some basic things you'll need to know to write this game. Some of them are obvious but worth saying, some of them are tricky shortcomings of the platform, and some are just useful tidbits.

Putting Things On The Screen

The most basic thing you need to do is draw things on the screen. There are two basic ways to do this - <canvas> and <svg>. These two technologies both let you put graphics on the screen, but they do so in fundamentally different ways, so either or both or them could be useful to you in creating your game.

The fundamental difference is the Canvas is an immediate mode graphics API, while SVG is retained mode. What this means is that SVG 'remembers' what you've put into it, so you can go back and ask it to alter what you'd already done. For example, you can draw a circle in SVG with the <circle> element, and then animate it by changing its x and y attributes. Canvas, on the other hand, is a dumb bag of pixels - as soon as you draw something into it, it forgets the source of the change and just treats it as an image. If you want to move a circle around, you have to erase and redraw the entire frame with the circle slightly moved in each frame. (There are techniques to mitigate the pain of this, which I'll go into below.) As such, SVG is more similar to to manipulating an ordinary web page, or working with Flash's vector capabilities, while Canvas is more like working with a traditional image editor. SVG can be easier to work with sometimes, but Canvas can be faster and give you more precise control. Consider both when deciding how to start.

Working With Canvas

Canvas works in all modern browsers, and will be available in IE9 as well, so anything you make with Canvas should be pretty available to your users immediately. The full spec for canvas is at

The first step is just putting a canvas in your page. Do this with the <canvas> element, like this:

<!doctype html>
<canvas width=500 height=500></canvas>

It's as simple as it looks, really. The canvas is now part of the page and ready to be manipulated. Now you need to get a context out of the canvas, which you'll use to draw things. Contexts are different methods of drawing things. For example, the context we'll be using in the example is the 2d context, used for drawing flat things (though of course, you can fake 3d if you want). There is also a WebGL context you can enable in experimental Chrome and Firefox builds (and which will be available publicly in each in the near future) that lets you use OpenGL to do full 3d scenes in a canvas. These two contexts are mutually independent - it doesn't make sense to use use them together, at least in a naive fashion. Thus, they're segregated into two different contexts, which each expose their own drawing APIs.

var canvas = document.getElementsByTagName("canvas")[0];
var context = canvas.getContext('2d');

Now, you draw. The current 2d context is intentionally very simple and low-level, so don't worry if it looks primitive. Libraries are being created right now that build richer drawing primitives on top of this base, and more than likely we'll see a better 2d context come out at some point as well.

// First, we'll paint the whole canvas black.
context.fillStyle = "black";

// Now we'll draw some shapes
context.fillStyle = "#06c";
context.strokeStyle = "white";
// These can be any CSS color.
context.lineWidth = 3;

// A triangle!  And a rainbow!
var rainbow = context.createLinearGradient(150,50,150,150);
context.fillStyle = rainbow;

// Some text!  And a shadow!
context.shadowOffsetX = -2;
context.shadowOffsetY = 2;
context.shadowColor = "#f88";
context.shadowBlur = .01;
context.fillStyle = "red";
context.font = "bold 72px monospace";
context.fillText("Foo Bar",30,400);

That's about it; there are only a few other primitives for you to learn, but nothing mindblowing. Canvas is really simple. Using it, though, you can draw anything. For example, some people have already created more advanced libraries for drawing. Check out the Akihibara library http://www.canvasdemos.com/2010/05/11/akihabara/, which is designed specifically to make pixel-based canvas games easier to write.

Working With Audio

Any game needs sound effects, or at least ambient music. HTML5 makes this easy with the <audio> element. For games you won't be using the element directly in the page; instead, you'll just create Audio elements in Javascript and use them directly from there.

Doing so is extremely simple:

var sfx = new Audio("http://www.example.com/sound-effect.mp3");

Unfortunately, different browsers support different audio formats right now. There's no single format that everyone uses, but if you provide your sound effects in both mp3 and wav, you'll cover everyone. The Audio element gives you a way to test which type the browser will play:

var audio = new Audio();
if( audio.canPlayType("audio/mp3") )
	audio.src = "piano" + i + ".mp3";
	audio.src = "piano" + i + ".wav";

The canPlayType() function can return 3 values - "" (the empty string) for no, "maybe", and "probably". There is no "yes", because there's enough variation in general that browsers can't usually tell for sure until they pass the data to their audio decoders. In practice, a "maybe" is good enough. Both "maybe" and "probably" are treated as true, while "" is treated as false, so it's easy to test.

Warning! Browser support for Audio is still... somewhat lacking. There are several bugs and pitfalls you need to be aware of.

  1. Chrome (and possibly other browsers, but I haven't tested) doesn't like sounds that are too short, and will produce odd audio artifacts, if it plays the sound at all. To be safe, keep your sounds above 2 seconds in length. This doesn't mean the sound itself has to last that long; you can pad out the end with silence to the desired length using tools like SOX http://sox.sourceforge.net/.
  2. No browser currently can play a single Audio more than once, and they get a little unhappy if you just try to play the same Audio repeatedly without enough time between plays. If you collect several coins in quick succession, for example, you want all the coins to make a sound, even if those sounds overlap somewhat in time. To get around this, you'll have to create several duplicate Audios, stuff them in an array, and loop through the array so you're always playing a "fresh" Audio. Estimate how many times you think a particular sound effect can overlap itself, double it just to be safe, and make an array of that size. Keep around a variable telling the index of the current "fresh" Audio in the array, and every time you play that sound, increment the index, looping back to the beginning when it gets too large.

If you need sound effects for your game, there are many free sources on the net, but many of them are confusing and difficult to navigate, or publish the sounds in strange format. I recommend using the Super Flash Bros sound effect generator at http://www.superflashbros.net/as3sfxr/. You can tweak the sound variables to your liking or, if you're like me and don't understand what any of that means, just hit "Random" until something good comes up. The range of sounds it can generate is astonishing. It only exports as WAV, though, so you'll have to convert it to MP3 to get the sound to play in Chrome. Remember that the sounds it generates are often very short, too, so be lengthen it with silence if necessary.

Controlling Your Game

Quick note: if you need to tell where on the canvas the mouse is, use this code:

function findPos(obj) {
	var curleft = curtop = 0;
	if (obj.offsetParent) {
		do {
			curleft += obj.offsetLeft;
			curtop += obj.offsetTop;
		} while (obj = obj.offsetParent);
	return [curleft,curtop];

//The 'mouse' variable is assumed to be global in scope here.
var mouse = {x:0, y:0}; 
window.addEventListener('mousemove', function(e) {
	var canvaspos = findPos(canvas);
	mouse = {'x':e.clientX - canvaspos[0], 'y':e.clientY - canvaspos[1]};

(a limited set of Markdown is supported)