I'm working on an animation library, and every once in a while I run a benchmark test to see how much of a gain or loss I get with certain features. Recently I've run into something that has me quite perplexed, perhaps someone with more knowledge can shine a light on this for me.

Performance Before:

  • Chrome: ~4460 sprites @ 30fps
  • Safari: ~2817 sprites @ 30fps
  • FireFox: ~1273 sprites @ 30fps
  • iPhone 4S: ~450 @ 30fps

Peformance Now:

  • Chrome: ~3000 sprites @ 30fps
  • Safari: ~2950 sprites @ 30fps
  • FireFox: ~1900 sprites @ 30fps (before Garbage Collection becomes too distracting)
  • iPhone 4S: ~635 @ 30fps

So you can see, Chrome took quite a hit in performance, while every other browser seems to have gotten a little better over this time frame. The biggest thing I notice, and what I'm figuring is the answer, is that the CPU usage seems to have been throttled back in Chrome (I swear before I could get up near 90%, now its maxing around 60%). The majority of the CPU is being used for the drawImage() call, and I'm not sure I can do anything to optimize that.

If its simply an issue where Chrome is now limiting my CPU usage, I'm fine with that.

Any insight would be greatly appreciated...

_s.Sprite.prototype.drawBasic = function() {
    var s = this.ctx;
    if(s.globalAlpha!=this._alpha) s.globalAlpha = this._alpha;

    var width = this.width;
    var height = this.height;
    var x = this._x;
    var y = this._y;

    if (_s.snapToPixel) {
        x = this._x + (this._x < 0 ? -1 : 0) | 0;
        y = this._y + (this._y < 0 ? -1 : 0) | 0;
        height = height + (height < 0 ? -1 : 0) | 0;

    var frame = this.sequence[this.frame] || 0;
    var sheetY = frame + (frame < 0 ? -1 : 0) | 0;
    var sheetX = (frame - sheetY) * this.spriteSheetX || 0;

    s.drawImage(this.bitmap.image, this.bitmap.frameRect.x2 * sheetX, this.bitmap.frameRect.y2 * sheetY, this.bitmap.frameRect.x2, this.bitmap.frameRect.y2, x - (width * this._scaleX) * this.anchorX, y - (height * this._scaleX) * this.anchorY, width * this._scaleX, height * this._scaleY);



So I downloaded an old version of Chrome (25.0.1364.5), and ran my benchmark test: Before

Then I reran in the most current version of Chrome: After

Clearly Chrome has changed. Was it on purpose? I don't know. You can see that in the old version of Chrome I've actually gained more performance over my original 4460 (+ ~400, my optimizations must have worked), but you can also see it lets me hover at 100% cpu usage. 2x cpu almost 2x object on the screen.

  • 4
    This will be difficult to answer without seeing the relevant code. – Matt Bryant Sep 10 '13 at 16:45
  • 1) if you are searching for performances, put the image inside a canvas for 33% boost. 2) screen coordinates will always be >=0, so no need to test for sign. If a coordinate is <0, then handle the clipping properly or do not draw if out of screen. 3) you might want to round the width, height provided in last arguments of drawImage to get some more speed. 4) you might want to cache into canvas a scaled version of your image to finally perform a 1:1 draw. – GameAlchemist Sep 11 '13 at 18:41
  • 1
    I guess people are missing what my question was about... I'm not trying to gain performance with this question, I have all sorts of tricks for that. What I wasn't aware of, was how Chrome changed its behavior to eventually cap itself at 30fps. I've only included code, because people keep asking for it. An example without any of my code... goodboydigital.com/pixijs/canvas/bunnymark if you run this in Chrome... you will see the point when it jumps from ~60fps straight to 30fps. This is not how Chrome use to behave. – ericjbasti Sep 11 '13 at 18:52
  • "I have all sorts of tricks for that". And yet, you are missing some important ones. – GameAlchemist Sep 11 '13 at 19:19
  • @GameAlchemist Ha, yeah your right. I only say that because, over the years, I've done a lot of these optimizations, and seen drastic differences from browser to browser, even between version numbers, like this issue. You'll see _s.snapToPixel actually rounds everything if that flag is on. In older versions of Safari that use to be a huge improvement, almost 2x, but now I see very minimal gain. I didn't mean to discredit your input. I've actually fallen into the trap of over optimizing to the point I finally decided to go for the obvious ones, and jsPerf the rest to find the best compromise. – ericjbasti Sep 11 '13 at 19:34


setInterval doesn't have the issue. Only happens with requestAnimationFrame. This finally makes so much sense. requestAnimationFrame already throttles things to 60fps, what I wasn't aware of, and can't seem to find any info on is that Chrome (others?) throttle it to 30 (60/2) and then 20 (60/3) and probably 15(60/4)... this keeps it in sync with 60hz, so you never end up with 40fps that looks strange because its out of sync with your screen refresh rate.

This explains a lot. I'm really enjoying the cpu savings this provides us.


An example without any of my code... http://www.goodboydigital.com/pixijs/canvas/bunnymark/ if you run this in Chrome... you will see the point when it jumps from ~60fps straight to 30fps. You can keep adding more bunnies, pixy can handle it... Chrome is throttling the fps. This is not how Chrome use to behave.

So I figured out whats going on here. It's not that performance has changed per say, I can still get 4800 objects on the screen at 30fps. What has changed seems to be the way Chrome tries to optimize the end users experience. It actually throttles things down from 60fps to ~30fps (29.9fps according to dev tools), which causes if(fps>=30) to return false:

    stage.onEnterFrame=function(fps){  // fps = the current system fps
        if(fps>=30){  // add astroids until we are less than 30fps
            stage.addChild(new Asteroid());

For some reason around 2800 objects, Chrome throttles down to 30fps instead of trying to go as fast as possible... So if I start the benchmark with 4800 objects, it stays at a wonderfully consistent 29.9fps.

fps meter

(you can see here that its either 60fps or 29.9fps no real in-between, the only thing that changes is how often it switches)

This is the code used for stage timing...

_s.Stage.prototype.updateFPS = function() {
    var then = this.ctx.then;
    var now = this.ctx.now = Date.now();
    var delta = now - then;
    this.ctx.then = now;
    this.ctx.frameRatio = 60 / (1000 / delta);

Hopefully this helps someone else down the road.

  • Dude, you shouldnt lock game actions & animations to fps but to time. locking to fps will give you variable speeds. not good! – Mike-O Sep 11 '13 at 16:45
  • @Mike-O... this is a benchmark... all it wants to do is add in as many asteroids as it can until it reaches 30fps. I've added in my timing code, so you will see the whole system is frame rate independent. However, you can still get an idea of FPS by looking at the delta between now and then... do some math and you have your FPS. – ericjbasti Sep 11 '13 at 18:04
  • the Chrome fps metter is -oddly enough- a performance killer, hence an fps killer. 60 to 30 just means one frame miss on a 60Hz display. You did not mention if you were using requestAnimationFrame or setInterval based rendering. – GameAlchemist Sep 11 '13 at 19:21
  • I use requestAnimationFrame which leads me to think Chrome is throttling things... ran it even longer... it seems to aim for consistency over performance. 60fps-30fps-20fps... – ericjbasti Sep 11 '13 at 19:25
  • Good call on the requestAnimationFrame call out... Changed it to a setInterval and the throttling went away. I'm not going to stop using requestAnimationFrame but its good to know. – ericjbasti Sep 11 '13 at 19:42

Your Answer

