In my CSS Tricks for HTML5 Canvas Games post, I covered techniqes such as upscaling, anti-aliasing, and the use of 3D. In this post we will build a library for trackig transformation matrices, which can save you a lot of time and hassle down the road.

Canvas offers some great built-in transformations such as scale, transform and rotate. However, the API unfortunately makes it impossible to retrieve the current value of the transformation matrix. Let’s take a look at how to build a library that tracks the transformation matrix and allows you to both set and retrieve its value.

## Tracking transformation matrices in HTML5 canvas

The 2D canvas API offers a number of convenient built-in functions to perform transformations, such as scale, transform, and rotate. Furthermore, the setTransform() function is able to set an arbitrary transformation matrix.

Unfortunately for us, it is impossible to retrieve the value of the current transformation matrix. The API simply does not expose the matrix via any public method.

We will examine the motivation behind tracking the transformation matrix, and walk through the implementation of a class to track the current transformation matrix and retrieve it at will.

## The Problem

Consider the following transformations:

1 2 3 4 5 6 7 8 |
var context = canvas.getContext(‘2d’); context.translate(200, 0); context.rotate(Math.PI / 4); context.scale(2, 2); // what is the current transformation matrix being used at this point? // we’re out of luck -- no method is available to retrieve this information |

The transformation matrix would ultimately end up being a result of translating by 200 pixels in the x-direction, rotating by 45 degrees, and scaling by a factor of 2.

The following matrix would be the result:

1 |
[1.4142135623730951, 1.414213562373095, -1.414213562373095, 1.4142135623730951, 200, 0] |

This would be formally written as:

1 2 3 |
√2 -√2 200 √2 √2 0 0 0 1 |

The save() and restore() functions exposed by the canvas API do allow developers a certain degree of control by allowing restoration of a previous state of the matrix. This handy stack is internally maintained by the canvas API. Unfortunately, direct access to the stack itself is also not permitted, so this will not get us very far if we wish to directly retrieve information about the the transformation matrix in use.

## Motivation

Why would anyone want to retrieve the current state of the transformation matrix?

The main issue is that although we have applied a transformation matrix to graphical representation of objects on our canvas, the logical objects in our game world are going to be unaware of the current state of the transformation unless we have a way of tracking the transformation matrix independently of the graphical representation.

In the event we need to perform collision detection or user interaction among the logical representations, having access to the value of the transformation matrix can be very useful. We can ensure that both the visual representation of an object would appear rotated, translated, or scaled, and the logical representation of the object would be transformed in the same way.

## Implementation

One solution in this case would be to create a wrapper class around the canvas API. The wrapper class will track the current state of the transformation matrix, and mirror the implementation of canvas’ transformation stack.

At minimum, we’d want to override the following internal canvas methods. The wrapper function will need to contain logic to track the state of the transformation matrix as well as call the native function.

1 2 3 4 5 6 |
context.setTransform() context.translate() context.rotate() context.scale() context.save() context.restore() |

We’ll create a class called Transform, which will accept a canvas context object as a parameter:

