completefrontend
Open lab My progress
THE WEB PERFORMANCE FIELD GUIDE

How INP works

Which clicks, taps and key presses INP counts, how one value is picked for a visit, why good is 200 ms, and why INP replaced FID, recorded in Chrome.

By the end, you can
  • 01
    Tell what countsSay which of a visitor's actions are interactions for INP, and why pointing and scrolling aren't.
  • 02
    Pick a visit's INPChoose one value from many interactions, skipping one for every 50, the way web-vitals and Chrome do.
  • 03
    Read the ratings, and what FID missedRate an INP against 200 and 500 ms, say where those lines come from, and explain why FID wasn't enough.

One number for a whole visit

Asha is on the Leaf and Kettle tea list, on her laptop. It has a touch screen, so sometimes she taps instead of clicking.

She does 15 things. She points at Mint tea to read about it. She scrolls down and back up. She clicks Add on Green tea, and taps Add on Mint tea. She swipes the list up and down with a finger. She taps the search box, types "chai" and deletes it. Then she types "mint" and deletes that too. She taps Sort by price. Last, she points at Earl Grey and clicks its Add button.

Almost everything answers at once. But when she taps Sort by price, nothing happens. The list sits still for about a third of a second. Then it jumps into its new order. A frame is one picture the browser draws on the screen. Here is her screen at that tap, in real frames Chrome captured:

Asha's screen in real frames as she taps Sort by price. At her tap, the list shows the old order: Mint tea, Green tea, Earl Grey, Chamomile. 100 ms later, the list shows the old order: Mint tea, Green tea, Earl Grey, Chamomile. 200 ms later, the list shows the old order: Mint tea, Green tea, Earl Grey, Chamomile. 306 ms later, the list shows sorted by price: Chamomile, Rooibos, Earl Grey, Green tea. Chrome timed the tap at 304 ms.her tapThe tea list, the old order: Mint tea, Green tea, Earl Grey, Chamomilethe old order100 ms laterThe tea list, the old order: Mint tea, Green tea, Earl Grey, Chamomileno change yet200 ms laterThe tea list, the old order: Mint tea, Green tea, Earl Grey, Chamomileno change yet306 ms laterThe tea list, sorted by price: Chamomile, Rooibos, Earl Grey, Green teasorted by price
Real frames from Asha's recorded visit. Chrome drew nothing new between her tap and the sorted list. It timed the tap at 304 ms.

Ravi builds the shop's site. He reads its numbers in the Chrome User Experience Report, CrUX: Chrome's record of how real visits went (lesson 10.1). For how quickly a page answers, Chrome records one number for each visit: its Interaction to Next Paint, or INP.

So Ravi wonders. Asha's visit had many quick moments and one slow one. Which single number did it send? And why that one?

Here is her visit in three numbers, as Chrome counted them:

Asha's visit, as Chrome counted itPop out in the code editor (opens in a new tab)JavaScript
// Asha's visit to the tea list, as Chrome counted it.const visit = { events: 309, interactions: 21, slowest: 304 }; console.log(`Chrome timed ${visit.events} events.`);console.log(`They made ${visit.interactions} interactions.`);console.log(`The slowest took ${visit.slowest} ms. That's her INP.`);
  • Line 1 is a comment.
  • Line 2 holds three numbers. events is how many input events Chrome timed: every pointer, mouse, touch, key and text event. interactions is how many clicks, taps and key presses there were. slowest is the slowest interaction, in milliseconds (ms).
  • Lines 4 to 6 print Chrome timed 309 events., They made 21 interactions. and The slowest took 304 ms. That's her INP.

So the short answer is 304 ms: the tap on Sort by price. The long answer takes four steps, and each section below takes one.

  1. Which of her 15 actions count?
  2. How do 21 interactions become one number?
  3. Is 304 ms good?
  4. Why did the old metric, FID, never notice taps like this one?

A library gives a feel for the whole idea:

