In this article, I am going to tell you how to create spritesheet animations in HTML5 canvas using javascript. You can use these for creating animations in your HTML5 game. I will be using javascript(as the title says), but you can use this concept in other canvas, like canvas in Android(java).

What are we creating?

It's better if we know what we are going to create. So here you go:

Prerequisites

  • Javascript

What are sprites, spritesheet and spritesheet animation?

Animations consist of a series of images(or frames) which are shown very quickly so the viewer perceive it as moving(instead of still image). Each of the image(or frame) in an animation is known as sprite. It can be something like this:
or

A series of these sprites laid down in a single sheet is what we say as spritesheet, like this: Just Google "spritesheet", and you gonna get a lot of them. Even better, create your own using TexturePacker!

Animation created using a spritesheet is called spritesheet animation, this is what we are gonna acheive.

Why to use a single image for all the sprites? Why not we just have different images and display them one by one? Hmm... We can do that, but using spritesheets have their own advantages. Having multiple images requires handling each of them individually and requires more memeory, and more images need to be loaded. While in spritesheet we need to load a single image, and can use it multiple times. Spritesheets have been in games since a long time, one classic example is pacman.

Concept

We will draw a single sprite at a time. Which will be pointed by some index pointer.

And after a certain duration, we will increment our index pointer. And keep doing this, untill we reach the last frame, or in the case of a looping animation we will set the index back to 0.(indexing starts from 0).

The drawImage() method of context allows us to draw an image cropped(clipped) and resized and at any (x, y) coordinate on the canvas. It takes the following parameters:

context.drawImage(img, sx, sy, swidth, sheight, x, y, width, height)
imgThe image to be drawn
sxThe x coordinate where to start clipping
syThe y coordinate where to start clipping
swidthThe width of image to be draw(clipping)
sheightThe height of image to be draw(clipping)
xThe x coordinate on the canvas where the image to be drawn
yThe y coordinate on the canvas where the image to be drawn
widthThe width of the image(resizing it)
heightThe height of the image(resizing it)

Implementation

Let's start coding it. Create an HTML file and add a canvas to it. Like this:

1
2
3
4
5
6
7
8
9
<!doctype HTML>
<Head>
    <title>Spritesheet Animation</title>
</Head>
<Body>
    <canvas id="canvasHolder" height="250px" width="300px" style="border: 2px solid black"></canvas>
    <script src="game.js"></script>
</Body>
</HTML>

You can adjust the size of canvas by changing the width and height attributes. If you dont's specify these attributes, the browser by default creates the canvas with a width of 300 pixels and a height of 150 pixels.

Let's create the script file. Create a new game.js file in the same directory as your html file. First we need to get the canvas element and it's context to draw something on the canvas.

1
2
3
4
5
var canvas, context;
window.onload = function() {
    canvas = document.getElementById("canvasHolder");
    context = canvas.getContext("2d");
}

Note that I am getting the canvas in the onload method. This will make sure that the canvas is loaded before being getting it.

Now let's create an object which represents an object inside the game. I am going to call it GameObject. Think of a GameObject as anything that is in your game. Everything in your game is considered as a single GameObject. It is your player, the enemies, the trees, pickups, etc. All these GameObjects will be updated and drawn on the canvas in every frame refresh, in a continous loop "THE GAME LOOP". The game loop is a continous cycle that keeps on looping untill the game ends. Think of it like this, in animated movies a loop is there which keeps drawing the frames on the screen. In games, other than just drawing frames on screen, we also have to do some(a lot) of calculations depending on player's behaviour. So, we have two things in our loop, update and draw. i.e. update all the GameObjects and draw them all.

1
2
3
4
5
6
loop {
    ...
    update()
    draw()
    ...
}

