One of my favorite demos from the IE9 Test Drive site is FishIE Tank. Labeled a “Speed Demo” the FishIE Tank demonstrates hardware acceleration, canvas based image manipulation and PNG based sprite sheet animation. And watching the FPS meter on the side of the demo, with 500 fish on a wide screen monitor it still reads ~32 FPS, almost 60 fps with 250 fish. That’s worth taking a look at the code to see how its done.
I decided to recreate the demo to understand the code better. This first part covers only the background image rendering and the sprite sheet animation. Plus I’ve switched out the image and sprite sheet to something a little more unique.

Run the first Spinning Heads demo
(for the best results run it in the IE9 Preview, Chrome isn’t too bad, Firefox is not so good)
Of course since this is just HTML, JavaScript and Canvas you can View Source and read the commented code to figure out how the demo works. To save you some time, though, I’ll highlight the setup and the two main features.
Two canvas layers created during Setup
When the page loads, the setup function is called and two canvas elements are created and added to the page. Pointers to the drawing context are then saved for reuse during the drawBackground and draw functions. With CSS, the spriteCanvas is absolutely positioned so the heads appear over the background.
Drawing the background image
On load and when the window is resized the drawBackground function is called:
function drawBackground() {
if (WIDTH < backgroundImageW) {
//Show a portion of the background
ctx3.drawImage(backgroundImage, 0, 0, backgroundImageW, HEIGHT);
}
else {
//tile the backgroundImage horizontally
var tileCount = Math.floor(WIDTH / backgroundImageW);
//flip the image when flip==-1 for better looking tiles
var flip = 1;
//loop through all the tiles that are needed and position them
for (i = 0; i <= tileCount; i++) {
ctx3.save();
ctx3.transform(flip, 0, 0, 1, 0, 0);
ctx3.drawImage(backgroundImage, (flip - 1) * backgroundImageW / 2 + flip * backgroundImageW * i, 0, backgroundImageW, HEIGHT);
ctx3.restore();
flip = flip * -1;
}
}
}
The main thing to point out here is the flip variable used to mirror every other background tile to create a smoother horizontally repeating background image, no matter what image is used. Vertically the image is stretched to match the height of the window.
Drawing the Sprites
During the setup, the sprites or Head objects in the case, are created with the createHeads function. A loop is then created using the setInterval function to call the draw function every 36 milliseconds. During the draw function each Head object is told to spin, effectively moving the next step through the sprite sheet.
function draw() {
//clear the canvas
ctx.clearRect(0, 0, WIDTH, HEIGHT);
// Draw each head
for (var head in heads) {
heads[head].spin();
}
}
function Head() {
var x = Math.floor(Math.random() * (WIDTH - headW) + headW / 2);
var y = Math.floor(Math.random() * (HEIGHT - headH) + headH / 2);
var scale = Math.random() + .2;
var cellCount = 10;
var cell = Math.floor(Math.random() * (cellCount - 1));
var cellReverse = -1;
function spin() {
ctx.save();
ctx.translate(x, y);
ctx.scale(scale, scale);
ctx.drawImage(imageStrip, headW * cell, 0, headW, headH, -headW / 2, -headH / 2, headW, headH);
ctx.restore();
//increment to next state
if (cell >= cellCount - 1 || cell <= 0) {
cellReverse = cellReverse * -1;
}
//go back down once we hit the end of the animation
cell = cell + 1 * cellReverse;
}
return {
spin: spin
}
}
Important to note here that in the draw method, if the context is not cleared first, artifacts from previous “frames” will remain. This is not a retained mode graphics system, we’re just creating a big set of pixels that all share the same layer.
The next bit of code to call out here is within the spin method a call to save() and restore() is used to manually add and remove the translate and scale transforms. This way the transforms are unique to each Head. Otherwise the transforms by default are cumulative and each successive Head would be moved further and further off the screen. This is similiar to pushMatrix and popMatrix in Processing.
And finally the real magic, each successive draw call increments (or decrements when going in reverse) the cell variable which offsets the drawImage call so that the appropriate section of the sprite sheet is called.
So that’s it for the basics! If you’re familiar with procedural based animation the techniques should look pretty familiar to you. The biggest difference here is that there’s no plug-in or applet, just JavaScript and a modern browser.