Real-life analogyQuestions at the library desk

You spend an afternoon at the library. Some of the time, you walk along the shelves and look at covers. Nobody times that. But each time you ask the librarian something, you wait for her to look up and say "one moment". Later, a friend asks how the help desk was. You don't count the browsing. You remember the question you waited longest for.

In real life: Asking the librarian a question
In INP: A click, a tap or a key press: an interaction
In real life: Her looking up to say "one moment"
In INP: The next frame: the page shows it heard you
In real life: Walking along the shelves, looking at covers
In INP: Scrolling and pointing: not interactions
In real life: The question you waited longest for
In INP: The visit's INP
The main idea

INP is one number for a whole visit: the slowest click, tap or key press, timed from the input to the next frame. On long visits, it skips one for every 50 interactions. Chrome records it for each visit. CrUX reports the 75th percentile of many visits: the value three in four visits were at or under. Good is 200 ms or less.

How the course recorded it

  • Asha's laptop: Chrome 157, in a window 412 by 480 pixels, with a touch screen. Playwright, a tool that drives a real browser, made every move. It used Chrome's own input: the mouse, the trackpad, taps, swipes and the keyboard.
  • The page: the tea list from lesson 25.1, with a search box, a Sort by price button and an Add button on every tea. Some of its event handlers, the code that runs when an event happens, are slow on purpose. Pointing at a tea's name runs 120 ms of work, each Add 90 ms, each letter in the search box 30 ms, and Sort by price 300 ms.
  • What the page measured: every event entry of 16 ms or more, and INP from the web-vitals library, version 6.2.3. Chrome also wrote a trace: a detailed record of everything it did, with screenshots. The trace lists every event Chrome timed, quick ones too.
  • A second, longer visit: on another day, Asha does the same 15 things. Then she writes a 50-letter note for Meera's gift card.

Clicks, taps and key presses

web.dev names exactly three kinds of interaction for INP:

  • clicking with a mouse;
  • tapping on a touch screen;
  • pressing a key, on a real keyboard or one on the screen.

Hovering, zooming and scrolling aren't on the list. How does Chrome tell them apart? With an ID. Lesson 11.4 showed that the events of one click, tap or key press share one interactionId. Every other event gets 0. Here is what five of Asha's actions left behind:

What five actions left behindPop out in the code editor (opens in a new tab)JavaScript
// Five things Asha did: [what she did, events Chrome timed, interactions].const actions = [  ["Point at Mint tea", 18, 0],  ["Swipe the list up", 26, 0],  ["Click Add on Green tea", 23, 1],  ["Tap Add on Mint tea", 29, 1],  ['Type "chai"', 22, 4],];for (const [action, events, interactions] of actions) {  console.log(`${action}: ${events} events, interactions: ${interactions}`);}
  • Lines 3 to 7 hold one action each: what she did, how many events Chrome timed, and how many interactions they made. The counts come from the trace.
  • Lines 9 to 11 print one line per action.
  • It prints Point at Mint tea: 18 events, interactions: 0 and Swipe the list up: 26 events, interactions: 0. Then come the click and the tap, with 1 each, and Type "chai": 22 events, interactions: 4.

Every action fired a crowd of events. Only some made interactions. Typing four letters made four: one per key press. Here is Asha's screen at the end of six actions:

Real frames of Asha's screen at the end of six of her actions. Not interactions: Point at Mint tea, no interaction; Scroll down with the trackpad, no interaction; Swipe the list up, no interaction. Interactions: Click Add on Green tea, 1 interaction: 104 ms; Tap Add on Mint tea, 1 interaction: 104 ms; Type "chai", 4 interactions.Not interactionsPoint at Mint teaThe tea list after: Point at Mint teano interactionScroll down with the trackpadThe tea list after: Scroll down with the trackpadno interactionSwipe the list upThe tea list after: Swipe the list upno interactionInteractionsClick Add on Green teaThe tea list after: Click Add on Green tea1 interaction: 104 msTap Add on Mint teaThe tea list after: Tap Add on Mint tea1 interaction: 104 msType "chai"The tea list after: Type "chai"4 interactions
Real frames from Asha's visit, each at the end of one action. Pointing, scrolling and swiping made no interaction. Each click, tap and key press made one. This demo's search box only times its work; it doesn't filter the list.

