Optimize Interaction to Next Paint

Interaction to Next Paint (INP) is an experimental metric that assesses a page’s overall responsiveness to user interactions by observing the latency of all click, tap, and keyboard interactions that occur throughout the lifespan of a user’s visit to a…


This content originally appeared on web.dev and was authored by Jeremy Wagner

Interaction to Next Paint (INP) is an experimental metric that assesses a page's overall responsiveness to user interactions by observing the latency of all click, tap, and keyboard interactions that occur throughout the lifespan of a user's visit to a page. The final INP value is the longest interaction observed, ignoring outliers.

To provide a good user experience, sites should strive to have an Interaction to Next Paint of 200 milliseconds or less. To ensure you're hitting this target for most of your users, a good threshold to measure is the 75th percentile of page loads, segmented across mobile and desktop devices.

Depending on the website, there may be few to no interactions—such as pages of mostly text and images with few to no interactive elements. Or, in the case of websites such as in-browser text editors or games, there could be hundreds—even thousands—of interactions. In either case, where there's a high INP, the user experience is at risk.

It takes time and effort to improve INP, but the reward is a better user experience. In this guide, a path to improving INP will be explored.

Figure out what's causing poor INP #

Finding ways to fix a poor INP can be difficult. To start, you have to know which interactions tend to be responsible for the page's INP, out of all the interactions users have with the page. To do that, you'll need to first lean on field data, and then test in the lab to figure out what's making an interaction slow.

Find slow interactions in the field #

Field data should be the first place you go to find slow interactions. The web-vitals JavaScript library is one way to find these interactions, thanks in large part to its ability to attribute INP values to specific elements through its attribution build:

// Use the attribution build:
import { onINP } from "web-vitals/attribution";

// Example reporting function that's passed to web-vitals:
const report = ({ name, value, attribution }) => {
console.log(name);
console.log(value);
console.log(attribution.eventType);
console.log(attribution.eventTarget);
};

// Pass the reporting function to the INP reporter:
onINP(report);

When this code runs and you interact with a page, the console may report something similar to the following:

INP
248
pointerdown
#main-nav>ul>li>button

In this case, you can see that tapping on a navigation menu toggle—which took 248 milliseconds—was responsible for the page's INP. At 248 milliseconds, this page's INP would be in the "needs improvement" category.

Once you've found elements that are responsible for slow interactions in the field, you can go into lab tools to start profiling them.

Reproduce slow interactions in the lab #

Once you have a better idea of what's responsible for slow interactions, you should profile them in the lab with a performance profiler such as the one available in Chrome's DevTools. To do so, take the following steps:

  1. Navigate to the page powering the interaction you want to test.
  2. Open Chrome's DevTools by pressing Cmd+Option+I (Ctrl+Alt+I on Windows and Linux).
  3. Go to the tab labeled Performance in DevTools.
  4. Click the record button at the upper left hand corner of the empty performance profiler to start recording.
  5. Test the desired interaction on the page.
  6. Click the record button again to stop recording.

Once the profiler populates, you'll be able to see what work occurred to drive the interaction.

There are some cues in the profiler you can use to narrow down where the interaction occurred, notably in the interactions track situated above the main thread track:

A depiction of tasks as related to interactions from Chrome's performance profiler. At top, an interactions panel with pointerdown, pointerup, and click events overlays a set of tasks in the main thread panel below. The click interaction starts before the event callbacks that drive the interaction due to a long task that delays the start of the event processing.
Figure 2. An interaction profiled in the performance profiler in Chrome's DevTools. The interactions track shows a series of events that amount to a click interaction. The interactions track entries span across the tasks responsible for driving the interaction.

What if you don't have field data? #

Not everyone collects field data from their website's users. While your long term performance optimization plans should include collecting field data (as that will make diagnosing issues significantly easier), if you're not doing that today, it's still possible to profile your pages and look for interactions that might be slow.

The first step is to diagnose common user flows. For example, if you're an online retailer, some common user flows would be to add items to a cart, search for products, and checkout.

When you test these user flows in the lab, consider doing the following:

  1. Throttle the CPU to 4x or 6x speeds to more accurately reflect interaction speeds experienced by users on low-end devices.
  2. Enable mobile emulation and choose a low-end mobile device. When you use mobile emulation in DevTools, the user agent string is changed to that of the emulated device, which can affect how page markup is served.
  3. If you have the ability, consider purchasing a cheaper Android phone (such as a Moto E) with reduced capabilities and profile interactions in the lab using remote debugging.