All of these GameObjects will have an image(the Spritesheet)which will be used for drawing, a coordinate representing the position on the canvas(or in the game world), size, and some other attributes that will help in drawing its sprite. You will need to add a lot of attributes once your game starts getting complex. For now, I am keeping it simple enough to acheive our goal. Here's the GameObject constructor:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//GameObject constructor
function GameObject(spritesheet, x, y, width, height, timePerFrame, numberOfFrames) {
    this.spritesheet = spritesheet;             //the spritesheet image
    this.x = x;                                 //the x coordinate of the object
    this.y = y;                                 //the y coordinate of the object
    this.width = width;                         //width of spritesheet
    this.height = height;                       //height of spritesheet
    this.timePerFrame = timePerFrame;           //time in(ms) given to each frame
    this.numberOfFrames = numberOfFrames || 1;  //number of frames(sprites) in the spritesheet, default 1

    //current frame index pointer
    this.frameIndex = 0;

    //time the frame index was last updated
    this.lastUpdate = Date.now();

    //to update
    this.update = function() {
        if(Date.now() - this.lastUpdate >= this.timePerFrame) {
            this.frameIndex++;
            if(this.frameIndex >= this.numberOfFrames) {
                this.frameIndex = 0;
            }
            this.lastUpdate = Date.now();
        }
    }

    //to draw on the canvas, parameter is the context of the canvas to be drawn on
    this.draw = function(context) {
        context.drawImage(this.spritesheet,
                          this.frameIndex*this.width/this.numberOfFrames,
                          0,
                          this.width/this.numberOfFrames,
                          this.height,
                          x,
                          y,
                          this.width/this.numberOfFrames,
                          this.height);
    }
}

And here's the game loop code with update and draw methods:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//The Game Loop
function loop() {
    update();
    draw();
    requestAnimationFrame(loop);
}

//update function to update all the GameObjects
function update() {

}

//draw method for drawing everything on canvas
function draw() {

}

Currently the update and draw methods are empty. Since we do not have any game object. So let's create one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//our hero
var hero;
var heroSpritesheet = new Image();
heroSpritesheet.src = "heroSpritesheet.png";   //provide the path to the image

window.onload = function() {
    canvas = document.getElementById("canvasHolder");
    context = canvas.getContext("2d");

    hero = new GameObject(heroSpritesheet,  //the spritesheet image
                              0,            //x position of hero
                              0,            //y position of hero
                              1536,         //total width of spritesheet image in pixels
                              256,          //total height of spritesheet image in pixels
                              90,           //time(in ms) duration between each frame change (experiment with it to get faster or slower animation)
                              6);           //number of sprites in the spritesheet
}

What next? Add the first loop call in the onload method, just before the end of it.

1
2
3
4
5
window.onload = function() {
    ...
    //call the game loop
    loop();
}

Hmm.. I dont see anything. Oh! remember we need to update and draw all the gameobjects. Add call to hero's update and draw methods at update() and draw() respectivily.

1
2
3
4
5
6
7
8
9
//update function to update all the GameObjects
function update() {
    hero.update();
}

//draw method for drawing everything on canvas
function draw() {
    hero.draw(context);
}

Great! Open the HTML page in your favourite browser and see the action.

What's that?

That's not exactly what I showed at the beginning. Something's not right.

A note on HTML5 canvas

The HTML5 canvas is just a drawing canvas. It draws the pixels where you said it to, but don't remember anything. It draws the image, and keeps drawing over it again and again. So, we see the remainng part of the previous images when new image is drawn over it.

How to fix it?

Fixing it is very simple. All we need to do is, before drawing the image, we have to clear the canvas. It can be done by context.clearRect() method. It clears the rectangular area defined by the parameters (x, y, width, height). Add the following in the draw method:

1
2
3
4
function draw() {
    context.clearRect(0,0,canvas.width, canvas.height);
    ...
}

In case you are drawing a background (which should be drawn first before drawing any other game object), then you don't need to clear the canvas. Since, the background image covers the whole canvas and overdraws on all the previous images.

Final note

This is it for this article, hope it helped you. I encourage you to try and experiment yourself. You can do a lot using sprites. Like this:

I used spritesheet animations in this game.

References