Web Performance Metrics

Reading time: 15minViews:
Web performance metrics help improve user experience by measuring how users perceive website performance. Key metrics include perceived load speed, load responsiveness, runtime responsiveness, visual stability, and smoothness. Tools like Google’s Web Vitals focus on critical metrics such as First Contentful Paint (FCP), Largest Contentful Paint (LCP), Interaction to Next Paint (INP), Total Blocking Time (TBT), Cumulative Layout Shift (CLS), and Time to First Byte (TTFB). Custom metrics can also be measured using APIs like User Timing and Performance Observer. These metrics assist in evaluating and enhancing website performance comprehensively.

Performance metrics for websites are essential for comprehending and enhancing the user experience in a manner that truly benefits real users. Typecially, there are several types of metrics that are relevant to how users perceive performance:

  • Perceived load speed: This refers to the rapidity with which a page loads and displays all visual elements on the screen.

  • Load responsiveness: This describes the speed at which a page loads and executes necessary JavaScript code, ensuring components respond swiftly to user actions.

  • Runtime responsiveness: This metric measures how quickly a page reacts to user interactions after it has fully loaded.

  • Visual stability: This assesses whether elements on the page move unexpectedly, potentially disrupting user interactions.

  • Smoothness: This evaluates whether transitions and animations render at a consistent frame rate and smoothly transition from one state to another.

Given these five dimensions, there is a general understanding of the performance characteristics of most websites. To directly capture and analyze these datasets, we can use a tool called Web Vitals, a Google initiative that aims to provide consistent guidance on web quality through metrics critical for delivering a great user experience.

Metrics in Web vitals

Below, I list all the metrics contained in web vitals. To provide a more sensible understanding of each metric, I will also include some related solutions for each one.

  • First Contentful Paint (FCP): FCP is a crucial web performance metric that gives insights into the user experience by measuring how quickly content starts appearing on the screen during the page load. Using a CDN, implementing server-side rendering (SSR), minifying static resources like CSS files, and lazy loading images are all effective methods to speed up page content loading and improve the FCP.

  • Largest Contentful Paint (LCP): Evaluates the time from when the page begins loading until the largest text block or image element is rendered. Optimizing images and assets by compressing them, serving next-gen formats like WebP, and lazy loading off-screen assets are essential practices. Additionally, improving server response time and prioritizing critical resources can significantly enhance LCP.

  • Interaction to Next Paint (INP): Assesses the delay of each tap, click, or keyboard interaction, and identifies the highest latency to describe overall page responsiveness. Optimizing event handling by debouncing or throttling events and reducing JavaScript execution time are effective ways to improve INP.

  • Total Blocking Time (TBT): calculates the total time between FCP and TTI (Time To Interactive) during which the main thread is blocked enough to hinder input responsiveness. Improving Total Blocking Time (TBT) can be achieved by optimizing JavaScript execution, handling third-party scripts efficiently, and minimizing the main thread's workload through techniques such as code splitting, using async and defer attributes, auditing and lazy loading external resources, breaking down long tasks, utilizing Web Workers, and optimizing render-blocking resources.

  • Cumulative Layout Shift (CLS): tracks the cumulative score of all unexpected layout shifts occurring between the start of page loading and the change to a hidden lifecycle state. One effective solution to improve CLS is to always include width and height size attributes on images and video elements, which prevents layout shifts by reserving the required space.

  • Time to First Byte (TTFB): measures the duration it takes for the network to respond with the first byte of a resource after a user request. To improve TTFB, optimize server configurations, use Content Delivery Networks (CDNs), reduce resource payloads, and leverage caching strategies.

These six universal metrics provide a solid foundation for measurement. Web Vitals, in an effort to simplify the scenario and help websites focus on the most important metrics, presents Core Web Vitals, a subset of Web Vitals that includes LCP (Largest Contentful Paint), INP (Interaction to Next Paint) and CLS ( Cumulative Layout Shift)

The three key indicators measure page performance from different perspectives: loading speed, interactivity, and visual stability.

To collect these metrics, Google offers the web-vitals library, which is approximately 1kB in size. This library helps gather not only the mentioned three metrics but also First Contentful Paint (FCP) and Time to First Byte (TTFB). For more information on how to use the web-vitals library, visit the web-vitals GitHub repository.

Custom metrics

Web-vitals metrics provide a general method to capture performance data. Custom metrics, on the other hand, allow you to assess unique aspects of your site's performance that are specific to your application, such as:

  • How long the browser's main thread is blocked affects the frame rate or input latency.

  • The duration for a page to present data retrieved from server.

  • The period required for a server-side-rendered (SSR) application to hydrate.

