Question

I'm working on an HTML5 game that uses the canvas element heavily. There's a continuous event loop that updates the main canvas element and then periodically a major change needs to be made to it. Basically, as the character moves around, the background is updated. There's a noticeable pause when this happens, so I've tried to perform the operation with an asynchronous function, but I seem to be taking exactly the same performance hit. I've tried performing all the operations to an invisible canvas in the asynchronous function, then copying that to the main canvas later in the event loop when they're complete, but, again, no improvement in performance.

I've written this nonsensical little program that seems to duplicate the behavior I'm talking about. It seems as though tieing up the canvas element, even in an asynchronous function, affects the rest of the program.

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var ImDat=ctx.createImageData(1000,1000);
var ticker = 0;

ImDat.setPixel=function(x,y,c){
   var i=(x+y*this.width)*4;
   this.data[i]=c.R;
   this.data[i+1]=c.G;
   this.data[i+2]=c.B;
   this.data[i+3]=c.A;
}

var bigOperation = function() {
   for (var i = 0; i < 20; i++) {
      lilOperation();
   }
   console.log(new Date + 'big op done');
}

var lilOperation = function() {
   for (var i = 0; i < canvas.width; i++) {
      for (var j = 0; j < canvas.height; j++) {
         ImDat.setPixel(i, j, { R: Math.random() * 256, G: Math.random() * 256, B: Math.random() * 256, A: 256 });
      }
   }
   ctx.putImageData(ImDat, 0, 0);
}

var eventLoop = function() {
   if (ticker == 50) {
      ticker = 0;
      console.log(new Date + 'big op start'); 
      window.setTimeout(bigOperation, 1);                     
   } else {
      ticker++;
   }
      lilOperation();
      console.log(new Date + 'normal loop');  
}

window.setInterval(eventLoop, 10);

So, you'll notice consistent time intervals being logged until the bigOperation function is called, at which point the event loop pauses for a moment. Note that I'm working on this on a pretty crappy system so you may have to increase the number of iterations in bigOperation to get the effect.

Thanks!

Was it helpful?

Solution

Even if you use setTimeout the execution will lock everything until the function call is completed, it's just the nature of Javascript. This is actually more a blessing than a curse because it simplifies things considerably.

Because of the single-threaded nature of Javascript, something called "Web Workers" was invented. This is a relatively new feature in browsers, but it allows you to perform tasks that are run separately without locking the main thread. In moderna browsers, you can perform canvas tasks using web workers.

Now, without knowing anything about your game, it sounds like what you want to achieve (updating background graphics, moving characters etc.) should be possible without needing to call heavy functions that take a really long time to execute. Are you performing a lot of per pixel operations? If so, can't they be done by other means? I would actually start looking at optimizing your code before looking into using web workers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top