We have already encountered transformations in our discussion of grids, where we applied a scaling transformation to all of our coordinates in our mathematical model of the world to render it in a larger size.

We can reason about such a transformation as a function, i.e.:

function scale(coordinate, scale) { return coordinate * scale; }

Or in mathematical notation,

(x, y, s) => (x * s, y * s)

But another way of reasoning about transformations is as a change in coordinate systems, i.e. when we scale by s, we're really using a coordinate system that is s times smaller (or larger) than the old coordinate system.

Thinking in Coordinate Systems

This idea, that a transformation is a change to our coordinate system, is especially useful in games. Consider a side-scroller, where we want the player to remain on-screen while moving through the world. We want to represent everything in the world by its position - what we call world coordinates. However, when we render the world, we want to know where everything in the world is relative to the screen, or screen coordinates.

Shifting between the two likely involves a translation, a linear value added to one or both coordinates. For example, if our character has walked 6,000 pixels into a horizontally-scrolling level, and our screen is 1024 pixels wide, we might transform all the object's x world coordinates by -6,488 (screenX = worldX - 6488) to obtain the screen coordinates (in this example, the y does not change). By applying this transformation to every coordinate in our rendering step, we can draw the game objects where they will appear on-screen.

Transformations and the Rendering Context

Applying transformations in this manner can get messy - we need to remember to apply the same transformations to all coordinates as we perform our rendering step. Thankfully, the JavaScript canvas gives us a way to apply this transformation automatically, by transforming our rendering context. Using the example above, a translation in the x axis of -6,448, we could apply the transformation in our rendering context with:

ctx.translate(-6448, 0); // Anything we now draw will be translated // by -6448 pixels along the x-axis.

The beauty of this approach is that we call our various drawing commands using the world coordinates - and the objects are then rendered using our transformed (screen) coordinates!

We can also save and restore the context state, allowing us to apply a transformation selectively, i.e.:; ctx.translate(-6448, 0); // objects drawn will be transformed ctx.restore(); // objects drawn will not be transformed

Finally, by making multiple transformation calls, we concatenate them together, applying them one after another, i.e.:; ctx.scale(2, 2); ctx.translate(100, 200); // render objects ctx.restore();

This last snippet scales the objects we will draw to be twice as big, and then moves them 100 pixels over and 200 pixels down on the screen.

Rotating about an object's center

One of the most common operations involving concatenating transformations is rotation. The rotation transformation in JavaScript ctx.rotate() rotates the coordinate space around the origin. But what we typically want to do is rotate an object about its own center of rotation! To do so, we simply need to translate the object so that its center of rotation lies at the origin, rotate, and then translate back, i.e.:

var gameObject = { image: new Image(), centerX: 56, centerY: 222, rotation: Math.PI / 4 } gameObject.render = function(ctx) {; ctx.translate(-image.width / 2, -image.height / 2); ctx.rotate(Math.PI / 4); ctx.translate(centerX, centerY); ctx.drawImage(image, 0, 0); ctx.restore(); }

Note that we use the save() and restore() methods of the context to return our rendering context to the state it was in when our render() method was invoked. This is good practice, as it helps prevent side effects from calling the render() function.