Huge performance difference when using drawImage with IMG vs CANVAS

I put together a couple of simple tests that render an image to a canvas. One renders from an IMG, while the other renders from an offscreen CANVAS. You can see the code and results here: http://jsperf.com/canvas-rendering/2

In most browsers rendering from an image is much faster than rendering from a canvas, except in Chrome, where the situation is reversed. Can anyone explain the reason for the differences? After all, we are rendering the same pixel data to the same destination.

• I'm not really sure this is a question, or at least one we could answer. Aside from that though, looking at your test it looks that just the others are really slow at rendering canvas objects, rather than Chrome being out of the ordinary for rendering images slower. – Matt Kemp Jul 12 '12 at 22:25
• But why is there a difference at all when in both cases they are rendering the same data? And the fact that at least one major browser has the opposite performance characteristic means we need to implement two code paths in our renderers. – alekop Jul 12 '12 at 23:23
• Could you add to the test rendering without either buffercanvas and img tag? Would be interesting to see. – justanotherhobbyist Jul 13 '12 at 0:01
• @hustlerinc: You mean render from a canvas onto itself? What would that prove? All the game's graphics are loaded from images, so you have to use an image at some point in the process. – alekop Jul 13 '12 at 21:42
• @alekop No, I mean skipping the offscreen canvas and just using one canvas. I think in web it should make rendering faster, but don't have proof for it. And too lazy/inexperienced to do the test myself. – justanotherhobbyist Jul 13 '12 at 21:45

OK, I figured it out. Almost. It's actually quite obvious, and I feel a bit dumb for not noticing this right away. When you call drawImage(src, 0, 0) without specifying width/height it draws the entire src region, which in this case is much larger (the canvas is 320x420 versus the img at 185x70). So in the canvas case the browser is doing much more work, which explains the slower performance. I' still puzzled by Chrome's higher score with the larger src.

dest.drawImage(src, x, y) // bad
dest.drawImage(src, x, y, w, h, destX, destY, w, h) // good


I've posted an updated version which uses the same regions, and the differences are much closer. http://jsperf.com/canvas-rendering/5

I still can't explain why there is a difference, but it's now small enough that I don't really care.

• On Chrome 43 with Windows 8 and Intel Graphics HD it crashes when the first test runs. When the test finishes drawImage(img) is the clear winner, drawImage(canvas) being 94% slower. – Șerban May 28 '15 at 8:08
• Firefox 38 runs smoothly, no problems at all, and the both tests are close. – Șerban May 28 '15 at 8:24
• If you scale your images it hurts performance as well @alekop (developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/…) – Jersh Jun 24 '17 at 16:05

Chrome is likely to use hardware acceleration.

Create a canvas 240x240 and run your experiment in Chrome then create a canvas 300x300 and do it again. The larger canvas I expect to be faster due to the fact hardware acceleration kicks in after 256x256 and chrome uses software when the sizes are less.

Also it worth pointing out that -webkit-transform:translateZ(0) turns off hardware acceleration.

I haven't tested any of the above; I only know this due to the fact one of the chrome engineers commented on a bug I reported in chrome when you cross the hardware and software threshold by dynamically resizing the canvas from larger to smaller than the 256x256 boundary or vice-versa. The solution to this bug was to turn off acceleration using the translateZ as mentioned above.

In my case, I simply did not allow users to resize less than 256x256.

• Turns off hardware acceleration? Doesn't it turn it on? – gilbert-v Sep 25 at 14:42

Sometimes images might be loaded to GPU memory and canvas in host memory. In that case when you draw from image to canvas the image data has to be copied first to host memory and then to canvas.

I noticed that kind of behaviour with Chrome, when I was writing project which loads over 100M pixel images and then reads parts of them to small 256x256 canvas ( http://elhigu.github.io/canvas-image-tiles/).

In that project if I drew directly from image tag to canvas in Chrome, memory always jumped to ~1.5GB when drawing started and then when drawing ended memory was freed again, even that 250 megapixel source image was shown all the time in the page.

I fixed the problem by writing image once to big canvas (same size with image) and then drawing smaller canvas from there (I also threw image away after converting it to canvas).

Can't explain the differences, but I'd disagree with

And the fact that at least one major browser has the opposite performance characteristic means we need to implement two code paths in our renderers. - alekop

If you look at the results on the js.pref the differences in chrome are fairly subtle. I'd stick with just rendering from an image when possible.

• The trouble is that I'm relying on offscreen canvases to compose complex images. For example, I'm rendering a character's animation frames into an offscreen buffer, and then rendering things like clothing/armor/weapons on top. The game then renders from the composite canvas, rather that re-rendering all these details for each character, each frame. With canvas-to-canvas performance being so poor in non-Chrome browsers, I'd have to render the composite back into an image. It's not the end of the world, but I was hoping there is a workaround. – alekop Jul 13 '12 at 21:31

The image's size is 185*70 but we create a canvas with size, I think this will waste some performance, so I set the offscreen canvas's size same as the image. And the difference is closer.

var g_offscreenCanvas = createCanvas(185, 70);


http://jsperf.com/canvas-rendering/60