Pointing: slow, and still ignored

When Asha pointed at Mint tea, a line about it appeared. That handler does 120 ms of work, so Chrome timed its events at 136 ms. That's slower than every interaction of her visit but one. INP ignored it anyway, because all 18 events had interactionId 0.

Why it matters: a slow hover handler can still hurt. A tap that came during those 120 ms would have to wait. That wait would show in the tap's own time, not as the hover's.

Scrolling: no events, or a cancelled press

The trackpad scroll left no event of its own. The Event Timing standard, which defines these timings, leaves out wheel events on purpose. They fire again and again while you move, so it calls them continuous. The only events in those moments came from the resting mouse, as the page moved under it. web.dev gives one more reason to leave scrolling out. Browsers can often scroll on a separate thread, so the page's code doesn't slow it down.

The swipe is more interesting. A finger going down fires pointerdown, as a tap does. At that moment, Chrome can't know if it's a tap or a swipe. So the standard holds the pointerdown back. When the finger moves and the page starts to scroll, Chrome fires pointercancel. The held pointerdown then gets interactionId 0. It's like reaching for a doorbell, then walking on. That's exactly what Asha's two swipes did.

Two counters on the page agree. performance.eventCounts counts every event of each kind Event Timing watches. performance.interactionCount counts the interactions:

Chrome's own countsPop out in the code editor (opens in a new tab)JavaScript
// From the end of Asha's visit: performance.eventCounts and performance.interactionCount.const counts = { pointerdown: 7, pointercancel: 2, keydown: 16 };const interactionCount = 21; const presses = counts.pointerdown - counts.pointercancel;console.log(`Clicks and taps: ${presses}`);console.log(`Key presses: ${counts.keydown}`);console.log(`Together: ${presses + counts.keydown}. Chrome counted ${interactionCount}.`);
  • Line 2 holds three of Chrome's counts. Line 3 holds its count of interactions.
  • Line 5 takes the cancelled presses away from the presses: the two swipes weren't taps.
  • Lines 6 to 8 print Clicks and taps: 5, Key presses: 16 and Together: 21. Chrome counted 21.

One thing is missing from eventCounts: it has no entry for wheel or scroll at all. Chrome doesn't count them, because Event Timing doesn't time them.

Pick any of Asha's 15 actions to see every event Chrome timed for it:

Playground: which of Asha's actions made an interaction?
Point at Mint tea18 events
  • pointerover×2interactionId 0
  • pointerenter×8interactionId 0
  • mouseover×2interactionId 0
  • pointeroutinteractionId 0
  • pointerleave×4interactionId 0
  • mouseoutinteractionId 0
Try it yourself
No interaction

Every event here has interactionId 0, so INP ignores them all, even the slowest, which took 136 ms.

Every event Chrome 157 timed for each action, read from the recorded trace. Durations come from the page's own observer.

The next paint, not the whole job

An interaction's time stops at the next frame, not when all its work is done. web.dev says INP doesn't try to measure every effect of a click, like a fetch that finishes later. It measures how long the next paint was held up. That first frame is the page's "one moment", like the librarian looking up.

So a press counts even when nothing much changes. Asha's tap on the search box ran none of the page's code. Chrome still timed it, at 16 ms, as one of her 21. Lesson 23.3 found the other side of this: a tap that came before the page was listening did nothing, and still counted as 0 ms.

Taps inside an iframe, a page inside the page like a video player, count for Chrome's INP too. But the page's own JavaScript can't see them. So your own measurements can come out lower than CrUX's.

From many interactions to one value

