User Timing API - Measuring User Experience Performance
Page load time, start render time and speed index are all used to quantify a web application's performance, but these metrics provide a very limited perspective of the user experience. Such numbers only tell you when everything on a page is finished loading; they don't take into account which content is most important to the user.
Another way to measure an app's performance is to pick out the most important design elements on each page and record timestamps to determine how long users are waiting to see the content that matters most. This guide will explain how to create your own performance metrics with the User Timing API.
What is the User Timing API?
performance.mark() records a timestamp in microseconds from navigationStart to a designated point in your performance timeline.
performance.measure() evaluates the difference between two marks. Most performance testing services can automatically extract and report User Timing data. Once you know how to properly use these functions, designing your own metrics is relatively easy.
How to make marks and measurements with the User Timing API
Establishing a mark is as easy as giving it a name. Let's use the
performance.mark() method to create two marks called
Placing these snippets in your code will record a timestamp when the mark is executed. To measure the time difference between these marks, we'll use the
measure() method, which requires three values:
performance.measure("difference", "start", "end");
The difference between the values of
end will now be recorded in an object called
How to remove performance marks
clearMeasures() methods are used to erase performance marks and measurements. Pass the names of the values to be deleted like this:
The above snippet deletes the mark named
start. To delete all of your marks or measurements, simply run the methods without passing a name:
Manually retrieving marks and measurements
As mentioned earlier, many performance monitoring services will extract any custom metrics you make with the User Timing API and display them on your dashboard. You may also be able to give your marks and measures more personalized names to keep better track of them. Nonetheless, you should still understand how to retrieve User Timing data manually.
To get a manual read out of the calculations, you can use the
getEntriesByType() method. First, you must pass the names and values of your marks and measures as an array to variables like so:
var marks = performance.getEntriesByType("mark"); var measuring differences = performance.getEntriesByType("measure");
Next, to extract the data, use the following setup:
console.log(mark.name + ": " + mark.startTime); console.log(mark.name + ": " + mark.startTime); console.log(measuring differences.name + ": " + measuring differences.duration);
name feature ensures that the name of each mark and measurement gets displayed beside its value in the browser console. The
startTime property returns the timestamps recorded by your marks. Likewise,
duration is used to display calculations produced by the
User Timing vs Navigation Timing
The User Timing API works similarly to the Navigation Timing API with one notable difference: The former is much more precise than the latter. While Navigational Timing returns simple timestamps recorded in milliseconds, User Timing gives you a decimal point value down to the microsecond, or one-thousandth of a millisecond.
User Timing API browser compatibility
All major browsers now support the User Timing API with the exception of Opera Mini. So if you want to start using the User Timing API rest assured that it's widely supported.
Common custom metrics
Only you can decide which page content is most relevant to your users, but here are a few metrics, thanks to Speedcurve, that are useful in most situations.
1. Style sheets done blocking
A page can't render until its style sheets are finished loading, so knowing when your style sheets are done blocking can help you better diagnose performance issues. To track style sheet blocking, place the following code below your style sheet link tags in your HTML document:
<link rel="stylesheet" href="/mystyles1.css"> <link rel="stylesheet" href="/mystyles2.css"> <script> performance.mark("style sheets done blocking"); </script>
The above snippet should not execute until all style sheets are successfully downloaded and parsed. You could use the Resource Timing API to track style sheet load time, but that doesn't account for any other style sheets that your main style sheet may import. Therefore, User Timing gives you the most accurate impression of when blocking is complete.
2. Hero images
A hero image is the banner graphic featured at the top of a webpage. It's the first thing that users' eyes are drawn to, and it lets them know they are in the right place. Therefore, knowing how long it takes for your hero image to load is useful information.
You can create a hero image metric like so:
<img src="yourheroimage.jpg" onload="performance.clearMarks('img displayed'); performance.mark('img displayed');"> <script> performance.clearMarks("img displayed"); performance.mark("img displayed"); </script>
It's good practice to clear any previous marks of the same name before setting new ones, which is why the
performance.clearMarks() method is included in the example above. The snippet provides an accurate measure of how quickly your hero image loads by comparing the image onload mark and the inline script mark. Image onload triggers when the image finishes downloading, and the inline script mark tells you when blocking is complete, so you need both pieces of information to know when users can actually see the image.
You can use this same snippet for your other images too. Since images are one of the most important elements of most websites, this custom metric is especially useful.
3. Scripts done blocking
Most performance issues are related to either your style sheets or your synchronous scripts. If page rendering doesn't begin right after your style sheets are done blocking, then the problem probably lies in your scripts. Place the following code below your synchronous scripts to pinpoint when blocking is complete:
<script src="a.js"></script> <script src="b.js" async></script> <script> performance.mark("scripts done blocking"); </script>
The above example ensures that
performance.mark() records immediately after the script
a.js is downloaded, parsed and executed. However, since
b.js has the async attribute attached to it, that mark will be recorded before the script is finished loading. Scripts that are set up to load asynchronously do not block rendering, so you don't need to worry about them as much.
Putting the aforementioned code in its right place can be a challenge because scripts tend to be sprinkled throughout the page. In such cases, the mark measurement might not trigger until after some DOM elements have already begun rendering, so the results would be inaccurate. Consequently, this trick works best when all synchronous scripts are located in the head of your document.
4. Fonts loaded
If a website includes custom fonts, some browsers will not render text until the font file is finished downloading. Thus, creating a fonts loaded metric helps you determine when users can see your text elements.
Since there is no
onload event for fonts, you'll have to rely on other means to determine when they are finished loading. For instance, if your website uses Google Fonts, you can track their status with Web Font Loader, and then add a call to performance.mark to establish a fonts loaded custom metric.
5. Text displayed
For text elements that don't use custom fonts, you can record render time by including an inline script after the text element like this:
<p>This is a text test.</p> <script> performance.mark("text displayed"); </script>
Recording render time for text that uses custom fonts is slightly more challenging since you'll have to compare the inline script's mark and the font loading mark. The higher of the two values indicates how quickly users see the text. Using a pattern similar to the hero image metric, include a text displayed mark in the font loading snippet as well as in your inline script following the text element.
Consult the official User Timing specification to see more suggestions for relevant timing events that you might want to track.
Analyzing performance marks and measurements
The many marks and measurements you make are only useful if they help improve your application. This should go without saying, but when you're designing custom metrics with the User Timing API, think about how you will put the data to use.
For example, once your custom metrics are in place, a waterfall chart can provide insight into how all of the elements on your webpage are loading. With waterfall charts, you can see the order in which your assets are rendered, know exactly how long each step takes and easily pinpoint any blocking issues or FOIT problems.
More tips for implementing the User Timing API
To get the most out of your custom metrics, follow these steps as you set up performance monitoring.
- Identify the most critical elements on each page of your application by asking yourself the following questions:
- How does the user interact with the page?
- How does the user know when the page is finished loading?
- Is there a hero image?
- Place your marks and measurements in the appropriate parts of your HTML document.
- Send the data to your performance monitoring tool of choice. Consult the Google developers guide to learn more about setting up real user monitoring in Google Analytics.
Once you perfect the practice of creating custom metrics, you can get a more accurate picture of what your users are experiencing. However, even if you have the best performance monitoring software money can buy, you still must be able to identify which metrics are most relevant to your users. Since you know your website best, only you can figure that part out.