In numerous instances, it is necessary to evaluate beyond some critical metrics to thoroughly understand the complete experience of your specific site.

APIs for Custom Metric Measurement

User Timing API

User timing api provides a high-percision timestamps to measure the performance of the web site.

There are two categories of timing performance entries:

  • PerformanceMark entries are named markers that can be placed anywhere within an application.

    javascript
    Copy code
    // Place at a location in the code that starts login performance.mark("login-started"); // Place at a location in the code that finishes login performance.mark("login-finished");
  • PerformanceMeasure entries represent the time taken between two specified marks.

    javascript
    Copy code
    // The Performance.measure() method is used to create a PerformanceMeasure object. // It accepts a name parameter, used to identify the measure, and two marks, start and end, that it should measure between. const loginMeasure = performance.measure( "login-duration", "login-started", "login-finished", ); // creates a "login-duration" measure and measures between the start and the finish of the login process. console.log(loginMeasure.duration);

Performance Observer API

Browser Support

52
79
57
11
Source

The Performance Observer API collects and presents data from all other performance APIs, including the previously mentioned User Timing API. This allows you to use a preferred method to get notified about your custom performance measures:

javascript
Copy code
const observer = new PerformanceObserver(function (list, observer) { list.getEntries().forEach(entry => { if (entry.entryType === 'mark') { console.log(`${entry.name}'s startTime: ${entry.startTime}`); } if (entry.entryType === 'measure') { console.log(`${entry.name}'s duration: ${entry.duration}`); } }); }); observer.observe({ entryTypes: ['measure', 'mark'] }); performance.mark('login-started'); //..other code.. performance.mark('login-finished'); //..other code.. performance.measure('login-duration', 'login-started', 'login-finished');