Asha's visit had 21 interactions. Each one has a latency: the longest duration among its events, from the input to the next frame (lesson 11.4). Now pick one number for the whole visit.

Why not the average? Her average was about 54 ms. It hides the one tap that felt broken. web.dev wants a value that "all (or nearly all)" interactions were beneath. So INP takes the slowest.

On long visits, INP lets a few off: one for every 50 interactions. web.dev explains why. The more interactions a visit has, the more likely a random hiccup becomes. One bad moment shouldn't decide a long, smooth visit. Skipping one in 50 is close to taking the 98th percentile: a value about 98 in 100 interactions were at or under.

  • Fewer than 50 interactions: INP is the slowest.
  • 50 to 99: the second slowest.
  • 100 to 149: the third slowest, and so on.

To pick the slowest, you don't need to remember every interaction. An arcade game shows how:

Real-life analogyA high-score table with ten rows

An old arcade game keeps a table of the ten best scores, highest first. A new game only gets on if it beats the lowest score there. Every other game is forgotten. But the machine still counts every game played, even the forgotten ones.

In real life: The ten rows of the table
In INP: The ten slowest interactions web-vitals keeps
In real life: A new game that beats the lowest row
In INP: A new interaction slower than the tenth
In real life: The counter of games played
In INP: performance.interactionCount
In real life: Reading a row from the top
In INP: Picking INP: row 1, or lower on a long visit

The web-vitals library, a small script from Chrome's team that measures these metrics on real visits, does exactly that. Its file InteractionManager.ts, in version 6.2.3, works like this:

  • It keeps at most ten interactions, the slowest ones. The limit is called MAX_INTERACTIONS_TO_CONSIDER.
  • It counts every interaction with performance.interactionCount, quick ones too.
  • It picks the row at Math.floor(count / 50), counting from 0. If the list is shorter, it takes the last row.
  • It only listens for events of 40 ms or more. It also listens for the first-input entry: Chrome's copy of the first interaction's first event, sent however quick it was. That way, a visit with only quick interactions still gets a value.

Here is that rule, run on web-vitals' lists from both of Asha's visits:

web-vitals picks one valuePop out in the code editor (opens in a new tab)JavaScript
// web-vitals' list of slowest interactions for each visit, slowest first, in ms.const firstVisit = { interactions: 21, slowest: [304, 104, 104, 96, 40] };const longVisit = { interactions: 72, slowest: [304, 96, 96, 96] }; const inp = (visit) => {  const skip = Math.floor(visit.interactions / 50);  return visit.slowest[Math.min(skip, visit.slowest.length - 1)];};console.log(`First visit: INP ${inp(firstVisit)} ms`);console.log(`Long visit: INP ${inp(longVisit)} ms`);
  • Line 2 is the first visit: 21 interactions, and the five of them that reached 40 ms, slowest first.
  • Line 3 is the long visit: 72 interactions. Only four reached 40 ms. The 50 letters of the note were all quicker.
  • Line 6 works out how many to skip: one for every full 50.
  • Line 7 takes the row after the skipped ones, or the last row.
  • Lines 9 and 10 print First visit: INP 304 ms and Long visit: INP 96 ms.
web-vitals' list of slowest interactions for each of Asha's visits. First visit: 21 interactions: Sort by price 304 ms, Add, Green tea 104 ms, Add, Mint tea 104 ms, Add, Earl Grey 96 ms, The c key 40 ms. It skips 0, so INP is 304 ms. Long visit: 72 interactions: Sort by price 304 ms, Add, Green tea 96 ms, Add, Mint tea 96 ms, Add, Earl Grey 96 ms. It skips 1, so INP is 96 ms.First visit: 21 interactions1. Sort by price 304 msINP2. Add, Green tea 104 ms3. Add, Mint tea 104 ms4. Add, Earl Grey 96 ms5. The c key 40 ms21 ÷ 50 → skip 0INP: 304 msLong visit: 72 interactions1. Sort by price 304 ms2. Add, Green tea 96 msINP3. Add, Mint tea 96 ms4. Add, Earl Grey 96 ms72 ÷ 50 → skip 1INP: 96 ms
web-vitals' list for each visit, slowest first. With 21 interactions, nothing is skipped. With 72, the slowest is skipped, and INP drops to the next one.

