RAFP: a proposal for performance measurements through requestAnimationFrame

I would like to propose a way of measuring the current performance of websites in real-world browsers with requestAnimationFrame. The really weird thing is that almost no one seems to have thought of this before.

The approach is very simple and a few tests show it appears to be worthwhile. I mean, it would be seriously cool if we find a reliable method to progressively enhance a site on-the-fly by turning off features if it turns out this specific browser is plagued by bad performance.

The basic idea is very simple: repeat requestAnimationFrame calls during one second, count how many times it’s called, and use the result to draw conclusions on the current performance of your website in your user’s browser. If the FPS (frames per second) rate starts to go down the browser is having more problems with executing your CSS or JavaScript, and it might be advisable to turn off, say, animations, or that one complicated DOM script that rewrites a table all the time.

I wrote a simple script that measures the current FPS rate, as well as the average and median FPS over a period. (I’m not yet sure if the average or the median is the better value.) Example usage:

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

This is an experimental approach. Though my initial tests show that it could be useful, my test pages are almost comically unperformant, since I wanted to test the idea in extreme circumstances. It needs to be thoroughly tested in real-world websites. (My main problem here is that I’m currently not working on a performance-heavy but well-written site that I could use as a test case.)

Prior art

When I started considering requestAnimationFrame for performance measurements I was certain that many people had already tried it. Since I knew that it isn’t being used a lot I assumed I’d find an article that patiently explains why this approach will never work.

Instead, I found nothing. Nobody appears to have written an article about using requestAnimationFrame for performance measurements. Although one Twitter follower reported he uses it, and has some findings to share, he hasn’t published yet. (I asked him to do so, and I’ll update this post when his article comes online.)

Other than that ... crickets.

Meanwhile I’ve become fairly certain that RAFP is theoretically sound on mobile at the very least. But will it also give us practical information we can actually use? Or are real-world differences in FPS too slight to give us the necessary information? I’m not sure yet, but I hope more tests will bring that to light.

Please help

That’s where you come in.

If you worry about your current site’s performance, or if you just want to help test this approach, add my script to a reasonably heavy site, read out the FPS average and/or median, and decide if they help you monitor live performance in a significant way.

If you find something interesting, or even if you find RAFP is no help to you, please publish an article instead of just sending a tweet. We need more data, and thorough discussions of that data.

Theory

requestAnimationFrame defines a callback function that is to be called when (in theory) the device is ready to refresh its screen. It’s meant for JavaScript animations: using requestAnimationFrame you can execute the next animation step at exactly the right moment, avoiding ugly flashing and other undesired side-effects.

I tested RAFP in three distinct set-ups; and run these tests on mobile to really see what we can learn from it:

A heavy CSS animation. This hardly has any effect on the FPS rate. Theory predicted this: browsers have been fierce in protecting the performance of their animations and transitions.

A bad scroll script that updates the position of a layer every time the user scrolls. Here it turns out that using the page has an effect on the FPS rate. Scrolling, obviously, brings the FPS rate down, since the browser is working very hard to run the bad onscroll script in addition to updating the screen.

It turns out that pinch-zooming affects the FPS rate as well: when you scroll at a higher zoom rate, the FPS rate is lower than when you scroll at 100% zoom. Page zooming doesn’t appear to have the same effect, but the fact that I can only test that on much faster desktop browsers likely plays a role.

Finally, giving the scrolling layer an opacity brings down the FPS rate even more. I think it is worthwhile to find out more about these use cases — provided we find that the entire RAFP-approach is valid.

What seems to happen here is that, although the actual device refresh rate doesn’t change, the browser’s does, which is reflected in the FPS rate. This is useful to know (and measure) from a performance point of view.

(How do I know it’s the browser’s refresh rate? I cannot be certain, but the fact that zooming affects the FPS rate is a powerful clue. There is no reason for zooming to affect the device’s refresh rate, but it will affect the browser’s, since ... well, I’m not yet sure why, although I have my theories. Something with many layers of content whose position should be recalculated on every scroll.)

Finally, a bad DOM script that adds rows to a table in the most unperformant way I could think of. Here it’s enough to just run the script and wait patiently for the FPS rate to drop.

There’s an interesting twist here. On Mac desktop, Firefox, Safari, and Edge/IE (with Parallels) hardly experience an FPS drop until hundreds of rows have been added, at which point the FPS drops sharply. Meanwhile, Chrome’s goes downward in a more linear pattern and reaches the end term (FPS below 20) much faster than the others.

I’m not sure if this is because Chrome implemented table DOM messing worse than the others, or that the other browsers pay more attention to the device’s refresh rate than Chrome does. Figuring this out is left as an exercise to the reader. (That’s science-speak for: I have no fucking clue.) The real question is: does RAFP also give correct performance metrics in this situation? Right now it seems it does, but I’m open to counter-arguments.

In any case, when you test on mobile the differences disappear: Safari, Firefox, IE, and Chrome all experience rapidly falling FPS rates. This means that RAFP is a valid approach on mobile, where it is most needed anyway.

Since, again, the device’s refresh rate is unlikely to be affected by adding rows to a table below the fold, I think that what we’re seeing here is the browser putting requestAnimationFrame on hold until its single thread has the time to handle it. This is pretty much the same as setTimeout, and it gives us useful information about the current performance of your website.

Test, test, test

That’s where I stand right now. Although my few test definitely appear to show that RAFP can become a powerful tool in our toolbox, I’ve only tested it in extreme circumstances, and real-world tests are called for.

Also, I suspect that the FPS measurements will be most useful in mobile browsers, who work with a slower CPU/GPU and less memory than desktop browsers.

Finally, we need to figure out if the average or the median is the better measurement. Try both!

Anyway, think about, it, study my script, and maybe try it in one of your sites. It would be seriously cool if we could just measure performance while the site runs and progressively enhance it on-the-fly if that turns out to be necessary.

This is the blog of Peter-Paul Koch, web developer, consultant, and trainer. You can also follow him on Twitter or Mastodon.
Atom RSS

If you like this blog, why not donate a little bit of money to help me pay my bills?

Categories: