Lightscore

Interaction to Next Paint (INP)

Interaction to Next Paint measures the time between a tap, a click or a key press and the next frame the browser paints. A good INP is 200 milliseconds or less. Above 500 milliseconds is poor. Google grades the 75th percentile of your real visits, so three quarters of them have to clear the bar.

INP is a field metric. Chrome collects it from real people, across a whole visit. It became a Core Web Vital on 12 March 2024, and replaced First Input Delay. FID timed only the first interaction, and only the delay before the handler ran.

What are the three parts of an interaction?

INP is the sum of three phases, not one measurement:

Visitor taps

Input delay
main thread busy

Processing duration
your handlers run

Presentation delay
layout, paint

Next frame
is visible

The three phases of one interaction. INP measures all of it, from the tap to the pixels.

Only three kinds of input are observed: a click with a mouse, a tap on a touchscreen, and a key press. Scrolling and hovering don't count.

Does INP report my slowest interaction?

Almost. Chrome discounts one interaction for every 50 on the page, so a single unlucky click can't decide the score of a page people use heavily. Past 50 interactions that works out close to the 98th percentile.

Below 50 interactions there is nothing to discount. INP is then the worst interaction of the visit.

Can you measure INP in a lab?

You can get a number. It isn't your INP, and the gap is wider than the usual lab-versus-field caveat.

Lighthouse reports INP in timespan mode only — you start a recording, interact with the page, and stop. The Lighthouse panel in Chrome DevTools drives that mode. Our own fleet runs navigation mode, so it never produces one.

Here's what that number is. In Lighthouse 12.8.2, responsiveness.js sorts the interaction events Chrome kept — at most the ten worst — by duration, longest first, and then picks one:

const index = Math.min(9, Math.floor(responsivenessEvents.length / 50));
return responsivenessEvents[index];

That's the same discount rule as the field metric. Under 50 interactions the index is 0. Your lab INP is the single worst click you made.

So I ran it. The test page has four buttons, and each handler blocks the main thread for a fixed time. I ran it on a laptop under Lighthouse 12.8.2 and Chrome for Testing 145, in timespan mode at 4× CPU throttling. The handlers busy-wait on wall-clock time, so the throttle doesn't inflate the blocking durations. Three timespans per row, and each range covers all three:

Clicks in the session INP TBT
60, 150, 350 ms 365–371 ms 415–432 ms
60, 150, 350, 900 ms 916–924 ms 1,267–1,268 ms
900 ms alone 918–923 ms 852–853 ms

One 900 ms click on its own reports the same INP as the session with all four clicks in it. The 60, 150 and 350 ms interactions add nothing. TBTHow long the main thread was busy and couldn’t respond to taps or clicks. moves by about 415 ms between those two sessions, because TBT counts the blocking time of every long task.

Reversing the click order changed nothing either: 60, 150, 350 and 350, 150, 60 both reported 365–371 ms.

So I'd read a lab INP as a measurement of the script, not of the page. It tells you what the interaction you chose to test costs, which is useful when you're fixing one known-slow button. It tells you nothing about the interaction your visitors actually make most.

What a normal Lighthouse run reports instead

A navigation run has no INP audit at all. Not notApplicable — absent. The audit declares supportedModes: ['timespan'], and filterAuditsByGatherMode in filters.js drops any audit that doesn't support the current mode.

I checked one of our own reports to be sure. A completed Lighthouse 12.8.2 run of image-line.com/fl-studio/download from Frankfurt has no interaction-to-next-paint key in lhr.audits, and no entry for it in the performance category.

What fills the gap is Total Blocking Time, which carries the largest single weight in the performance score at 30 percent. TBT sums the part of each long task that runs past 50 milliseconds, between First Contentful Paint and Time to Interactive.

That window closes during the load. Interactions after the load fall outside it. So TBT speaks to input delay on a busy page, and says nothing about handlers that run later or about paint cost. A page can report 0 ms TBT and still respond slowly to every click.

How to reduce INP

Take the three phases one at a time. Each one has its own fix.

  1. Cut input delay first. Break up long tasks during load, and evaluate less script while the page loads.
  2. Shorten your event handlers. Do the visible update, then hand the rest back to the browser with setTimeout. When the update has to paint first, use requestAnimationFrame followed by setTimeout.
  3. Don't read a layout property straight after you write one. That forces a synchronous layout inside the handler.
  4. Attack presentation delay last. A smaller DOM paints faster, and content-visibility lets the browser skip work that's off screen.

What we measure, and what we don't

We can't give you INP. Our worker loads your page with nobody in front of it, so there's no interaction to time. Every result carries six lab metrics — lcp_ms, cls, tbt_ms, fcp_ms, speed_index_ms and tti_ms. There's no inp_ms field, because there'd be nothing to put in it.

What a lab run does give you is a fixed set of conditions. We pin the Lighthouse and Chromium version, and the result records the Lighthouse version that produced your number. You pick the regions with the checkboxes on the audit form, up to two per run. Through the API you can ask for up to 5 runs per region. We return the median run, so one slow load doesn't decide the answer. The raw Lighthouse JSON and HTML come back with every report.

For INP itself, go to the field: the Chrome UX Report, Search Console, or the web-vitals library on your own pages. Use TBT here as the early warning, and treat a green TBT as evidence about the load and nothing more.

what’s this

The small set of field metrics Google treats as a ranking input and reports in Search Console. We measure their lab equivalents here — real-user field data (CrUX) is a separate signal. TBT is the lab stand-in for INP, not a Core Web Vital itself.

Common questions