Performance Observer can passively subscribe to performance-related events, which means that this API usually does not interfere with the performance of the main page thread, as its callbacks are usually triggered during idle periods. And below all APIs can also be used as an observerable way:

  1. Long Tasks Timing

    Browser Support

    58
    79
    x
    x
    Source

    The Long Tasks API is beneficial for identifying when the browser's main thread is obstructed for durations that could impact frame rate or input latency. This API reports tasks that take more than 50 milliseconds to execute.

    It is advantageous to monitor if the main thread is blocked whenever you run resource-intensive code, or load and execute large scripts. Many higher-level metrics, such as Time to Interactive (TTI) and Total Blocking Time (TBT), are built on the Long Tasks API.

    To detect long tasks, you can use PerformanceObserver and register it to observe longtask entries.

    javascript
    Copy code
    const observer = new PerformanceObserver((list) => { list.getEntries().forEach((entry) => { console.log(entry); }); }); observer.observe({ type: "longtask"});
  2. Element Timing

    Browser Support

    77
    79
    x
    x
    Source

    The aim of the Element Timing API is to give web developers or analytics tools the ability to measure rendering timestamps of critical elements(image and text node elements) on a page.

    The below example, two elements are being observed by adding the elementtiming attribute

    text
    Copy code
    <img src="image.jpg" elementtiming="big-image" /> <p elementtiming="text" id="text-id">text here</p>

    Then,the PerformanceObserver should be registered to capture all performance entries of type element. The buffered flag is used to access data from before the observer's creation.

    javascript
    Copy code
    const observer = new PerformanceObserver(list => { list.getEntries().forEach(entry => { console.log(entry); }); }); observer.observe({ type: 'element', buffered: true });

    The Largest Contentful Paint (LCP) metric helps in determining when the largest image or text block was painted on the screen, which may not always align with what you want to measure. For different elements, the Element Timing API can be used, which allows explicit reporting of any element by adding the elementtiming attribute and using a PerformanceObserver to monitor the element entry type.

  3. Event Timing

    Browser Support

    76
    79
    89
    x
    Source

    The Interaction to Next Paint (INP) metric assesses overall page responsiveness by observing all click, tap, and keyboard interactions throughout the life of a page. A page's INP is most often the interaction that took the longest to complete, from the time the user initiated the interaction, to the time the browser paints the next frame showing the visual result of the user's input.

    The INP metric is made possible by the Event Timing API. This API exposes a number of timestamps that occur during the event lifecycle, including:

    • startTime: the time when the browser receives the event.

    • processingStart: the time when the browser is able to begin processing event handlers for the event.

    • processingEnd: time when the browser finishes executing all synchronous code initiated from event handlers for this event.

    • duration: the time (rounded to 8 milliseconds for security reasons) between when the browser receives the event until it's able to paint the next frame after finishing executing all synchronous code initiated from the event handlers.

    The following example shows how to use these these values to create custom measurements:

    javascript
    Copy code
    const po = new PerformanceObserver(entryList => { // Get the last interaction observed: const entries = Array.from(entryList.getEntries()).forEach(entry => { // Get various bits of interaction data: const inputDelay = entry.processingStart - entry.startTime; const processingTime = entry.processingEnd - entry.processingStart; const duration = entry.duration; const eventType = entry.name; const target = entry.target; console.log('----- INTERACTION -----'); console.log(`Input delay (ms): ${inputDelay}`); console.log(`Event handler time (ms): ${processingTime}`); console.log(`Total event duration (ms): ${duration}`); console.log(`Event type: ${eventType}`); console.log(target); });}); // A durationThreshold of 16ms is necessary to surface more // interactions, since the default is 104ms. The minimum // durationThreshold is 16ms. po.observe({ type: 'event', buffered: true, durationThreshold: 16 });
  1. Resource Timing

    Using the Resource Timing API, you can retrieve and analyze detailed network timing data for the loading of an application's resources.

    For each PerformanceResourceTiming entry, a resource loading timeline will be documented with high-resolution timestamps for various network events such as redirect start and end times, DNS lookup start and end times, request start, response start and end times, among others.

    For example, if we need to measure TCP handshake time, the calculation look like connectEnd - connectStart .

    Typically, the code for measuring metrics looks like this:

    javascript
    Copy code
    const observer = new PerformanceObserver(list => { list.getEntries().forEach(entry => { const request = entry.responseStart - entry.requestStart; if (request > 0) { console.log(entry.name, 'Request time:', request, 'ms'); } }); }); observer.observe({ type: 'resource', buffered: true });
  2. Navigation Timing

    Browser Support

    57
    12
    58
    15
    Source

    The Navigation Timing API, similar to the Resource Timing API, focuses exclusively on navigation requests. The navigation entry type resembles the resource entry type but includes additional details specific to navigation requests, such as the times when the DOMContentLoaded and load events occur.

    javascript
    Copy code
    const observer = new PerformanceObserver(list => { list.getEntries().forEach(entry => { const domContentLoadedTime = entry.domContentLoadedEventEnd - entry.domContentLoadedEventStart; console.log(`${entry.name}: DOMContentLoaded processing time: ${domContentLoadedTime}ms`); }); }); observer.observe({ type: 'navigation', buffered: true });
  3. Server Timing

    The Server Timing API let you pass request-specific timing data from your server to the browser through response headers. For example, given a Server-Timing like this:

    text
    Copy code
    Server-Timing: cache;desc="Cache Read";dur=23.2,db;dur=53,app;dur=47.2

    A PerformanceObserver can log the entries on the client side with the following code:

    javascript
    Copy code
    const observer = new PerformanceObserver(list => { list.getEntries().forEach(entry => { entry.serverTiming.forEach(serverEntry => { console.log( `${serverEntry.name} (${serverEntry.description}) duration: ${serverEntry.duration}`, ); // Logs "cache (Cache Read) duration: 23.2" // Logs "db () duration: 53" // Logs "app () duration: 47.2"F }); }); }); ['navigation', 'resource'].forEach(type => observer.observe({ type, buffered: true }));

By default, the performance observer object can only observe entries as they appear. If we want to delay loading the performance analysis code (without blocking higher priority resources), we need to do this:

javascript
Copy code
// Run the observer perfObserver.observe({   // Polls for Navigation and Resource Timing entries   entryTypes: ["navigation", "resource"], // set the buffered flag to true buffered: true, });

Setting buffered to true will cause the browser to return the history of entries in its performance entry buffer the first time the performance observer callback is invoked.

Recap

Understanding and measuring web performance metrics are crucial for enhancing user experience. The core metrics from Google’s Web Vitals, including First Contentful Paint (FCP), Largest Contentful Paint (LCP), Interaction to Next Paint (INP), Total Blocking Time (TBT), Cumulative Layout Shift (CLS), and Time to First Byte (TTFB), offer essential insights into loading speed, interactivity, and visual stability of web pages.

Custom metrics provide additional granularity by assessing unique performance aspects using APIs like User Timing and Performance Observer. These tools allow developers to capture high-precision performance data, ensuring a comprehensive evaluation beyond standard metrics.

Ultimately, leveraging both universal and custom metrics facilitates a deeper understanding and enables targeted optimizations, paving the way for a robust, user-friendly web experience.

Reference