On the long visit, the slow tap was still there, and still 304 ms. The 50 quick letters bought it one skip. Why it matters: a long visit forgives one slow moment. A short visit forgives nothing.

When did web-vitals settle on 96 ms? It sends a new report each time INP changes. Here are its reports from the long visit:

INP changes during the visitPop out in the code editor (opens in a new tab)JavaScript
// web-vitals' reports from the long visit: [ms since the page started, INP in ms, interactions so far].const reports = [[5101, 96, 1], [15856, 304, 20], [28356, 96, 72]]; for (const [at, inp, count] of reports) {  console.log(`At ${at} ms, after interaction ${count}: INP ${inp} ms`);}
  • Line 2 holds three reports: when each came, the INP it sent, and how many interactions there had been.
  • It prints At 5101 ms, after interaction 1: INP 96 ms and At 15856 ms, after interaction 20: INP 304 ms. Then At 28356 ms, after interaction 72: INP 96 ms.

The first report came after her first click. The second came after the slow tap, her 20th interaction. The third came only when she left, as the tea list was hidden. By then she had made 72, so the slow tap was skipped. Why it matters: INP isn't final until the visit ends.

Chrome doesn't use web-vitals for its own records. It works out INP in its own code, and uses the same rule. That code also keeps the ten longest interactions, and skips one for every 50.

Try the rule yourself. The list holds Asha's ten slowest interactions. Pretend she made more quick ones, and watch which row becomes INP:

Playground: one value from many interactions
The ten slowest, slowest firstskip 0
  1. Sort by price304 ms
  2. Add, Green tea104 ms
  3. Add, Mint tea104 ms
  4. Add, Earl Grey96 ms
  5. The c key40 ms
  6. The h key32 ms
  7. The a key32 ms
  8. The i key32 ms
  9. Delete it, one key at a time32 ms
  10. Delete it, one key at a time32 ms
Try it yourself
INP: 304 ms, needs improvement

21 ÷ 50 rounds down to 0, so nothing is skipped. INP is the slowest.

Asha's ten slowest interactions from her first visit, as the page's own 16 ms observer saw them. Only the number of interactions changes: pretend she made more quick ones.

The 200 ms line

Asha's first visit had an INP of 304 ms. Is that good? web-vitals rates INP against the two lines from lesson 3.1:

Good, needs improvement, or poorPop out in the code editor (opens in a new tab)JavaScript
// web-vitals' ratings for INP: good up to 200 ms, poor over 500 ms.const rate = (inp) => (inp > 500 ? "poor" : inp > 200 ? "needs improvement" : "good"); console.log(`First visit, 304 ms: ${rate(304)}`);console.log(`Long visit, 96 ms: ${rate(96)}`);console.log(`Exactly 200 ms: ${rate(200)}`);
  • Line 2 checks for poor first: over 500 ms. Then needs improvement: over 200 ms. Anything else is good.
  • Lines 4 to 6 print First visit, 304 ms: needs improvement, Long visit, 96 ms: good and Exactly 200 ms: good.

Why 200 ms?

Lesson 2.1 told the story of a light switch. When the light comes on within about 100 ms, it feels like you turned it on. Research on cause and effect says the same. In a classic study, people watched one shape bump into another. When the second moved within about 100 ms, they saw the first one cause it. Between 100 and 200 ms, they weren't sure. After 200 ms, the link was gone.

So the research points at 100 ms. But a line only helps if sites can reach it. When Chrome's team chose INP's lines, they checked CrUX. At 100 ms, only 12% of sites on phones would have been good. At 200 ms, 56% would. They chose 200 ms: near the research, and within reach.