1 2 3 |
function Transform(context) { this.context = context; ... |

To use this class, create a new Transform object and pass in the canvas context for which we want to track transformation matrix:

1 2 |
var context = canvas.getContext('2d'); var transform = new Transform(context); |

## Tracking the Matrix

We’ll want to set up a variable to track the current transformation matrix and initialize it with the identity matrix. We’ll also define a getter, setter, and a clone function.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function Transform(context) { this.context = context; this.matrix = [1,0,0,1,0,0]; this.getMatrix = function() { return this.matrix; }; this.setMatrix = function(m) { this.matrix = [m[0],m[1],m[2],m[3],m[4],m[5]]; this.setTransform(); //this will be used later }; this.cloneMatrix = function(m) { return [m[0],m[1],m[2],m[3],m[4],m[5]]; }; ... |

We’ll kick things off by wrapping the most basic canvas matrix function, setTransform(). All this will do is apply the current matrix being tracked by our class to the canvas context.

1 2 3 4 5 6 7 8 9 10 |
this.setTransform = function() { this.context.setTransform( this.matrix[0], this.matrix[1], this.matrix[2], this.matrix[3], this.matrix[4], this.matrix[5] ); }; |

Next, we’ll create a translate wrapper function. This function will accept an x, y offset (just like the native canvas translate function) and perform some math in order to apply the desired translation to the current transformation matrix.

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 |
this.translate = function(x, y) { this.matrix[4] += this.matrix[0] * x + this.matrix[2] * y; this.matrix[5] += this.matrix[1] * x + this.matrix[3] * y; this.setTransform(); }; this.rotate = function(rad) { var c = Math.cos(rad); var s = Math.sin(rad); var m11 = this.matrix[0] * c + this.matrix[2] * s; var m12 = this.matrix[1] * c + this.matrix[3] * s; var m21 = this.matrix[0] * -s + this.matrix[2] * c; var m22 = this.matrix[1] * -s + this.matrix[3] * c; this.matrix[0] = m11; this.matrix[1] = m12; this.matrix[2] = m21; this.matrix[3] = m22; this.setTransform(); }; this.scale = function(sx, sy) { this.matrix[0] *= sx; this.matrix[1] *= sx; this.matrix[2] *= sy; this.matrix[3] *= sy; this.setTransform(); }; |

Incidentally, the same math is already being done behind the scenes in the canvas API whenever one makes a call to context.translate(), but doing it ourselves allows us a greater degree of control — and we can store the result in our internal matrix variable.

At the end of the function, setTransform() is called in order to commit the transform to the canvas. In this case, the commit is included within the translate() function for convenience.

For better efficiency, you may want to pull the setTransform() commit out of the function (for example, if you wanted to create a batch of transforms and then commit only once at the end, rather than after each transform).

We can go ahead and create similar functions for rotation and scale. An explanation of the math behind these is beyond the scope of the article; I will defer to the vast amount of resources explaining affine transformations in this case.

## Retrieving the Matrix

Returning to our original example, here is how we would now perform the same translate + rotate + scale operations using our newly minted Transform class:

1 2 3 4 5 6 7 8 9 10 11 12 |
var context = canvas.getContext('2d'); var transform = new Transform(context); transform.translate(200, 0); transform.rotate(Math.PI / 4); transform.scale(2, 2); var currentMatrix = transform.getMatrix(); console.log(currentMatrix); // output will be: // [1.4142135623730951, 1.414213562373095, -1.414213562373095, 1.4142135623730951, 200, 0] |

By using the getMatrix() and setMatrix() functions, we can now both set and retrieve the value of the canvas context’s transformation matrix!

## Tracking the Stack

We can no longer make use of canvas’ native context.save() and context.restore() commands when using this Transform class. If we tried to use these, our matrix tracker would become woefully out of sync.

Fortunately, it is relatively simple to wrap the save() and restore() functions within our Transform class as well. It will be a matter of creating an array which will represent the stack, and functions to push and pop items from the stack:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
function Transform(context) { ... this.stack = []; this.save = function() { var matrix = this.cloneMatrix(this.getMatrix()); this.stack.push(matrix); this.context.save(); }; this.restore = function() { if (this.stack.length > 0) { var matrix = this.stack.pop(); this.setMatrix(matrix); } this.context.restore(); }; ... |

## Why Track the Stack?

Normally, there should be no need to directly access or manipulate the contents of the stack itself. However, for debugging purposes it can be quite valuable to take a peek into the stack.

There are some particularly sticky situations I’ve found myself in, when in the midst of debugging a large-scale application some unexpected transformation effects were occurring. This can come down to a single mismatched save/restore pair.

In this case, I had no visibility into the state of canvas’ internal transformation stack, so literally only trial and error was I able to find the mismatched save/restore pair. A glimpse into the state of the transformation stack would have come in handy!

## Retrieving the Stack

Here is an example of tracking the contents of the stack. Note that I’m using JSON.stringify here — this ensures the current value of the stack array is logged to the console.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var transform = new Transform(context); //initial stack console.log('initial stack:', JSON.stringify(transform.stack)); //translate transform.translate(200, 0); transform.save(); console.log('after translate:', JSON.stringify(transform.stack)); //rotate transform.rotate(Math.PI / 4); transform.save(); console.log('after rotate:', JSON.stringify(transform.stack)); //undo rotate transform.restore(); console.log('undo rotate:', JSON.stringify(transform.stack)); //undo translate transform.restore(); console.log('undo translate:',JSON.stringify(transform.stack)); |

The output is as follows:

1 2 3 4 5 6 7 8 9 10 11 |
initial stack: [] after translate: [[1,0,0,1,200,0]] after rotate: [[1,0,0,1,200,0],[0.7071067811865476,0.7071067811865475,-0.7071067811865475,0.7071067811865476,200,0]] undo rotate: [[1,0,0,1,200,0]] undo translate: [] And Beyond |

Because of how we’ve structured our Translate() implementation, as an added bonus, we can easily extend the canvas API with additional functionality beyond the usual translate/scale/rotate.

For instance, the canvas API does not natively offer any sort of skew or shear function. Fortunately, we can easily add a transform.shear() function which will manipulate the current matrix and then commit the result to the canvas.

Another useful set of extensions would be compound functions, which call a batch of other functions within our Translate class . A common case would address the need is to rotate an object about a certain coordinate; to handle this, we can define a rotateAbout() function in this way:

1 2 3 4 5 6 7 |
this.rotateAbout = function(rad, x, y) { this.translate(x, y); this.rotate(rad); this.translate(-x, -y); this.setTransform(); } |

Wrapping canvas functionality as shown here is certainly not the only approach to this issue; it will ultimately depend on the structure of your application. However, I hope that this presents a simple baseline that can be expanded upon as needed!

The complete code for the matrix transformation tracker can be found at https://github.com/kcmoot/transform-tracker.

## Safari Books Online has the content you need

Check out these HTML5 Canvas books available from Safari Books Online:

You’ll learn how to build interactive multimedia applications with HTML5 Canvas, using this new element to draw, animate, compose images, and more. You’ll also learn the best way to use existing JavaScript libraries, as well as how to incorporate related aspects such as audio and video. | |

Foundation HTML5 Canvas: For Games and Entertainment teaches you how to make exciting interactive games and applications using HTML5 canvas. Canvas lets you produce graphics, animations, and applications using the HTML5 and JavaScript web standards. It allows you to draw directly within the browser without the need for third-party plugins like Adobe Flash, and so canvas works perfectly across desktop and mobile devices, like the iPhone and Android. | |

HTML5 Games Development by Example will show you how to use latest HTML5 and CSS3 web standards to build card games, drawing games, physics games and even multiplayer games over the network. With the book you will build 6 example games with clear step-by-step tutorials. | |

HTML5 Canvas Cookbook begins by covering the basics of the HTML5 Canvas API and then provides techniques for handling features not directly supported by the API such as animations and canvas interactivity. It winds up by providing detailed templates for a few of the most common HTML5 canvas application. | |

Core HTML5 Canvas: Graphics, Animation, and Game Development presents a code-fueled, no-nonsense deep dive into that API, covering everything you need to know to implement rich and consistent web applications that run on a wide variety of operating systems and devices. | |

If you’re a web developer looking to use this new version of HTML, you might be wondering how much has really changed. Head First HTML5 Programming introduces the key features — including improved text elements, audio and video tags, geolocation, and the Canvas drawing surface — and uses concrete examples and exercises to apply and reinforce these concepts. |

## About this author

Kevin Moot has had an interest in computer graphics since creating games as a wee lad on his Apple IIe – complete with its vast array of six colors and a mind boggling 280×192 resolution. Kevin has worked with HTML5’s canvas technology for several cutting-edge websites and counts HTML/CSS, JavaScript and .NET among his specialties. He currently works at The Nerdery as an interactive software developer. |