Showing the Frame Rate on Canvas Animations

A while back I built a silly little performance-testing animation that I called Bouncer. It’s nice, but I noticed recently that it was missing a vital component: an indication of the frame rate. I added one this morning, and I thought I would share the code with you.

Here’s What I Did

Frame rate can vary wildly from browser to browser, especially when you consider mobile devices. You could just use your browser’s developer tools to check things, but it would be nice to have a clear number displayed on the test so we can watch as it changes. There are two bits of code you’ll need. First, you’ll need these variables:

// For getting the fps
var timeInterval = 0;
var lastTime = 0;
var frame = 0;
var avgFps = 0;

You need all of those to track the FPS. You need to see how long it takes to go from one frame to the next, and average it out to show a whole number. It will be clearer in a moment.

And here’s the text that we’ll write onto the canvas:

context.textAlign="right";
context.fillText("fps: " + getfps(), theCanvas.width - 10, 10);

Two things happen here. First, I set the text alignment to “right” because I want this in the top-right corner. Then I place the text. In this case, I just set the alignment to “left” at an earlier point so it isn’t still set to “right” on the next animation loop. I could have saved and restored the context stack, but I figured that would be two lines of code and the earlier left-alignment was one line. Normally you would want to save the stack, but in this case either method is fine.

The second thing to notice is the getfps() function. It returns the value we need, so we can put it into the statement just like a variable. Here’s what the function looks like:

function getfps() {
     frame ++;
     var date = new Date();
     var thisTime = date.getTime();
     timeInterval = 1000 / (thisTime - lastTime);
     lastTime = thisTime;

     if (frame % 10 == 0) {
          avgFps = Math.round(timeInterval * 10) / 10;
     }

     return avgFps.toFixed(0);
};

This is where the action is. First, we increment the frame. This isn’t required to get the frame rate, but it keeps things a bit more readable. Thanks to the if statement, the number is only updated every 10 frames. It does this by using the remainder operator to see if there’s a remainder. Without this check, the frame rate is constantly flashing and flipping out, and gives a headache to anyone looking at it. This way, you can adjust the updates per second to whatever’s comfortable.

The next few lines check the current clock time and compare it to the time from the last animation loop. The interval is converted into frames-per-second and stored in the timeInterval variable. This was originally two lines, but I combined them. Maybe not a good idea for a tutorial, but this is more of an experiment that I’m sharing than a teaching post. Anyway, the interval should really just be the difference between the last loop and this one, and then you convert the milliseconds to frames-per-second by dividing 1000 by your result. 1000 milliseconds to a second, divided by the number of milliseconds we have… you get the idea. It might help you to rename timeInterval to something like rawFps or something. I do suggest the single line, though. Less code is good, so long as it remains readable.

Just before we finish, we set lastTime to the current time. The next animation frame will compare its time to this, and so on, rinse, repeat.

When we set avgFps we want to ditch all the decimal stuff, so we average it out. I got the line from a book that I’m reading, so I’m not entirely sold on it. But it seems to work. Lastly, we use JavaScript’s toFixed() Method to show a nice whole number. Then we’re done.

I hope this helps. There are bunches of things I could do to improve the Bouncer app, like causing it to bounce when you hit the Enter key in the form inputs, but I don’t want to spend all my time on a little experiment like this. Besides, it served its purpose. It’s handy and I learned a couple of things. That’s what experiments are for.  🙂