Canvas slowly degrades in performance after a while

I was creating a simple game and was shocked to see how the performance started to degrade. It never happened to me before, most probably because I use images and not the context drawings. So I used this function and the whole game becomes so slow, it just cannot be played after 30 seconds.

Office.prototype.draw = function(){
for(var i = 0; i < this.tables.length; i++){
for(var j = 0; j < this.tables[i].length; j++){
Canvas2D.ctx.rect((373 * j) + 75, (370 * i) + 250, 170, 20);
Canvas2D.ctx.fillStyle = 'green';
Canvas2D.ctx.fillRect((373 * j) + 75, (370 * i) + 250,
this.moneyBar[(i*4)+j], 20);

// Draw the experience bar.
Canvas2D.ctx.rect((373 * j) + 75, (370 * i) + 270, 170, 20);
Canvas2D.ctx.fillStyle = 'yellow';
Canvas2D.ctx.fillRect((373 * j) + 75, (370 * i) + 270,
this.expBar[(i*4)+j], 20);

// Draw the tables.
Canvas2D.drawImage(sprites.items[0], new Vector2(
(250 * j) + 50, (250 * i) + 200));
}
}
}

So as you can tell from the code, there is the multidimensional array table which has bars on top displaying moneyBar and expBar. If I am to use Canvas2D.ctx.stroke() inside the for loop, the game performance drops right away. Outside of the loop, it keeps up a bit, then it degrades. If I don't use stroke at all, works better, but degrades anyway. So I am really confused as to why this degrades so fast. Also am using requestAnimationFrame and have used the same engine in previous games and performance never degraded.

If I was to remove fillStyle, keeps up 3-4 seconds more. There are 8 tables in all, which means the for loops run 8 times per draw.

Vector2 function.

function Vector2(x, y) {
this.x = typeof x !== 'undefined' ? x : 0;
this.y = typeof y !== 'undefined' ? y : 0;
}

• please factorise your code !! ( var ctx=Canvas2D.ctx). The slowdown might be related to the garbage collector : try to create only one Vector2 and reuse it in your for loops. – GameAlchemist Apr 21 '16 at 5:25
• Tried, did not work. Vector2 works super fast, no problem with it. – GSquadron Apr 21 '16 at 8:43
• How many tables do you have and does the 'moneyBar' and 'expBar' functions create some temporary objects? It sounds like a GC issue. – luben Apr 21 '16 at 9:21
• Never mind, this was solved, but there were 8 tables, 8 moneyBar and 8 expBar. this.moneyBar and this.expBar are arrays, not functions. – GSquadron Apr 21 '16 at 10:47

You are using rect to draw the background of the bars, but you forgot the beginPath. So the rect calls pile up and make the drawings slower and slower each frame.

To explain a bit further, each non-direct draw command (arc, rect, lineTo, XXXTo) is used to build the current path. If you never use beginPath to reset the current path, the next frame will just resume building the path with the new commands : path gets bigger and bigger, and slower and slower to draw.

You can use beginPath then rect then fill, or rather use fillRect, which is faster.

• When I completely remove the rect, performance increases, but it just starts slowing a bit later on. I will try beginPath for fillRect to check if that is the real problem. – GSquadron Apr 21 '16 at 10:34
• Update: Looks like this was it. Now it runs much faster and no degradation. Thank you for your answer, I am so happy now! :D Can you elaborate a bit more why this degrades the performance? I never used it before. Also note that this should be used with fillRect too as it also degrades the performance. – GSquadron Apr 21 '16 at 10:43
• Glad you could get your frame-rate back ! :-) ( I explained a bit more. ) – GameAlchemist Apr 21 '16 at 11:05

Since i can't comment to get more information i'll do my best here.

Have you tried clearing the canvas before each draw ?

You could also change your code to due batch drawing instead of consecutive drawing calls as you are currently making;

Another thing that may degrade the performance the fill style changes in your loop. Try to make all drawing calls for each fill style instead of change fill style in each iteration like this:

Canvas2D.ctx.fillStyle = 'green';
for(var j = 0; j < this.tables[i].length; j++){
Canvas2D.ctx.rect((373 * j) + 75, (370 * i) + 250, 170, 20);
Canvas2D.ctx.fillRect((373 * j) + 75, (370 * i) + 250,
this.moneyBar[(i*4)+j], 20);
}

Canvas2D.ctx.fillStyle = 'yellow';
for(var j = 0; j < this.tables[i].length; j++){
// Draw the experience bar.
Canvas2D.ctx.rect((373 * j) + 75, (370 * i) + 270, 170, 20);
Canvas2D.ctx.fillRect((373 * j) + 75, (370 * i) + 270,
this.expBar[(i*4)+j], 20);
}

for(var j = 0; j < this.tables[i].length; j++){
// Draw the tables.
Canvas2D.drawImage(sprites.items[0], new Vector2(
(250 * j) + 50, (250 * i) + 200));
}

• Very nice idea. In fact, I did your version, it works somewhat better, but there is speed degradation again. I also changed the for loops with while, still somewhat better but performance slows after a while. – GSquadron Apr 21 '16 at 10:33