Growing with the Web

Fast and Simple JavaScript FPS Counter

Published
Tags:

A side project called for a JavaScript FPS counter, here’s how I did it.

I know Chrome has an FPS counter but it only counts something a frame when something actually changes, so on a page that isn’t doing anything the FPS would drop to less than 60.

Before implementing my FPS counter I looked at this question on Stack Overflow where this was the most up voted answer:

var lastCalledTime;
var fps;

function requestAnimFrame() {

  if(!lastCalledTime) {
     lastCalledTime = Date.now();
     fps = 0;
     return;
  }
  delta = (Date.now() - lastCalledTime)/1000;
  lastCalledTime = Date.now();
  fps = 1/delta;
}

While definitely fast, this solution suffers by having the number be very unstable and jumping between 59/60 pretty much every frame, making it very difficult to read. It also only measures over the last frame, meaning if there was a very long frame that took 500ms in between frames of 1ms, the FPS counter would change to read “2 FPS” for only a single frame (about 16ms) before shooting back up to “60 FPS”.

A nicer solution turns out to be just as simple:

const times = [];
let fps;

function refreshLoop() {
  window.requestAnimationFrame(() => {
    const now = performance.now();
    while (times.length > 0 && times[0] <= now - 1000) {
      times.shift();
    }
    times.push(now);
    fps = times.length;
    refreshLoop();
  });
}

refreshLoop();

It works by using window.requestAnimationFrame to fire the function refreshLoop every animation frame. The refreshLoop function adds the current timestamp value to the queue times and removes any timestamps that occurred more than a second ago from the front of the queue. Since performance.now() always increases in value, the queue times should always be sorted. The FPS is then calculated by counting the size of the queue.

This met my needs perfectly but it could be improved in a few ways:

  • By only updating the FPS display every 500ms
  • Use a circular list for the queue if GC’s caused by the queue are a concern

Another option is to go with an existing project like stats.js which has some additional features like a nice display and memory tracking.

Like this article?
Subscribe for more!