Once you've evaluated your potential user flows, make a note of the slowest interactions, and then you can begin to figure out where to start optimizing them. Regardless of whether you're collecting field data at this point, it depends on what part of the interaction you need to pay attention to in order to know what to optimize.

Diagnose and deal with long input delay #

The input delay is the first phase of an interaction. This is the period of time from when the user action is first received by the operating system until the browser is able to start processing the first event triggered by that input. The input delay ends right as the event callbacks for the interaction begin to run.

How to identify input delay #

Identifying input delay in Chrome's performance profiler can be done by finding the start of an interaction in the interactions panel, and then finding the beginning of when the event callbacks for that interaction start to run.

You'll always incur at least some input delay, as it takes some time for the operating system to pass the input event to the browser—but you do have some control over how long the input delay is. The key is to figure out if there is work running on the main thread that's preventing your callbacks from running.

A depiction of input delay in Chrome's performance panel. The start of the interaction comes significantly before the event callbacks because of increased input delay due to a timer firing from a third-party script.
Figure 3. Input delay caused by a task fired by a timer from a third-party script.

In the previous figure a task from a third-party script is running as the user attempts to interact with the page, and therefore extends the input delay. The extended input delay affects the interaction's latency, and could therefore affect the page's INP.

How to fix long input delays #

When it comes to solutions for long input delays, it all depends. If you have expensive first-party work that could be affecting an interaction's input delay, the answer is four-fold:

  1. Do as little work as possible in any tasks your code kicks off.
  2. Break up long tasks.
  3. Use the Scheduler API to prioritize critical tasks and defer non-critical tasks.
  4. Consider using isInputPending to check if a user input is pending. If so, you can yield to the main thread so the event callbacks for that input can run sooner than they otherwise would.

If it's third-party code that's causing problems, then you have a lot more to consider:

  1. Do a complete inventory of all your website's third-party scripts.
  2. Figure out if more than one third-party script's functionality overlaps significantly with others.
  3. Cull redundant or low-value third-party scripts.
  4. Maintain whatever third-party scripts are left over so that they impact performance as little as possible.

