How to improve the INP Presentation Delay?

339 views
Skip to first unread message

Jan Nicklas

unread,
May 3, 2024, 10:19:22 AMMay 3
to web-vitals-feedback
The upcoming Version 4 of the Google web-vitals open source library will break down the INP into three parts (web-vitals #442).

- Input Delay
- Processing Time
- Presentation Delay

The Chrome web-vitals extension is already display these metrics in the console:
inp-breakdown.png

Analysing the Input Delay in the Chrome Performance Panel seems to be quite doable as it is usually a previous long task which blocks the main thread.

Very often the processing time is the easiest part to analyse because it's the code which is directly connected to the click listener.

However I haven't found a good way to analyse the Presentation Delay.
It's just a big purple bar with almost no additional information:

Total Time 149.78 ms
Self Time 149.78 ms
Elements Affected 3805
Initiated by Schedule Style Recalculation
Pending for 25.7 ms

How can I find out why that DOM update affected 3,805 Elements?
How can I find out which part of the DOM was affected?

Does anyone know any good Videos or Guides for this topic?

Kevin Farrugia

unread,
May 20, 2024, 3:42:15 AMMay 20
to Jan Nicklas, web-vitals-feedback
Hi Jan,

Firstly I recommend using a development build if available as it would make it much easier to debug.

From my own experience, the length of time spent on Recalculate Style is proportional to the number of elements affected. For example, if you are adding a large number of elements to the DOM or changing a parent element's CSS class. Not all CSS properties  In your example, the number of elements affected is also very high.

You may find Microsoft Edge's Selector Stats useful at understanding which elements have been affected—although I was unable to get much value in your example:

image.png

To enable it, you need to Enable advanced rendering instrumentation (slow) in the Performance Panel. I think the feature is also available in Chrome Canary.

In your example you set the `inert` attribute to a number of HTML elements which can cause a significant Recalculate Style (not sure why TBH). I learned this recently from this video which covers the topic: https://youtu.be/cmtfM4emG5k?feature=shared

Hope that helps.

--
You received this message because you are subscribed to the Google Groups "web-vitals-feedback" group.
To unsubscribe from this group and stop receiving emails from it, send an email to web-vitals-feed...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/web-vitals-feedback/7decca2b-bc80-4732-a6b3-f336462dc98cn%40googlegroups.com.

Barry Pollard

unread,
May 20, 2024, 5:13:50 AMMay 20
to Kevin Farrugia, Jan Nicklas, web-vitals-feedback
You may find Microsoft Edge's Selector Stats useful at understanding which elements have been affected

This has actually made it to Chrome stable from v125: https://developer.chrome.com/blog/new-in-devtools-125#selector-stats

It should be noted however that this option does slowly down the performance trace considerable. In v126 we've added the "(slow)" text label for this reason:

image.png

So it's best to use this as a check of relative cost between selectors rather than absolute costs.


Jan Nicklas

unread,
May 22, 2024, 10:02:53 AMMay 22
to web-vitals-feedback
Thanks for mentioning the new Selector Stats pane in Chrome DevTools!
It's a great addition that helps learning and debugging on how fast the browser resolves CSS selectors.
Instead of long debates over certain selector performance we have now real data and can optimize based on that.

However, although that's quite powerful to compare the speed of different selectors, it doesn't address the (or at least my) core
issue behind many long "Recalculate Style" tasks and therefore is not ideal to optimize the presentation delay.

Often, these Recalculation Style tasks are slow because the large number of elements are affected.
Especially when frameworks or libraries dynamically inject styles, it can be a way to hard to find the source of performance bottlenecks.

I encountered a Chrome performance bug that had gone unnoticed for years, maybe because the debugging experience isn't as good as it could be.

An example to show you what I mean - lets assume somewhere in your app you have this code:

const style = document.createElement("style");
style.innerHTML = `
 html:has(button:hover) {
   background: orange
 }
`;
document.head.appendChild(style)


Hovering a button is blazing fast:

recalculate2.png

Now lets do something different - instead of the background color lets use a custom property which is never used and does nothing:

const style = document.createElement("style");
style.innerHTML = `
 html:has(button:hover) {
   --demo-hover: 1
 }
`;
document.head.appendChild(style)


Now hovering isn't that fast anymore.
The recalculate style takes now 1000x longer! (maybe this is also a bug?)
Anyway the point I am trying to make is that it is almost impossible to track down that this 600ms delay was caused by `--demo-hover: 1`

recalculate.png

And now imagine a situation where a dev installs a library which has code from the example above built in...
There is a good chance that optimizing INP and presentation delay will end in a debugging nightmare and
they might never find out that two lines of css are slowing down their entire page that much.

Do you maybe know any other way to debug the presentation delays?

Michal Mocny

unread,
May 22, 2024, 10:23:23 AMMay 22
to Jan Nicklas, web-vitals-feedback
Jan,

Thanks for sharing a fantastic simple example to showcase presentation style calculation issues!

I can sympathize with your concerns on the ease with which these types of performance issues might be able to sneak in, and the difficulty in catching them.

I just wanted to clarify: in this case, would you agree that it is a good thing that INP the metric, and web-vitals.js attribution the library, at least are highlighting a real UX performance problem?

-Michal

Jan Nicklas

unread,
May 24, 2024, 9:08:42 AMMay 24
to web-vitals-feedback
That’s an interesting question. I can only tell you about our journey.
 
Some background:
Our react codebase has almost 10.000 tsx files.
We have over a million of unique daily users.
My team has spent a lot time over the past half-year improving the INP metric.
 
In short:
Did it improve UX? No
Did it help us developer? Yes
 
The good things:
- Our developers now have a better understanding of the performance goals
- INP provides a helpful argument when advocating for performance improvements in libraries and browsers
- web-vitals.js allowed us to track slow INP together with meta data to help reproducing slow INP cases
- we can compare our performance with relevant competitors
- web-vitals.js together with cypress helped us to verify and falsify different performance believes: https://github.com/jantimon/inp-tests
 
Other observations:
- we identified two performance issues in Chrome's rendering, which, once addressed and rolled out, will improve the speed especially for our low end android users ( https://issues.chromium.org/issues/339311809 and https://issues.chromium.org/issues/342214592 )
- we discovered https://groups.google.com/g/web-vitals-feedback and learned a lot from the questions and answers
- our developers took time to learn about performance optimization
- modern frameworks (like react, angular, vue …) are already optimised for INP as all of them avoid layout thrashing
- the most relevant improvement of our INP was deferring tracking with requestIdleCallback
 
The bad things:
- we got hints like “just reduce dom nodes” which were hardly possible to implement without sacrificing UX or accessibility
- Browser extensions influence the INP but that’s hard to identify and even harder to improve
- Some profiler options slow down the profiling which can be very misleading
- Slow third-party code, whether from a library or the browser itself, is challenging to improve, leading developers to use techniques like `setTimeout` to game the metric. Which can lead to a slower overall performance
 
INP (and its sub metrics) together with web-vitals.js enhances our performance monitoring and understanding but the impact on users is less direct
 
Hope that helps

Tiago Rodrigues

unread,
Jun 14, 2024, 6:10:00 AM (2 days ago) Jun 14
to web-vitals-feedback
Hey folks!

I'd like to add my 2 cents here as I've been dealing with a situation that sounds similar to what Jan has been describing here.

As we started tracking INP earlier this year at our company we've been learning more about the metric and surfacing cases where we need to make improvements.

The majority of these were indeed cases where the fixes we introduced have made an improvement to the UX for the user, although one can probably argue as the usefulness of some cases versus others.

In most of them it was all about adding a loading animation to some buttons after the user clicks them, so they can have more immediate feedback that their action led to something.

Here's a before and after of one of these cases:

Screenshot 2024-06-10 at 17.13.41.png

After:

Screenshot 2024-06-10 at 17.19.12.png

However, as you can see on the second image, the Presentation Delay is still 44ms! All that we're doing in this case is replacing the contents of a button with a CSS spinner and an opacity change, so effectively an animation and an opacity change.

These profiles were also taken on an Android device (https://www.ayntec.com/products/odin-2), which is definitely not a low end device, specially given that it's made for gaming purposes.

If these are the numbers we're seeing on a high end Android device I can only imagine what lower end devices are seeing.

Our use case here is actually very similar to the rotation animation example seen here on this page: https://web.dev/articles/animations-guide

More specifically, this one: https://animation-rotate.glitch.me/

I took a similar profile on this same device and here's the result:

Screenshot 2024-06-10 at 18.56.11.png


That's still close to 40ms on a much simpler example compared to the page I've been working on.

Even taking another profile with Enable CSS Selector stats on shows how little is going on and how long the presentation delay still takes:

Screenshot 2024-06-10 at 18.59.19.png


I ran a similar profile with Enable CSS Selector stats in our case and there does seem to be some things we can optimize, but our presentation delay isn't much higher than this stripped down example on glitch, so I really wonder if there's any value in spending any time on any further optimization here.

Is this a case of a measurement issue or is it a case of "this is as fast as the browser goes" when it comes to rendering these animations?

Michal Mocny

unread,
Jun 14, 2024, 10:13:31 AM (2 days ago) Jun 14
to Tiago Rodrigues, web-vitals-feedback
40ms of presentation delay on many android devices is within the range of commonly observed values, even even each stage of rendering is within its own deadline, due to scheduling and pipelining of the rendering system. 

I think it's common advise to suggest 100ms for providing feedback to user input, yet INP choose 200ms for the target threshold (among other reasons) due to these realistic constraints.

You should still have 100-150ms of main thread time even on low end devices, provided you don't request abundant rasterization effects.  If you do have these, you might need to trade some main thread time to allow for more presentation delays.

Good luck, and good work making improvements to UX so far!

Tiago Rodrigues

unread,
Jun 14, 2024, 11:01:07 AM (2 days ago) Jun 14
to web-vitals-feedback
One thing I did forget to mention is that while we see these values (40ms or so) for presentation delay in our tests, for these specific cases we often see 150ms and up on the real world data we collect.

Most of these cases and the reason why we looked into them were based on this data we collect. For some of them we saw larger processing durations (like the example I showed above), then optimized those and it improved overall, but we still often see long presentation delays.

For example sometimes we'll see a 20ms input delay, 50ms processing, and 150ms presentation delay. I guess that still fits in to the timings you mentioned on low end devices but yeah, it sometimes feels a lot for something fairly simple.

In most of these cases we're only doing an opacity reduction on the button and replacing the label with a CSS animation based spinner (animation with a rotate transform), so nothing too wild and that should be pretty optimized by browsers.

Michal Mocny

unread,
Jun 14, 2024, 11:38:29 AM (2 days ago) Jun 14
to Tiago Rodrigues, web-vitals-feedback
(First of all-- this sounds like you are doing excellent work and that you have a good handle on it already-- cheers to that!)

In my humble 2cents opinion, I would try to split out that 150ms into smaller breakdowns for what is on the main thread (e.g. requestAnimationFrame, style layout paint) from time off the main thread.  You can (finally!) do this conveniently with the LoAF entry (if you have it available), by leveraging timestamps such as renderStart, styleAndLayoutStart, and the loaf endtime (startTime + duration) comparing against the INP timestamps such as processingEnd and endTime (startTime + duration).

That total presentation delay is really a sum of stages:
  • Other blocking work after event handlers... such as other JS tasks that sneak in before rendering is scheduled, or
  • work that is required as part of rendering steps for an animation frame, such as rAF callbacks, various JS Observers, or
  • the actual cost of style/layout/paint for your specific page.
  • Finally, once the main thread is done, there is still off-thread work like rasterizing.
Usually this is now in parallel with the main thread work that follows, but on some hardware you can still have contention for resources.  Or, some specific effects (like opacity animations?) might be very fast on some hardware and slower on other hardware.

In all of those stages the design of your site will have an impact, and it might help knowing which stage to chase.

Good luck!
-Michal

Reply all
Reply to author
Forward
0 new messages