This content originally appeared on web.dev and was authored by Jeremy Wagner
One challenging aspect of optimizing Interaction to Next Paint (INP) is figuring out what's causing poor INP. There's a large variety of potential causes: third-party scripts that schedule many tasks on the main thread, large DOM sizes, expensive event callbacks, and so on.
Finding ways to fix poor INP can be difficult. To start, you have to know which interactions tend to be responsible for a page's INP. If you don't know this already, start by reading Find slow interactions in the field. Once you have that field data and know what interactions to test, you can do so in lab tools to work out why those interactions are slow.
What if you don't have field data? #
Ideally, you'll want field data, as it saves you a lot of time trying to figure out which interactions need to be optimized. You might be in a position where you don't have field data, though. If that's your situation, you can still find interactions to improve: you'll just have to take a different approach.
Total Blocking Time (TBT) is a metric that assesses page responsiveness during load. It correlates very well with INP, and can give you an idea if there might be interactions you can profile while the page is loading.
You can use either Lighthouse or PageSpeed Insights to measure your page's TBT. If your TBT is either poor or needs improvement, there's a good chance there are interactions that might be slow during page load.
To find slow interactions after the page has loaded, you might need to rely on other types of data, such as common user flows that you may already have in your website's analytics. If you work on an ecommerce website, for example, a common user flow would be the actions users take when they're adding items to an online shopping cart or going through an online checkout.
Whether or not you have field data, the next step involves reproducing that interaction—because it's only when you're able to conclusively identify a slow interaction that you'll be able to fix it.
Reproducing slow interactions in the lab #
Once you've identified a slow interaction, the next step is to test it in the lab to see if it's reliably slow.
Don't reach for the performance profiler right away #
Chrome performance profiler—while invaluable—doesn't provide a live view while interacting with the page. It's more efficient to test interaction latency in a much faster way first, so you can quickly assess whether a given interaction is reliably slow, and then reach for the performance profiler when you need more information.
Use the Web Vitals Chrome Extension #
The Web Vitals Chrome Extension involves the lowest amount of effort in testing interaction latency. Once installed, the extension will display interaction data in the console if you do the following:
- In Chrome, click the extensions icon to the right of the address bar.
- Locate the Web Vitals extension in the drop-down menu.
- Click the icon at the right to open the extension's settings.
- Click Options.
- Enable the Console logging checkbox in the resulting screen, and then click Save.
Once this has been done, open the console in Chrome DevTools, and begin testing the desired interactions on your website. As you interact with the page, you'll receive useful console logs giving you detailed diagnostic information for the interaction.
Use a JavaScript snippet #
Using the Web Vitals extension may not be a viable option for a number of reasons. Extensions can be blocked in some cases, and they also can't be installed on mobile devices. The latter is problematic if you want to test on a physical Android device with remote debugging.
An alternate method involves copying and pasting some JavaScript into the console of Chrome DevTools. The following code yields the same console output as the Web Vitals extension for every interaction:
let worstInp = 0;
const observer = new PerformanceObserver((list, obs, options) => {
for (let entry of list.getEntries()) {
if (!entry.interactionId) continue;
entry.renderTime = entry.startTime + entry.duration;
worstInp = Math.max(entry.duration, worstInp);
console.log('[Interaction]', entry.duration, `type: ${entry.name} interactionCount: ${performance.interactionCount}, worstInp: ${worstInp}`, entry, options);
}
});
observer.observe({
type: 'event',
durationThreshold: 0, // 16 minimum by spec
buffered: true
});
Once you have determined that the interaction is reliably slow, you can then profile the interaction to get more detailed information on why that interaction is slow.
What if you can't reproduce a slow interaction? #
What if you've managed to find something in your field data that suggests a particular interaction is slow, but you can't reproduce it in the lab?
For one, this is a common challenge in troubleshooting performance issues of any kind. Your point of view when testing is entirely relative and dependent on the hardware you're using. After all, you may be on a fast device with a fast internet connection—but that doesn't mean your users are too. For this situation you can do one of three things:
- If you have a physical Android device, use remote debugging to open a Chrome DevTools instance on your host machine and try to reproduce slow interactions there.
- If you don't have a physical device, enable the CPU throttling feature in Chrome DevTools.
- Follow both steps 1 and 2, as you can also enable CPU throttling on the DevTools instance for a physical Android device.
Another cause could be that you're waiting for a page to load before you interact with it, but your users are not. If you're on a fast network, enable network throttling and interact with the page as soon as it paints. You should do this because the main thread is often busiest during startup, and testing at that time might help reveal what your users are actually experiencing.
Record a trace #
To get more information on why an interaction is slow, you'll need to take things to the next level by using the performance profiler in Chrome DevTools. To profile an interaction in Chrome's performance profiler, do the following:
- With the page you need to profile loaded and ready for interactions, open Chrome DevTools and go to the Performance panel.
- Click the Record button at the upper left of the panel to start tracing.
- Perform the desired interaction.
- Click the Record button again to stop tracing.
When the profile populates, the first place to look should be the activity summary at the top of the profiler. The activity summary will show red bars at the top where long tasks occurred in the recording. This allows you to quickly zoom in on problem areas.
You can quickly focus on the problem area by dragging and selecting a region in the activity summary. Once you've focused to where the interaction occurred, the interactions track will help you line up the interaction and the activity that occurred in the main thread track below it:
From here, it's a matter of observing what the problem with the interaction might be. There are many things that can contribute to high interaction latency, so let's go through what some of the culprits could be.
Use Lighthouse timespans as an alternative to tracing #
Chrome's performance profiler—while rich with diagnostic information—can be a bit intimidating to the uninitiated. One alternative to the performance profiler is Lighthouse's timespan mode. To use this mode, do the following:
- With DevTools open, go to the Lighthouse tab in DevTools.
- Under the section labeled Mode, select the Timespan option.
- Select the desired device type under the section labeled Device.
- Ensure at least the checkbox labeled Performance is selected under the Categories label.
- Click the Start timespan button.
- Test the desired interaction(s) on the page.
- Click the End timespan button and wait for the audit to appear
- Once the audit populates in the Lighthouse tab, you can filter the audits by INP by clicking the INP link next to the label which reads Show audits relevant to.
At this point, you'll see a drop-down list for audits that have failed or passed. When you expand that drop-down, you'll probably see a breakdown of time spent during the interaction.
How to identify long input delays #
One thing that could be contributing to high interaction latency is 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.
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.
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.
For more information on how you can resolve long input delays, read about how you can identify and reduce input delay.
How to identify expensive event callbacks #
Event callbacks occur immediately after the input delay. If an event callback runs too long, it delays the browser from presenting the next frame, and can add significantly to an interaction's total latency. Event callbacks can often run for too long, whether they run as the result of first-party or third-party JavaScript—and in some cases, both.
Finding expensive event callbacks can be done by observing the following in a trace for a specific interaction:
- Determine whether the task associated with the event callbacks is a long task. To reveal long tasks in a lab setting more reliably, you may need to enable CPU throttling in the performance panel, or connect a low to mid-tier Android device and use remote debugging.
- If the task that runs the event callbacks is a long task, look for event handler entries (entries with names such as Event: click, for example) in the call stack that have a red triangle at the upper right corner of the entry. These are expensive event callbacks.
To address expensive event callbacks, try one of the following strategies:
- Do as little work as possible. Is everything that happens in an expensive event callback strictly necessary? If not, consider removing that code altogether if you can, or deferring its execution to a later point in time if you can't. You can also take advantage of framework features to help. For example, React's
PureComponent
class and memoization feature can skip unnecessary rendering work when props and state haven't changed for a component. - Defer non-rendering work in the event callback to a later point in time. Long tasks can be broken up by yielding to the main thread. Whenever you yield to the main thread, you're ending execution of the current task and breaking up the remainder of the work into a separate task. This gives the renderer a chance to process updates to the user interface that were processed earlier in the event callback. If you happen to be developing in React, its transitions feature will do this for you.
By employing these strategies, you should be able to get your event callbacks in a place where they're responding more quickly to user input.
How to identify presentation delays #
Long input delays and expensive event callbacks aren't the only possible culprits of poor INP. Sometimes the rendering updates that occur in response to even small amounts of event callback code can be expensive. The time it takes for the browser to render visual updates to the user interface to reflect the result of an interaction is known as presentation delay.
Of all the possible causes of high interaction latency, rendering work can be the most difficult to troubleshoot and fix, but the result is worth the effort. Excessive rendering work could be caused by any of the following:
- Large DOM sizes. Rendering work often increases along with the size of the page's DOM. For more information, read How large DOM sizes affect interactivity—and what you can do about it.
- Forced reflows. This happens when you apply style changes to elements in JavaScript, and then query the results of that work. The result is that the browser has to perform the layout work before doing anything else, so that the browser can return the updated styles. For more information and tips on avoiding forced reflows, read Avoid large, complex layouts and layout thrashing.
- Excessive or unnecessary work in
requestAnimationFrame
callbacks.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 usingrequestAnimationFrame()
to do work that doesn't involve changes to the user interface, understand that you could be delaying the next frame. ResizeObserver
callbacks. Such callbacks 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 next frame.
Troubleshooting INP is an iterative process #
Finding out what's causing high interaction latency that contributes to poor INP takes a lot of work—but if you can pin down the causes, you're halfway there. By following a methodical approach to troubleshooting poor INP, you can reliably pin down what's causing a problem, and arrive more quickly to the right fix. To review:
- Rely on field data to find slow interactions.
- Test problematic field interactions in the lab to see if they're reproducible.
- Identify whether the cause is due to long input delay, expensive event callbacks, or expensive rendering work.
- Repeat.
The last of these is the most important. Like most other work you must do to improve page performance, troubleshooting and improving INP is a cyclical process. When you fix one slow interaction, you'll need to move onto the next, and continue to do so until you start to see results. Stay vigilant!
Hero image from Unsplash, by Louis Reed.
This content originally appeared on web.dev and was authored by Jeremy Wagner
Jeremy Wagner | Sciencx (2023-05-09T00:00:00+00:00) Diagnose slow interactions in the lab. Retrieved from https://www.scien.cx/2023/05/09/diagnose-slow-interactions-in-the-lab/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.