This is difficult in large part because third-party JavaScript is an issue of work culture. For example (tag managers make it easy for non-technical people in a company to add third-party scripts without the knowledge of the development teams.

Optimize event callbacks #

The input delay is only the first part of what INP measures. You'll also need to make sure that the event callbacks that run in response to a user interaction can complete as quickly as possible.

A depiction of event callback tasks in Chrome's performance panel. The event callbacks occur for the pointerdown and click events, which compose a long task.
Figure 4. The event callbacks that run in response to a tap interaction, as shown in the performance profiler in Chrome DevTools.

The best way to ensure your event callbacks run quickly is to limit what gets run to just the logic that is required to apply visual updates for the next frame. Everything else can be deferred to a subsequent task.

For example, imagine a rich text editor that formats text as your type but also updates other aspects of the UI in response to what you've written (such as word count, spelling mistakes, etc.). In addition, the application may also need to save what you've written so that if you leave and return, you haven't lost any work.

In this example, the following four things need to happen in response to characters typed by the user. However, only the first item needs to be done before the next frame is presented.

  1. Update the text box with what the user typed and apply any required formatting.
  2. Update the part of the UI that displays the current word count.
  3. Run logic to check for spelling mistakes.
  4. Save the most recent changes (either locally or to a remote database).

The code to do this might look something like this:

textBox.addEventListener('keydown', (keyboardEvent) => {
// Update the UI immediately, so the changes the user made
// are visible as soon as the next frame is presented.
updateTextBox(keyboardEvent);

// Defer all other work until at least after the next frame,
// by queuing a task in a `requestAnimationFrame()` callback.
requestAnimationFrame(() => {
setTimeout(() => {
const text = textBox.textContent;
updateWordCount(text);
checkSpelling(text);
saveChanges(text);
}, 0);
});
});

The following visualization should help explain why deferring any non-critical updates until after the next frame can reduce the processing time and thus the overall interaction latency.

A depiction of a keyboard interaction and subsequent tasks in two scenarios. In the top figure, the render-critical task and all subsequent background tasks run synchronously until the opportunity to present a frame has arrived. In the bottom figure, the render-critical work runs first, then yields to the main thread to present a new frame sooner. The background tasks run thereafter.
Click the above figure to see a high-resolution version.

While admittedly the use of setTimeout() inside of a call to requestAnimationFrame() in the previous code example is a bit esoteric, it is an effective and cross-browser way to ensure that the non-critical code does not block the next frame.

In the future, as new Scheduling APIs like scheduler.postTask() and scheduler.yield() are standardized and implemented, it will be easier for developers to write code that can be automatically prioritized without them needing to have an intimate knowledge of the browser event loop.

Minimize presentation delay #

The last step of an interaction is the presentation delay, which starts when the event callbacks have finished running, and ends when the browser is able to present the next frame to the user's display.

The unfortunate fact is that presentation delays are something you have the least amount of control over. However, here's a few things to look out for if you're noticing that frames are being delayed:

  1. Don't abuse requestAnimationFrame() for work not related to rendering. requestAnimationFrame() callbacks are run during the rendering phase of the event loop, and must complete before the next frame can be presented. If you're using requestAnimationFrame() to do work that doesn't involve changes to the user interface, understand that you could be delaying the next frame.
  2. Be careful with async and await and your assumptions around how they work. Just because you're using these features (or Promises in general) doesn't mean that the work they do won't block rendering. It's entirely possible that an async function—even if it does break the work in a callback into a separate task—can still block rendering if the browser chooses to run it before the next frame is presented. This is why it's important to keep all tasks short.
  3. Be careful with observer callbacks, such as those used by IntersectionObserver or ResizeObserver. These callbacks are run prior to rendering, and may delay presentation of the next frame if the work in them is expensive, as with event callbacks, defer any logic not needed for the very next frame.

When interactions overlap #

It's also important to understand how interactions can overlap. For example, if there are many interactions within a short period, it's possible that the processing time or presentation delay for one interaction can be a source of input delay for a subsequent interaction.

A depiction of when tasks can overlap to produce long input delays. In this depiction, a click interaction overlaps with a keydown interaction to increase the input delay for the keydown interaction.
Figure 6. A visualization of two concurrent interactions in the performance profiler in Chrome's DevTools. The rendering work in the initial click interaction causes an input delay for the subsequent keyboard interaction.

These overlaps are tricky to diagnose in the field, in part because it's difficult to track if the source of a high INP is due to multiple interactions that interfere with one another. The best thing you can do to alleviate poor responsiveness in your interactions is to do as little work as possible in your interaction's event callbacks and always keep your tasks short. This helps to reduce contention between interactions and gives the main thread a bit more breathing room.

Improving INP requires persistence #

Improving your site's INP is an iterative process. When you fix a slow interaction in the field, chances are good that—especially if your website provides tons of interactivity—you'll start to find other slow interactions, and you'll need to optimize them too.

The key to improving INP is persistence. In time, you can get your page's responsiveness in a spot where users are happy with the experience you're providing them. Chances are also good that as you develop new features for your users, you may need to go through the same process in optimizing interactions specific to them. It will take time and effort, but it's time and effort well spent.


This content originally appeared on web.dev and was authored by Jeremy Wagner


Print Share Comment Cite Upload Translate Updates
APA

Jeremy Wagner | Sciencx (2022-12-08T00:00:00+00:00) Optimize Interaction to Next Paint. Retrieved from https://www.scien.cx/2022/12/08/optimize-interaction-to-next-paint/

MLA
" » Optimize Interaction to Next Paint." Jeremy Wagner | Sciencx - Thursday December 8, 2022, https://www.scien.cx/2022/12/08/optimize-interaction-to-next-paint/
HARVARD
Jeremy Wagner | Sciencx Thursday December 8, 2022 » Optimize Interaction to Next Paint., viewed ,<https://www.scien.cx/2022/12/08/optimize-interaction-to-next-paint/>
VANCOUVER
Jeremy Wagner | Sciencx - » Optimize Interaction to Next Paint. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/12/08/optimize-interaction-to-next-paint/
CHICAGO
" » Optimize Interaction to Next Paint." Jeremy Wagner | Sciencx - Accessed . https://www.scien.cx/2022/12/08/optimize-interaction-to-next-paint/
IEEE
" » Optimize Interaction to Next Paint." Jeremy Wagner | Sciencx [Online]. Available: https://www.scien.cx/2022/12/08/optimize-interaction-to-next-paint/. [Accessed: ]
rf:citation
» Optimize Interaction to Next Paint | Jeremy Wagner | Sciencx | https://www.scien.cx/2022/12/08/optimize-interaction-to-next-paint/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.