Welcome to my short tutorial about HTML5 game programming. I’ll try to briefly explain how to create simple game using HTML5, canvas element and JavaScript. Some code will be omitted during this tutorial but feel free to view my game demo – I intentionally not minified source code.
HTML5 come with lots of fantastic features such as :
- canvas element
- video and audio support
- local storage
- offline web applications
- geolocation
…and much more. I won’t be covering HTML5 specification in this post but you can read more about HTML5 here : http://diveintohtml5.org/.
Games were created in HTML before HTML5 so why write this post now, did anything change? Well yes, <canvas> element come very handy when creating client-side games.
Canvas and JavaScript can be used to draw and animate game elements easily.
Typical Game class
Before we start with our game implementation and canvas element I would like to quickly show you the structure of typical Game class:
function Game() {
this.Initialize = function () {
// initialize all game variables
}
this.LoadContent = function () {
// load content – graphics, sound etc.
// since all content is loaded run main game loop
// Calls RunGameLoop method every ‘draw interval’
this.GameLoop = setInterval(this.RunGameLoop, this.DrawInterval);
}
this.RunGameLoop = function (game) {
this.Update();
this.Draw();
}
this.Update = function () {
// update game variables, handle user input, perform calculations etc.
}
this.Draw = function () {
// draw game frame
}
}
This is a typical JavaScript game skeleton. Interesting bit here is the “setInterval” method. When all resources are loaded, we can start the main game loop, collect user input, perform calculations and render our game every X ms. This is needed for games that perform some calculations in the background, AI movement, animations etc. For more static games that need to redraw content only based on user input we can modify this skeleton class and get rid of game loop.
function Game() {
this.Initialize = function () {
// initialize all game variables
}
this.LoadContent = function () {
// load content – graphics, sound etc.
var ourGame = this;
$(document).bind('keyup', function (event) {
ourGame.Update(event);
ourGame.Draw();
});
}
this.Update = function (event) {
// update game variables, handle user input, perform calculations etc.
}
this.Draw = function () {
// draw game frame
}
}
Here both the game update and redrawing will happen only in response for user input. This approach is less CPU intensive but is only possible for simple games.
I’m going to show you example of a quite simple, classic game that we all know: Sokoban. There are lots of sokoban clones made for every available platform but I didn’t see any written using canvas element yet.
Getting started
Let’s start with creating our HTML5 page with single canvas element:
<html> <head> <title>Sokoban</title> </head> <body> <canvas id="canvas" width="800" height="500"> Canvas not supported. </canvas> </body> </html>
That’s it! Now we can see…blank page in browser supporting canvas element: Chrome, Firefox, Safari and “Canvas not supported” in Internet Explorer 8 and older versions.
Before we can start drawing on canvas we need to get the drawing context. Canvas exposes one or more drawing contexts but we will focus on the most popular and supported one – “2d” context.
Let’s add reference to JavaScript file straight after our closing canvas tag:
<script type="text/javascript" src="../Scripts/test01.js"></script>
This is part of this JavaScript file.
var _canvas = document.getElementById('canvas');
var _canvasContext = null;
//check whether browser supports getting canvas context
if (_canvas && _canvas.getContext) {
_canvasContext = _canvas.getContext('2d');
// ... drawing here ...
}
I will quickly cover canvas context drawing methods that will be used during this tutorial. The only methods that we need are :
- drawImage(img, x, y);
- fillRect(x, y, width, height);
- strokeRect(x, y, width, height);
- fillText(‘Text’, x, y);
Note: It’s important to remember that canvas has it’s beginning (0,0) in upper left corner.
Those methods are very straight-forward. drawImage is drawing specified Image object or <img> on canvas in location specified by x, y. fillRect and strokeRect are both used for drawing rectangles, the only difference is that the first method is drawing rectangle filled with colour and the second one is drawing empty rectangle with coloured border. fillText is used to put text on canvas.
Working demo can be found here: http://demo2.felinesoft.com/Sokoban/Home/Test01
JavaScript source that is rendering our 2 rectangles, text and an image can be found here:
http://demo2.felinesoft.com/Sokoban/Scripts/Test01.js
This is how the test page should look like :

