I love web performance conferences because I learn so much from interacting with other practitioners in my field. Velocity and WebPerfdays are the big conference + unconference pair in our field and so much happens there.

At WebPerfDays London last year, while adding support for SPDY to boomerang, I learnt about Chrome’s alternate performance timing object… one that was different from Navigation Timing.

That’s when I noticed that Chrome also reports when the the first paint happens.

First Paint

In Chrome, first paint is reported via the object returned from window.chrome.loadTimes(). If you’re a performance geek like me, you’ve probably just opened up your WebDev console and inspected this object, and you’ve probably seen that it looks something like this:

{
	commitLoadTime: 1384152185.366253
	connectionInfo: "http/1"
	finishDocumentLoadTime: 1384152185.810402
	finishLoadTime: 1384152185.906618
	firstPaintAfterLoadTime: 1384152185.974076
	firstPaintTime: 1384152185.817258
	navigationType: "Other"
	npnNegotiatedProtocol: "unknown"
	requestTime: 1384152185.094807
	startLoadTime: 1384152185.220614
	wasAlternateProtocolAvailable: false
	wasFetchedViaSpdy: false
	wasNpnNegotiated: false
}

firstPaintTime is what I cared about at the time. Note that it’s reported in seconds since the epoch with microsecond resolution, so everything before the decimal point is seconds and everything after it is microseconds. This is different from other JavaScript timestamps which are in milliseconds, possibly with microsecond resolution if using the High Resolution Timer.

IE too

Chrome is not the only browser that reports first paint. IE also reports it as part of window.performance.msFirstPaint, although in IE’s case it’s reported in milliseconds.

Other modern browsers

And this brings us to the topic of this post and why I love Velocity.

At Velocity New York earlier this year, I was chatting with Seth Walker from Etsy. Etsy was our first customer but more importantly keep contributing ideas that make boomerang better. This was one such idea.

Seth’s new engineer, Daniel Espeset worked on a proxy for detecting first paint. We compared the numbers to what Chrome’s firstPaintTime reported, and they were within 2-3 milliseconds of each other, so the proxy was pretty good on Chrome at least.

Daniel’s trick was to attach a handler to requestAnimationFrame, and measure when it first fired. The logic was that requestAnimationFrame fires when the browser is ready to draw something to screen, and the first time that happens is when the browser can first draw stuff to screen, ie, on first paint.

The brilliance is in its simplicity.

I won’t post code here. I’ll let Daniel do that, or wait for his pull request to boomerang.

Older browsers

And this is where it gets even better. While chatting with Steve a little later in the conference, he mentioned another one of our customers who had a different proxy for first paint.

Flipkart.com is an online marketplace in India, much like what Amazon is in the US. They’d been using boomerang to measure load time long before lognormal was founded, and moved over to our platform soon after.

Flipkart’s idea was to measure when the last CSS file had finished loading. Since CSS blocks rendering, this was a pretty good lower limit of when rendering could start. It may not tell you that rendering has in fact started, but it’s pretty certain that nothing has rendered before this time.

The full list

So for the full list of first paint measures, I’d go through in this order:

  1. Attach a handler to requestAnimationFrame and remove it when it fires.
  2. Attach a handler to the onload event of all my CSS files (probably do this inline) (Edit: Andy Davies suggests all blocking resources in the HEAD instead of just CSS)
  3. Check for window.chrome.loadTimes().firstPaintTime or window.performance.msFirstPaint
  4. Use the earliest time from 1. and 3. or use the time from 2. if neither 1. or 3. are available.

See the Navigation Timing Plugin for what we have right now.

Do you have a better hack in place? Let us know in the comments.

Acknowledgements

I’d like to thank Daniel Espeset for his technique, Seth Walker for showing it to me and Steve Souders for letting me know about the CSS hack.

I’d also like to thank all of our customers. You’re all performance geeks just like we are, and a lot of the awesome sauce in our products come from ideas that you share with us. The above is just one example.

Update 2013-11-25

We’ve been doing a lot of testing on these techniques, and at the moment it looks like the requestAnimationFrame hack only works correctly on Chrome. All other browsers fire the first RAF within 40ms of the page starting up, even before HEAD has completed loading, and then repeatedly every 13-23ms.

This is likely a bug in all browsers since painting cannot start until HEAD has loaded. From the MDN docs:

The window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint.

I have no idea why this happens. Unfortunately, since Chrome already reports first paint time, this technique won’t be very useful until browsers fix their bug.

Strangely, Opera, which uses Chrome’s engine, also has the bug. On the other hand, since Opera uses Chrome’s engine, it also exposes window.chrome.loadTimes()