LEARNING OBJECTIVES ⌵
- Understand why Interaction to Next Paint (INP) officially superseded First Input Delay (FID) as a Core Web Vital.
- Identify all user input types tracked by INP (clicks, taps, keypresses) vs. compositor-driven gestures (scrolls, hovers).
- Master the 3 sub-parts of interaction latency: Input Delay, Processing Duration, and Presentation Delay.
- Learn how browsers calculate session-level INP across short vs. long user journeys (the 98th percentile outlier rule).
- Instrument and debug interaction latencies using the Event Timing API and
PerformanceObserver.
📖 The Mental Model & Story (Intuitive Foundation)
Imagine typing a message on your smartphone's virtual keyboard.
Under the legacy First Input Delay (FID) metric, the browser only timed how long it took between when your finger touched the very first letter "H" and when the keyboard driver woke up. If that first letter woke up promptly in $10\text{ ms}$, the site was awarded a gold medal—even if every subsequent letter you typed took $800\text{ ms}$ to appear on the screen, causing stutter and skipped characters!
LEGACY FID (First Input Delay)
Finger Taps 'H' ──► [ Input Delay ] ──► (Stop Tracking! FID measured here)
[ ... The rest of the entire user session is IGNORED ... ]
MODERN INP (Interaction to Next Paint)
Finger Taps 'H' ──► [ Input Delay ] ──► [ Execute JS Callback ] ──► [ Render Next Frame ] (Total: 45ms)
Finger Taps 'E' ──► [ Input Delay ] ──► [ Execute JS Callback ] ──► [ Render Next Frame ] (Total: 38ms)
Finger Taps 'L' ──► [ Input Delay ] ──► [ Heavy Background JS ] ──► [ Render Next Frame ] (Total: 480ms ⚠️ WORST INP!)
Interaction to Next Paint (INP) represents the modern standard: It monitors every single click, tap, and keypress across the entire lifecycle of a page visit. When the user leaves the page, INP reports the single representative worst-case interaction latency.
If an interface freezes, lags, or stutters at any point during the user's journey, INP captures it.
Technical Deep Dive & Specifications
What Interactions Count Toward INP?
The Event Timing API tracks discrete user interactions requiring JavaScript event dispatch and UI rendering:
| Interaction Category | Eligible Events Tracked | Excluded Gestures & Reasons |
|---|---|---|
| Mouse Clicks | pointerdown, pointerup, click |
Continuous mousemove (hovering) is excluded to avoid telemetry flooding. |
| Touchscreen Taps | touchstart, pointerdown, touchend |
Pinch-to-zoom and continuous touch panning are offloaded to the compositor thread. |
| Keyboard Keystrokes | keydown, keyup, keypress |
Keystroke combinations (Ctrl+C, Alt+Tab) that trigger OS native shortcuts without browser paint. |
+-------------------------------------------------------------------------------+
| INP INTERACTION ELIGIBILITY |
+-------------------------------------------------------------------------------+
| ELIGIBLE FOR INP: |
| ├── Mouse clicking a button, link, or custom interactive widget |
| ├── Tapping on a mobile touchscreen element |
| └── Pressing keys inside a text input, textarea, or keyboard-navigated UI |
| |
| EXCLUDED FROM INP: |
| ├── Scrolling or swiping a long feed (handled by GPU compositor thread) |
| ├── Mouse hover / pointer movements across screen elements |
| └── Pinching, zooming, or OS window resizing |
+-------------------------------------------------------------------------------+
The 3 Sub-Parts of Interaction Latency
An interaction begins the microsecond the user physically touches hardware and concludes only when the browser paints the resulting pixels to the screen. This journey is divided into three sequential sub-parts:
User Action Display Updated
(Finger Tap) (Pixels Rendered)
│ ▲
▼ │
┌───────────────────────┬───────────────────────────────┬───────────────────────────────┐
│ 1. INPUT DELAY │ 2. PROCESSING DURATION │ 3. PRESENTATION DELAY │
│ (Wait on Main Thread) │ (Execute JavaScript Handlers) │ (Style + Layout + Paint + GPU)│
└───────────────────────┴───────────────────────────────┴───────────────────────────────┘
1. Input Delay ($T_{\text{input}}$)
The time between the physical user action and when the browser's main thread is actually free to begin executing the first registered event handler callback.
- Root Cause: Long-running tasks, heavy garbage collection, or background third-party analytics running on the main thread when the user interacts.
2. Processing Duration ($T_{\text{process}}$)
The cumulative execution time spent running all synchronous JavaScript event listener callbacks associated with that interaction (pointerdown $\rightarrow$ pointerup $\rightarrow$ click).
- Root Cause: Complex client-side algorithms, synchronous DOM queries, large array filtering, or heavy state management updates.
3. Presentation Delay ($T_{\text{present}}$)
The time between when JavaScript execution finishes and when the browser calculates style recalculation, layout reflow, rasterization, and commits the composite frame to the GPU display buffer.
- Root Cause: Massive DOM size, recalculating styles across thousands of nodes, forced synchronous layouts, or complex CSS filter repaints.
$$\text{Total Interaction Latency} = T_{\text{input}} + T_{\text{process}} + T_{\text{present}}$$
The Session-Level INP Calculation Algorithm
How does the browser pick a single INP number for a session with hundreds of clicks?
Low-Interaction Sessions ($< 50$ interactions): $$\text{Session INP} = \max(\text{All Interaction Latencies})$$ The absolute highest latency observed during the visit is the INP.
High-Interaction Sessions ($\ge 50$ interactions): To prevent unusual, single-frame OS hardware hiccups from skewing telemetry, the algorithm discards 1 outlier per every 50 interactions. $$\text{INP Value} = 98\text{th Percentile of Interactions}$$
- For example, if a user performs $100$ interactions, the worst $1$ is ignored, and the $2\text{nd}$ worst interaction becomes the reported INP.
Total Interactions: 100
[ 12ms, 15ms, 18ms, ... 180ms, 195ms ] [ 210ms (98th % - INP) ] [ 950ms (Discarded Outlier) ]
▲
│
REPORTED SESSION INP
💻 Interactive Code Playground
Starter Code
Line-by-Line Code Breakdown
- Lines 55–60 (
slow-btnhandler): Uses a synchronouswhileloop to hold the main thread hostage for $250\text{ ms}$, creating an immediate $250\text{ ms}+$ Processing Duration bottleneck. - Lines 63–87 (
PerformanceObserver): Observes entries of type'event'. - Line 66 (
if (!entry.interactionId) continue;): Filters specifically for interactions assigned aninteractionIdby the Chromium engine (distinguishing key/pointer events from passive lifecycle events). - Lines 68–71 (Sub-part arithmetic):
inputDelay = entry.processingStart - entry.startTimeprocessingTime = entry.processingEnd - entry.processingStartpresentationDelay = entry.startTime + entry.duration - entry.processingEnd
- Line 87 (
durationThreshold: 16): Captures all interactions exceeding $16\text{ ms}$ (one display refresh frame).
Expected Browser Render Output
[Interaction Captured: pointerdown]
• Target Node: <BUTTON> (ID: #slow-btn)
• Total Duration: 268.4 ms (FAIL >200ms)
├── 1. Input Delay: 2.1 ms
├── 2. Processing Time: 251.2 ms
└── 3. Presentation Delay: 15.1 ms🏋️ Hands-On Exercise
🎯 The Challenge: Diagnose and Triage INP Sub-Part Bottlenecks
Instructions:
- Review the performance audit data for three different failing user interactions:
- Interaction A (Mobile Menu Toggle): Total: $380\text{ ms}$ (Input Delay: $310\text{ ms}$, Processing: $20\text{ ms}$, Presentation: $50\text{ ms}$)
- Interaction B (Filter Dropdown Change): Total: $420\text{ ms}$ (Input Delay: $10\text{ ms}$, Processing: $380\text{ ms}$, Presentation: $30\text{ ms}$)
- Interaction C (Accordion Expand): Total: $310\text{ ms}$ (Input Delay: $15\text{ ms}$, Processing: $25\text{ ms}$, Presentation: $270\text{ ms}$)
- For each interaction, identify the exact sub-part that is bottlenecking the interaction and specify the architectural engineering remedy.
- Write a JavaScript helper
analyzeInteraction(entry)that automatically flags which sub-part exceeded its target budget ($T_{\text{input}} > 50\text{ ms}$, $T_{\text{process}} > 50\text{ ms}$, $T_{\text{present}} > 50\text{ ms}$).
🏁 Starter Code Sandbox
⚠️ Common Pitfalls
- Testing Only Mouse Clicks and Ignoring Keypresses: INP tracks keyboard events inside forms. A complex autocomplete search box that parses arrays on every
keydownwithout yielding will fail INP even if all buttons are lightning fast. - Assuming
setTimeout(fn, 0)Breaks Up Processing Time: CallingsetTimeout(fn, 0)queues a macro-task, but if you do not yield to the renderer before mutating the DOM, the presentation delay will still absorb the cost. - Overlooking Third-Party Tag Manager Scripts: Heavy third-party advertising, analytics, and chat widget scripts running continuous loops inflate Input Delay for unrelated native UI buttons.
💡 Pro Tips
- Leverage the
interactionIdin Performance Logs: Chromium assigns a unique integerinteractionIdgrouping together related events (such aspointerdown,pointerup, andclick) from a single user physical gesture. - Target Sub-50ms Budgets per Sub-Part: To comfortably pass the $\le 200\text{ ms}$ threshold in the 75th percentile of real-world low-end mobile devices, design each sub-part ($T_{\text{input}}$, $T_{\text{process}}$, $T_{\text{present}}$) to take under $50\text{ ms}$.
📌 Key Takeaways
- INP measures the full responsiveness lifecycle ($\le 200\text{ ms}$) across all clicks, taps, and keystrokes throughout the entire page visit.
- Unlike FID (which only measured initial input delay), INP spans Input Delay + Processing Duration + Presentation Delay.
- Session INP reports the worst-case interaction for sessions with $<50$ events, or the 98th percentile for sessions with $\ge 50$ events.
- Continuous gestures like scrolling and mouse movements are handled on the compositor thread and excluded from INP.
- Resolving INP requires pinpointing which specific sub-part is responsible for the latency spike.
- --