# Can the performance of this canvas drawImage() test be improved?

I have a quite simple loop that draws an 8px by 8px image many times in a canvas in a tiling fashion. Currently it's drawing 7500 images each loop.

See the jsfiddle.

var img = new Image();
img.src = 'http://i.imgur.com/3dzaMlv.png';

var W = 8;
var H = 8;
var R = 800/W;
var C = 600/H

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var fps = document.getElementById('fps');
var timePrev = new Date().getTime();
var fpsInterval = 30;
var i = 0;

window.setInterval(function() {

ctx.clearRect(0, 0, 800, 600);

for (var r=0 ; r<R ; r++) {
for (var c=0 ; c<C ; c++) {
ctx.drawImage(img, r*W, c*H);
}
}

// fps
if (i % fpsInterval == 0) {
var timeNow = new Date().getTime();
var spf = (timeNow - timePrev) / fpsInterval / 1000;
fps.innerHTML = (1/spf).toFixed(2);
timePrev = timeNow;
}
i++;

}, 1000/60);


On my I7-2700K (3.5GHz) processor and ATI Radeon HD7970, I am getting the following framerates:

• Chrome 36.0.1985.143 m : ~40 FPS
• Firefox 30.0 : ~55 FPS
• IE 11.0.9600.17239 : ~35 FPS

Is there any way to improve FPS performance here, assuming I do actually need to draw this many tiles on each update?

To clarify, I'm requiring that they be drawn as individual tiles on each update. So, rendering them all to an off-screen canvas wouldn't work. I am essentially disappointed in the number of drawImage() calls that seems to perform well.

Edit: Only now did I notice your comment that you require the tiles to be drawn individually. Since I don't know exactly why that is (I can only review the code I can see), I can't offer you any other advice than what I've already written below.
If you can redraw larger rects using a tiling fill style, or only update some tiles per frame, it'll be an improvement over redrawing everything each frame. But if your exact requirements are "redraw every tile by itself on every frame" then your current code is as probably as good as it gets. But the end result is the same as the much more efficient methods outlined below.

Edit edit: Don't know why I didn't think of it sooner, but off-screen drawing could be an improvement. The link provides a nice simple implementation.

You can draw the background using a single rect with a tiling background, and not do any drawImage calls. The technique is outlined in this StackOverflow answer. In its simplest form:

ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fillRect(0, 0, canvas.width, canvas.height);


Here's a demo - it runs at the target 60fps (in Chrome at least).

Alternatively, you could just set the image as a repeating CSS background image, and not do any JS at all. Just:

canvas { background: url(...) repeat; }


And done. Though I won't guarantee how that'll work with whatever you intend to draw on top of it.

In terms of code review, I noticed a couple of things

• Single-letter variables - always better to use full words (the exception being known conventions like the i variable in a loop)

• Speaking of, you can also avoid the hard-coded values for R, C, W and H "constants". The canvas' width/height you can get as simply canvas.width and canvas.height. Similarly (once the image has been loaded; see below), you can get its dimensions the same way: image.width and image.height.

• You can use Date.now() instead of new Date().getTime()

• You don't actually wait for the image to be loaded. It's a tiny image, but the network may be slow. So (as shown above), you should wait for the load event to fire on the image object, before trying to use it:

var image = new Image();
image.src = "...";

In general, though, don't expect exceptional performance from canvas. It's good, but it's not compiled code being mainlined to the GPUs in parallel. You're writing in JS - an interpreted language with a single-threaded runtime - doing compositing on top of an already complex view (the web page). Your many calls to drawImage each require the computer to do a lot of extra work - your loop is not a single operation; it's many individual operations and - depending on how smart the browser is, how much hardware acceleration there is, etc. - it takes time to update the screen after each draw.
• @TonyR Yeah, probably, but don't hold your breath. I mean, performance will automatically improve with hardware speeds (of course), but canvas is also a "young" addition to browsers compared to so much else, so I'm sure people will find more optimizations. Regardless, drawImage will always be a very high-level call; the tip of a very large iceberg of tech. You are about as far away from setting the actual pixels as you can be. – Flambino Sep 3 '14 at 18:54