The poor line got the same check. At 300 ms, more than half of the 10,000 most popular sites would have been poor on phones. At 500 ms, about a quarter would. So poor starts above 500 ms.

Real-life analogyA bus that counts as on time

Passengers would love every bus to arrive to the second. In city traffic, no bus company can promise that. So the company sets a rule its routes can keep. A bus up to two minutes late counts as on time. More than five minutes late counts as very late.

In real life: Arriving to the second
In INP: The 100 ms the research points to
In real life: Up to two minutes late: on time
In INP: Up to 200 ms: good
In real life: More than five minutes late: very late
In INP: Over 500 ms: poor
In real life: A rule most routes can keep
In INP: A line many sites could already reach

From one visit to the whole site

Asha's visit is one of many. Chrome records one INP per visit, and CrUX gathers them. It reports the 75th percentile: the value three in four visits were at or under. Lesson 10.1 called it the survey's headline. web.dev splits it by phones and desktops, too.

So here is Ravi's answer. Asha's visit sent 304 ms, which needs improvement. Whether the whole site is good depends on the other visits. Three in four of them need an INP of 200 ms or less.

Now feel the lines yourself. The button below runs a real handler for as long as you choose. Your own browser times each press, the way Chrome timed Asha's. Compare 150 ms of work with 250 ms. Does the second feel like a pause?

Playground: feel the 200 ms line
What this panel runs, shortenedJavaScript
// What the panel's button runs, and how it times your click.button.addEventListener("click", () => busy(work)); new PerformanceObserver((list) => {  for (const entry of list.getEntries()) {    if (entry.target === button && entry.interactionId) show(entry.duration, rate(entry.duration));  }}).observe({ type: "event", durationThreshold: 16 });
Cart: 00 timed
  • No presses timed yet.
Try it yourself
Good, needs improvement, or poor?

Pick how long the button's handler works, then press Add to cart. Your browser times the interaction, from your press to its next frame.

Real times from your own browser, rounded to 8 ms. The handler is a real loop that keeps the main thread busy.

Why FID wasn't enough

Before INP, Chrome's responsiveness metric was First Input Delay, or FID. web.dev defines it as the time from a person's first interaction until the browser can start running its event handlers. Good was 100 ms or less.

In code, FID is one subtraction on the first-input entry from lesson 11.4. Here it is for Asha's first visit:

FID and INP, same visitPop out in the code editor (opens in a new tab)JavaScript
// Asha's first input: the pointerdown of her click on Add, Green tea. Times in ms.const firstInput = { startTime: 4363.1, processingStart: 4365.9 };const inp = 304; const fid = firstInput.processingStart - firstInput.startTime;console.log(`FID: ${fid.toFixed(1)} ms`);console.log(`INP: ${inp} ms`);
  • Line 2 holds two times from the first-input entry: the pointerdown of her click on Add, Green tea.
  • Line 3 is her INP.
  • Line 5 subtracts: the wait before her first click's handlers could start.
  • Lines 6 and 7 print FID: 2.8 ms and INP: 304 ms.

By FID, Asha's visit was fast. By INP, it needs improvement. The picture shows what each one looked at:

Two of Asha's interactions, to scale. Her first click, on Add for Green tea, took 104 ms, and FID timed only its first 2.8 ms: the wait before its handler started. Her tap on Sort by price took 304 ms, past the 200 ms line. That's her INP.200 ms: the good lineHer first clickAdd, Green tea104 msFID stops here: 2.8 msHer slowest tapSort by priceINP: 304 ms0100 ms200 ms300 ms
Bars to scale. FID timed only the first 2.8 ms of her first click. INP timed every interaction to its next frame, and kept the slowest.

Chrome's team pointed out that both gaps are in FID's name: "first input" and "delay".

  • First: FID only looked at her first click. It never saw the tap on Sort by price, her 20th interaction.
  • Delay: even for that first click, it stopped as the handler started. The click took 104 ms to show the new cart count. FID saw the first 2.8 ms of it.