Double buffering
Since we now have game skeleton and know how to draw on canvas the only thing that is left before actual game implementation is double buffering. In our game double buffering does not come that handy since we won’t have any animating effects that can flicker, but since this post should be your starting point in the land of canvas game programming I figured that I’ll quickly show you how to implement simple double buffering on canvas.
The idea behind double buffering is to reduce flickering by first drawing in memory buffer and later drawing the entire image from the memory onto the screen.
We only need to modify our canvas JavaScript slightly:
_canvas = document.getElementById('canvas');
if (_canvas && _canvas.getContext) {
_canvasContext = _canvas.getContext('2d');
_canvasBuffer = document.createElement('canvas');
_canvasBuffer.width = _canvas.width;
_canvasBuffer.height = _canvas.height;
_canvasBufferContext = _canvasBuffer.getContext('2d');
}
Now instead of drawing on _canvasContext object we should draw on _canvasBufferContext and after the drawing is done call just one line:
_canvasContext.drawImage(_canvasBuffer, 0, 0);
This will draw whole content of our canvas buffer onto the screen and that’s it!
So what exactly are we trying to write?
We need to write simple Sokoban game. The goal of the game is to move all “boxes” to marked fields. Game rules:
- Boxes can only be pushed, not pulled.
- Only one box can be moved at a time
For more information about Sokoban visit: http://en.wikipedia.org/wiki/Sokoban
We will need to create few JavaScript classes for our game :
- Main “Game” class
- “Map” representation
- Player
- Map statistics – this will need to display level number, number of moves / pushes performed
- Class for every “drawable” element :
- Wall
- Box
- Box on Goal
- Floor
- Empty space
- Goal
- Image Repository – this class will be storing all images used in our game. Instead of loading and creating new instance of Image we will be able to get our images from here.
Map representation
In my example all maps are stored in one XML file. I’m using the following format:
<?xml version='1.0'?> <Levels> <Level No="1" Width="19" Height="11"> <Row> #####</Row> <Row> # #</Row> <Row> #$ #</Row> <Row> ### $##</Row> <Row> # $ $ #</Row> <Row>### # ## #===######</Row> <Row># # ## ##### ..#</Row> <Row># $ $ ..#</Row> <Row>##### ### #@## ..#</Row> <Row> # #########</Row> <Row> #######</Row> </Level> .... </Levels>
Explanation of character representation:
- ‘#’ – wall
- ‘$’ – box
- ‘ ‘ or ‘=’ – empty space
- ‘ ‘ – Floor
- ‘@’ – player
- ‘.’ – goal
- ‘*’ – box on goal
- ‘+’ – player on goal
I’ve created a base class for all elements that can be draw:
function DrawableElement() {
this.GetImage = function () {
//implement this method in your sub-class if your element is represented by image
//this should return an Image object
return null;
}
this.GetFillStyle = function () {
//implement this method in your sub-class if your element is represented by filled rectangle
//this should return canvas "fillStyle" string
return null;
}
this.ImageRepository = null;
}
Sample sub-class (ImageRepository implementation omitted but can be found by looking at demo page source files):
function Floor(){
this.GetImage = function () {
return this.ImageRepository.Floor;
}
}
JavaScript inheritance creation:
Floor.prototype = new DrawableElement(); Floor.prototype.constructor = Floor;
I’m using very simple WCF service to return a specific level. You can store each level in a separate XML file, just change ajax url parameter to url path pointing to your xml file, for example:
“http://<domain>/level_” + levelNumber + “.xml”,
It’s very easy to access web service or direct xml file using jQuery :
$.ajax({
type: "GET",
url: "http://localhost/SokobanWCF/SokobanService.svc/GetMap/" + levelNumber,
dataType: "xml",
success: this.ParseXmlMap,
context: this
});
jQuery is also very useful for parsing XML, the following code demonstrate how a map can be constructed ( part of ParseXmlMap method ):
$(xml).find("Row").each(function () {
var wall = false;
var row = $(this).text();
for (var x = 0; x < mapWidth; x++) {
// some rows are shorter than map width - fill rest with Empty elements
if (x >= row.length) {
mapRef[x][y] = new Empty();
} else {
switch (row[x]) {
case " ":
// if we had wall already that mean we need to insert a Floor element,
// for Empty elements that are between walls on some maps we are using '=' character
if (wall) {
mapRef[x][y] = new Floor();
}
else {
mapRef[x][y] = new Empty();
}
break;
case "#":
mapRef[x][y] = new Wall();
wall = true;
break;
case "$":
mapRef[x][y] = new Box();
break;
(...)
As you can see each map/level is represented using two dimensional Array. I’m iterating through every XML element – <Row> and later through every character within the row value.
Since downloading and parsing XML document can take a while I’m using “Loaded” flag on Map object to determine whether document parsing have ended and map is ready to be draw. In my Game class in LoadContent method I’m setting the timer to check whether map is ready ( every 50 ms ) :
this.InitialUpdateDraw = setInterval(this.InitialUpdateRun, this.CheckInterval);
and InitialUpdateRun method :
this.InitialUpdateRun = function (ev) {
if (_map.Loaded && _imageRepository.Loaded()) {
document.sokobanGame.Update(ev);
document.sokobanGame.Draw();
//we don't need timer anymore
clearInterval(document.sokobanGame.InitialUpdateDraw);
}
}
This is needed since when the level is loaded we need to update game variables and display the level. Later, all game Update and Draw methods will only be called after user performs some sort of action.
Updated method for Map is very simple. We only need to check for end game conditions and update number of moves / pushes.
this.Update = function () {
//check for end game conditions
if (this._goals > 0 && this._goalsAchived == this._goals) {
this.Finished = true;
}
_levelStatistics.Update();
}
Finally the Map Draw method. Thanks to DrawableElement base class and inheritance we just need to iterate through the two dimensional Array and the correct image will be draw for each element because of our JavaScript implementation of Polymorphism.
this.Draw = function (canvasContext) {
var xCanvasPos = 20;
var yCanvasPos = 20;
var tileSize = 30;
for (var y = 0; y < this._height; y++) {
xCanvasPos = 20;
for (var x = 0; x < this._width; x++) {
var img = this._map[x][y].GetImage();
if (img != null) {
// draw image
canvasContext.drawImage(img, xCanvasPos, yCanvasPos);
} else {
// draw rectangle
canvasContext.fillStyle = this._map[x][y].GetFillStyle();
canvasContext.fillRect(xCanvasPos, yCanvasPos, tileSize, tileSize);
}
xCanvasPos += tileSize;
}
yCanvasPos += tileSize;
}
// this is used to properly position level statistics according to the level
this.OnScreenWidht = xCanvasPos;
this.OnScreenHeight = yCanvasPos;
_levelStatistics.Draw(canvasContext);
}
Player movement
Player movement is being handled on keyup event. I used WASD buttons to handle user movement. Just as a reminder W = up, S = Down, A = Left, D = Right.
Move direction is represented by MoveDirection enumeration.
Handler method for user movement :
this.KeyCheck = function (event) {
var KeyID = event.keyCode;
switch (KeyID) {
case 87: // W
this.Move(this.MoveDirection.Up);
break;
case 65: // A
this.Move(this.MoveDirection.Left);
break;
case 68: // D
this.Move(this.MoveDirection.Right);
break;
case 83: // S
this.Move(this.MoveDirection.Down);
break;
}
}
Obviously user cannot move two boxes or go through walls. We need a method to validate user movement:
this.ValidateMove = function (targetCell, nextCell) {
var posToMove = targetCell.constructor;
if (posToMove == Wall) {
// wall is next, player cannot move there
return false;
}
var nextObject = nextCell.constructor;
if ((posToMove == Box || posToMove == BoxOnGoal) && (nextObject == Wall || nextObject == Box || nextObject == BoxOnGoal)) {
//player attempts to push box, if the next element after the box is wall or another box – player cannot move
return false;
}
return true;
}
The only interesting bit here is the “constructor” property. This property returns a function that was used to create object stored in this cell. That way we can easily check what type of elements surrounds the player.
In previous section, while creating Wall, Floor and DrawableElement base class I’ve created inheritance using the following way:
Floor.prototype = new DrawableElement(); Floor.prototype.constructor = Floor;
Second line is very important here, without this line returned function for Floor object would be DrawableElement…this would prevent us from using move validation as described above.
I’ll omit the full code responsible for changing player position; it’s just assigning player object to new position on our two dimensional array and can be viewed by examining source code on the demo page.
Level finished and loading new level
I used cookies to be able to load and save previous state for user; after level is finished I’m inserting cookie specifying level that needs to be loaded. LoadContent is reading the cookie and loading the right map. That way the user can close browser and come back to the same level later. Loading and saving of cookies is handled by cookie plugin that can be found here: http://plugins.jquery.com/project/cookie
I’ve also added “Congratulations” popup when user finished level with a button saying “Go to the next level”. Automatic map loading seemed to me not very user friendly.
My sokoban clone contains 40 classic levels so it’s very possible that user won’t be able to finish them all during one session.
Finally!
View Demo
I wanted to make this blog post as brief as possible. It turned out quite long but finally we are at the end of our journey. This is how our game looks like; see image below or visit View Demo.
I’ve skipped some code during this “Accelerated” tutorial but I left it non-minified so it can be viewed by looking at live demo source code.

What next?
Possible game improvements that are out of scope of this tutorial:
- High score list ( after level is finished POST number of moves and pushes to the WebService and store them in Database, later display top 5 best scorers for every level )
- Undo move option.
- More secure end game conditions check. Currently it’s very easy to start with map level 40.
- In case you want to give ability for the users to navigate between levels, don’t worry about previous point – implement level selection instead.
- Ability to upload your own maps and share them with friends.
Hope you’ve enjoyed reading this post and it gave you a bit of insight on how game programming in HTML5 looks like.
