RAFP (requestAnimationFrame-Performance)

Here is my script that uses requestAnimationFrame for performance measurement, RAFP, as well as a few test pages.

This is an unproven approach. I think it’s valid, but many people need to run many tests in order to determine whether it truly returns useful data. Also, I don’t yet know if the average or median FPS is the better performance measurement.

See also this blog post.

Idea

Run a requestAnimationFrame loop and increase a counter by one for every frame. Wait until a second has passed, and the counter gives you the current frames per second rate, which could be a measure for how well (or badly) your page is performing. Simplified:

function cycle() {
	var currentTime = new Date().getTime();
	if (currentTime - startTime < 1000) {
		currentFPS += 1;
		requestAnimationFrame(cycle);
	} else {
		// store currentFPS and set up for next cycle
	}
}

Below you find the script that does so and that exposes a mini-API for your convenience.

I suspect that this approach is mostly useful for mobile. Although desktop browsers’ FPS drop a bit in the bad test pages, the mobile browsers’ FPS drop a lot more, and they will run into annoying performance issues more quickly.

Meanwhile, here are a few test pages. Note that two of these three pages feature comically badly written, extremely unperformant scripts. I wanted to test RAFP in seriously adverse conditions in order to figure out if the approach works at all. Tests in more realistic condition would be useful.

  1. Animation test. This page contains a copy of an animation of the solar system. (If you created this animation, please get in touch; I lost my reference to the page I found it on.)
    Used to test how much performance drops due to heavy animations. (Preliminary conclusion: not a lot.)
  2. Scroll test. The very bad script here adjusts the position of an absolute layer every time the user scrolls. This affects performance a bit, especially when you zoom in. Also, removing the opacity from the absolute layer seems to help performance.
    This is the test I’m most excited about because, if the RAFP approach is validated, it would yield the most interesting results once we start messing with visibility, opacity, and more such CSS declarations.
  3. DOM test. Comically bad script that adds rows to a table in the least performant way I could think of. This affects performance the more rows you add.
    The script stops automatically if the current FPS drops below 20, and it’s interesting to study how many rows browsers add before that happens.

Script and usage

Here is the script. Add it to your page, then initialise it as follows:

var RAFPdetect = rafp();
RAFPdetect.start();
// later
if (RAFPdetect.getAverageFPS() < 35) {
	// performance is taking a hit
	// better disable something to get back on track
}

Now the script runs and you can use the API to get useful information.

No support

A few older browsers don’t support requestAnimationFrame. You can detect that yourself or use the supported property which is false if requestAnimationFrame is not supported. Also, when it’s not supported the three main get() methods return "not supported".

var RAFPdetect = rafp();
RAFPdetect.start(); // calling start() is always safe
// later
if (RAFPdetect.supported && RAFPdetect.getAverageFPS() < 35) {
	// performance is taking a hit
	// better disable something to get back on track
}

API

The script exposes the following methods:

RAFPdetect.start()
Start the script. Necessary in order to do anything else.
RAFPdetect.end()
Stop the script. The data remains available, but no more measurements will be added. You can restart if you wish.
RAFPdetect.supported
Property. Is false if requestAnimationFrame is not supported; true if it is.
RAFPdetect.setPeriod(30)
Set the period over which average and median are calculated, in seconds. The default is 60.
RAFPdetect.getCurrentFPS()
Get the most recent FPS reading. I’m not entirely sure this is the best value; brief actions, such as scrolling, may temporarily lower the FPS, but only for a single cycle. It’s likely better to use the average or median.
Returns "not supported" if the browser does not support requestAnimationFrame.
RAFPdetect.getAverageFPS()
Get the average FPS over the last 60 measurements, representing one minute of data. You can adjust this period with setPeriod().
Returns "not supported" if the browser does not support requestAnimationFrame.
RAFPdetect.getMedianFPS()
Get the median FPS over the last 60 measurements, representing one minute of data. You can adjust this period with setPeriod().
Returns "not supported" if the browser does not support requestAnimationFrame.
RAFPdetect.addNote('note')
Add a note to the current FPS cycle. This makes your data dump more useful.
RAFPdetect.getFullDump()
Returns a data dump of all detections ever made.
Returns an array (start to end) containing objects that each have an fps and a notes property, which contains the measured FPS and the notes you added. The DOM test contains an example of a full dump.