Tag Archives: performance

Increasing Performance by Caching Paths on HTML5 Canvas

I’m writing a book on HTML5, including Canvas! Click here for more information.

Much of the functionality of Canvas comes from its path drawing functions. Unfortunately for game designers and animators, re-drawing paths over and over can amount to a tangible performance hit. To increase performance, let’s take a look at caching paths as images to avoid redrawing them traditionally.


Paget holmes
Gentlemen waiting for a path to finish rendering

First we need to ensure that caching a path will actually lead to a performance increase. We can devise a simple test for this using JSPerf. We need a path to test, so let’s write something fairly simple.

ctx.beginPath();
ctx.strokeStyle = 'red';
ctx.lineWidth = 4;
ctx.moveTo(10,10);
ctx.lineTo(10,30);
ctx.lineTo(30,30);
ctx.lineTo(40,70);
ctx.quadraticCurveTo(72,43,22,12);
ctx.quadraticCurveTo(12,43,12,102);
ctx.stroke();

This does not produce a particularly complex or exciting path:



But it will do.

The thing we want to ponder here is whether redrawing this path, in other words executing every one of the instructions needed to make the path, will be a slower process than if we cached it. We can achieve caching by drawing the path from these instructions only once, to an in-memory canvas, and then using drawImage from our in-memory canvas onto our real canvas to redraw the path.

That isn’t the only way to cache. We could instead draw it to a canvas and then make a PNG out of it, and call drawImage from that PNG instead, but for the sake of making a simpler test we will stick with using an in-memory canvas.

So let us take all of the drawing instructions above and execute them on the in-memory canvas. Then in our draw loop, instead of drawing out the path every time, we simply draw the in-memory canvas to our real canvas:

// can2 is our in-memory canvas
ctx.drawImage(can2, 0, 0);

The test is simple enough. Giving it a go, the results are immediately clear: pre-rendering and using drawImage is more than ten times as fast as drawing the path, even for the relatively simple path used!



The more complex the path, the more time you will save with caching. If you’re using a lot of complex paths to render shapes, such an optimization ought to speed up your draw loop by a great deal. The JSPerf page shows a simple setup if you want to make the test for yourself.

Other considerations

Pre-rendering paths isn’t a magic bullet, there are still a lot of uses for drawing paths constantly in canvas. If you are making a live drawing application, or otherwise constructing dynamic paths and/or moving and animating paths on the fly, then any kind of pre-rendering is going to be nearly pointless or even harmful to performance. After all, what’s the use of drawing something to a separate canvas and drawing that state back to the original canvas if the path changes constantly? You’d need to re-draw the in-memory canvas just as often, so you’d lose all benefit.

It is also worth mentioning that you might want to play around with using PNGs instead of in-memory canvases. Another thing to test is putting multiple paths onto one in-memory canvas versus putting them all in their own separate canvases, effectively making a sprite sheet. From previous tests, it seems that there is a slight advantage to giving each sprite its own png instead of using a single-png (or single in-memory canvas) sprite-sheet, but it wasn’t that big of a difference.

If you do choose to use a sprite sheet, note that there are a lot of decent tools out there for compressing and organizing them, such as Sprite Sheet Packer.

How you clear your HTML5 Canvas matters

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.


Seco-06
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.

HTML5 Canvas fillText considered harmful

I’m writing a book on HTML5, including Canvas! Click here for more information.

I have in my possession sufficient evidence for the conviction of Canvas’ fillText().

The charge? Performance murder.


Reig-05
fillText() resisting arrest

I suspected fillText() of being slow ever since adding blocks of text as interactive objects to a Canvas library I am working on. I’ve also begun writing a canvas performance guidebook, so the sacrament of confirmation was doubly in order.

Since most of the slowness in canvas applications isn’t drawing something once, but redrawing it many times per second, I wondered if there were a faster way to redraw text than using the Canvas context’s fillText() method.

This is especially pertinent when drawing vertical text, since every new letter has to be on another line, and thus another call to the fillText() method.

I puzzled around with ways to avoid all the calls, and settled on trying something: Instead of calling fillText() to redraw my text objects each frame, I would instead create a new canvas (one never added to the DOM) for every single text object, and call fillText() on each object only once, drawing the text to its personal canvas.

Then, every time I wanted to (re)draw that text object, I would call drawImage() on my real canvas, passing in the object’s personal canvas, instead of using fillText(). This gives exactly the same per-pixel drawing, but is it faster?

I wrote a small, distilled test for the purpose, hypothesizing that it probably wasn’t worth doing for normal text but may be worth doing for vertical text. There’s a link at the end if you’re interested in trying it yourself.

There are a lot of scenarios though: What if I had just one text object that had to be redrawn 60 times a second? Surely – if drawImage() is faster than fillText() – having an additional canvas would be a small price to pay in overhead.

But what if I had 200 text objects? Maybe each text object having a canvas would cause it to be far slower.

I won’t bore you much longer, the definitive answer is this: If you are redrawing several text objects more than just a few times, it is far faster to give each object its own canvas and use drawImage() instead of fillText(). Not just for vertical text, but for any text, even if its just drawing a single character.

Some of the tests are described below in my poorly-made graphs. The first row of graphs are closer to real-use scenarios. Please note the Y-axis range (milliseconds) changes from graph to graph.

Six of the tests. Smaller bars are better.

As you can see, drawing a text object of just 1 character several hundred times is far faster in Chrome, Firefox and, IE9 when using drawImage(), and thus very much worth it to have that extra canvas around.

The second row of graphs were some fooling around to find the limit of this method’s utility. If you have 200 text objects (and thus 200 additional canvases) of just 1 character each, and render them only once, using drawImage() becomes a bit of a waste. But drawing those 200 objects merely three times and you can see that it will be faster in chrome to use drawImage() than to use fillText(). Firefox and IE9 are a little more resilient here, but they don’t take very long at all for drawImage() to become worth it.

So giving each text object its own Canvas is a pretty workable solution even if the objects are redrawn very few times. If you are trying to achieve 30 frames per second, giving each text object its own Canvas and using drawImage() is a no-brainer, and should probably a default consideration.

Of course Canvas applications can differ wildly, and you should draw up tests for your own situation before deciding how to draw text. Perhaps exceptionally large canvases or exceptionally small text yield different results, or perhaps memory on the targeted machine means that so making so many canvases are an untenable proposition, and so on.

Here’s a link to the test I made. I would of course appreciate any corroboration of results that anyone is willing to do.

EXTRA CREDIT:

You’d need to do more work to get this to work with scaling text, since you’d have to size the temporary canvases appropriately. It does however have the added benefit of fixing the scaling + rotating text bug that currently exists in Chrome and Opera.