Real-life analogyA hotel survey with one question

A hotel asks every guest one question: at check-in, how long until the receptionist looked up? Most guests say "straight away", so the hotel scores well. Nobody asks how long the check-in itself took. Nobody asks about the room service that took an hour on the third night.

In real life: How long until the receptionist looked up
In INP: Input delay: until the handlers start
In real life: Asking about check-in only
In INP: Timing the first input only
In real life: The check-in itself
In INP: Processing and drawing: the rest of the wait
In real life: Room service on the third night
In INP: Later interactions, like the tap on Sort by price

Why was FID built that way? web.dev says the first impression shapes how people judge a site. And the worst delays often came while a page was still loading. But web.dev also says 90% of a person's time on a page comes after it loads. FID looked at just one moment of a visit: the first input.

The numbers show how much it missed. When Chrome announced the change in 2023, 93% of sites had good FID on phones. Only 65% had good INP.

INP replaced FID as a Core Web Vital, one of the three metrics web.dev asks every site to measure, on March 12, 2024. Chrome's tools stopped supporting FID on September 9, 2024, and CrUX dropped it that month. web-vitals 5 removed its onFID() function. Lesson 2.3 has the whole road in a table.

Read this table across: each row asks one question of both metrics.

FID and INP, side by side
QuestionFirst Input Delay (FID)Interaction to Next Paint (INP)
Which inputs?Only the first click, tap or key press.Every click, tap and key press of the visit.
Which part of the wait?Only the input delay, until the handlers start.All of it, until the next frame is painted.
Which number for the visit?The first input's delay.The slowest interaction, skipping one for every 50.
Good is100 ms or less.200 ms or less.
Asha's first visit2.8 ms: good.304 ms: needs improvement.
StatusRetired. Chrome's tools stopped supporting it on September 9, 2024.A Core Web Vital since March 12, 2024.

Does it count toward INP?

Each action below happens on the tea list. Sort it: does it make an interaction that INP counts?

Does it count toward INP?
  • Clicking Add with a mouse
  • Tapping Sort by price on a touch screen
  • Pressing Backspace in the search box
  • Tapping the search box, which runs none of the page's code
  • Pointing at a tea's name to read about it
  • Scrolling the list with a trackpad
  • Swiping the list with a finger
  • A script calling button.click()
Try it yourself
0 of 8 correct

Sort each action: an interaction INP counts, or not an interaction.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "INP is the average interaction." No. It's the slowest, skipping one for every 50. Asha's average was about 54 ms, and her INP was 304 ms.
  • "Anything slow on the page raises INP." No. Only clicks, taps and key presses count. Her hover took 136 ms and didn't count.
  • "A slow, jerky scroll makes INP worse." No. Scrolling isn't an interaction. A swipe's press is cancelled, and wheel events aren't timed at all.
  • "INP waits until the work is done." No. It stops at the next frame. A spinner shown at once ends the wait, even if a fetch takes longer.
  • "web-vitals only counts the interactions it hears about." No. It only hears about the slow ones, but it counts every interaction with performance.interactionCount. The long visit had 72.
  • "A good FID meant a responsive page." No. Asha's FID was 2.8 ms. Her INP was 304 ms.
  • "A visit with no clicks has an INP of 0." No. It has no INP at all. web.dev says pages where people only scroll or hover report no value.

Practice exercises

Answer first, then use the hints.

Exercise 1 · Warm-upA busier visit

The same list came from a visit with 120 interactions. What does it print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
// web-vitals' list for a busier visit, slowest first, in ms.
const busyVisit = { interactions: 120, slowest: [304, 104, 104, 96, 40] };

