Stop the press! As seen in jsperf, the nightly build of Chrome (14) has optimized the width-setting case and now swings heavily the other way. As with all optimizations, be sure to bench often on the platforms and browser versions you are targeting.
I’m considering writing a small (e-)book on Canvas performance issues, considerations and tips. If you’d be interested in that sort of thing, let me know.
As much as we all like to see dramatic performance increases from clever optimizations, getting the best Canvas performance often means scrutinizing every little place in our code. Today we’ll take a look at how canvases are cleared.
A man’s careful search for his receding hairline
One of the ways that is implicitly endorsed in the spec and often used in people’s apps to clear a canvas is this:
canvas.width = canvas.width;
There is of course another common way to clear the canvas using a context method:
ctx.clearRect(0, 0, canvas.width, canvas.height);
They may seem to do the same thing on the surface, but the end difference between the two methods is large: Setting the canvas width to itself not only clears the canvas, it clears all canvas state. This means it clears the transformation matrix, the current clipping region, and all of the following attributes: strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation, font, textAlign, and textBaseline.
Much of the time clearing above doesn’t matter, except maybe the transformation matrix, because canvas programmers tend to set the relevant properties before they redraw each shape anyway.
The performance difference between the two above methods is also large, often an order of magnitude or more. The context’s clearRect method is much faster than setting the canvas width to itself. I have a jsperf page up here where you can see the results per browser.
Curiously, clearRect is faster on every browser except Safari on Windows, where it is the other way around. I can think of a few possible reasons why that might be the case, but I don’t want to speculate out loud. If someone on a Mac could test Safari for me, I’d be interested to know what it benches.
Back to clearRect. Not all is well all the time when using this method. After all, if your canvas context has anything but the identity transform, there’s a good chance you won’t be clearing the entire canvas. This leads some people to end up erroneously using the width-setting method. Additionally, many people want to clear the canvas but keep their transformation matrix the same. Both of these problems can be fixed in one go, so in the interest of completeness, lets see a safer way:
// I have lots of transforms right now ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); // Will always clear the right space ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.restore(); // Still have my old transforms
Not only does it clear the screen, but it ensures that any existing transformation won’t get in the way, and also allows you to keep that transformation should you need it. If you don’t need the transformation, you can of course remove the calls to save() and restore().
Because of the large performance discrepancy, I tentatively suggest the use of clearRect over setting the canvas width equal to itself, though the canvas width method is still useful for doing a complete reset of the context state. Of course, browser development happens rapidly and you should always test on the browsers and systems you are targeting.