const inp = (visit) => {
  const skip = Math.floor(visit.interactions / 50);
  return visit.slowest[Math.min(skip, visit.slowest.length - 1)];
};
console.log(`Busy visit: INP ${inp(busyVisit)} ms`);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upRight on the line

    What does this print for an INP of exactly 500 ms?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    // web-vitals' ratings for INP: good up to 200 ms, poor over 500 ms.
    const rate = (inp) => (inp > 500 ? "poor" : inp > 200 ? "needs improvement" : "good");
    
    console.log(`Exactly 500 ms: ${rate(500)}`);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeFive hundred interactions

      In the one-value playground, set 500 interactions. Which latency becomes INP?

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeEvents behind one tap

        How many events did Chrome time when Asha tapped Sort by price?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeThe red triangle

          Open the course's trace of Asha's first visit in Chrome's DevTools. What does DevTools call the interaction that has the red triangle?

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeChrome's own fifty

            In Chrome's own INP code, what is the constant that holds the 50 called?

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, ask which actions count, how many interactions there were, and which line the value crosses.

              How INP works quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What does the web-vitals pick example print on its first line?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // web-vitals' list of slowest interactions for each visit, slowest first, in ms.
                const firstVisit = { interactions: 21, slowest: [304, 104, 104, 96, 40] };
                const longVisit = { interactions: 72, slowest: [304, 96, 96, 96] };
                
                const inp = (visit) => {
                  const skip = Math.floor(visit.interactions / 50);
                  return visit.slowest[Math.min(skip, visit.slowest.length - 1)];
                };
                console.log(`First visit: INP ${inp(firstVisit)} ms`);
                console.log(`Long visit: INP ${inp(longVisit)} ms`);

                Choose an answer to see the explanation.

              2. Question 2 of 7Asha points at Mint tea, and the page's hover handler keeps the main thread busy for 120 ms. What does INP do with it?

                Choose an answer to see the explanation.

              3. Question 3 of 7A visit has 130 interactions. Which one is its INP?

                Choose an answer to see the explanation.

              4. Question 4 of 7Research says about 100 ms feels instant. Why is INP's good line 200 ms?

                Choose an answer to see the explanation.

              5. Question 5 of 7What does the rating example print on its last line?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // web-vitals' ratings for INP: good up to 200 ms, poor over 500 ms.
                const rate = (inp) => (inp > 500 ? "poor" : inp > 200 ? "needs improvement" : "good");
                
                console.log(`First visit, 304 ms: ${rate(304)}`);
                console.log(`Long visit, 96 ms: ${rate(96)}`);
                console.log(`Exactly 200 ms: ${rate(200)}`);

                Choose an answer to see the explanation.

              6. Question 6 of 7What does this FID example print, for the long visit's first input?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // Asha's first input: the pointerdown of her click on Add, Green tea. Times in ms.
                const firstInput = { startTime: 4304.3, processingStart: 4304.4 };
                const inp = 96;
                
                const fid = firstInput.processingStart - firstInput.startTime;
                console.log(`FID: ${fid.toFixed(1)} ms`);
                console.log(`INP: ${inp} ms`);

                Choose an answer to see the explanation.

              7. Question 7 of 7CrUX says a site's INP is 180 ms. What does that tell you?

                Choose an answer to see the explanation.

              Key takeaways

              • Only clicks, taps and key presses are interactions. Pointing, scrolling, swiping and zooming aren't.
              • An interaction lasts from the input to the next frame, not until all its work is done.
              • A visit's INP is its slowest interaction, skipping one for every 50. web-vitals and Chrome keep the ten slowest, and count them all.
              • Good is 200 ms or less, poor is over 500 ms, at the 75th percentile of a site's visits.
              • FID timed only the first input's wait. Asha's FID was 2.8 ms while her INP was 304 ms.

              Remember the one-liner.
              Count the clicks, taps and key presses, time each to its next frame, and keep the slowest.

              Sources

              The facts on this page come from these specs, docs and source files:

              Coming next: The three phases of an interaction. Asha's slow tap took 304 ms. The next lesson splits a slow interaction into its three parts, to find which one to fix.

              CompleteFrontend Clear concepts. Working examples.