Core Web Vitals INP Debugger

Audit Interaction to Next Paint (INP) bottlenecks by simulating main-thread blocking and layout thrashing.

100% Client-Side Execution. Your JavaScript code never leaves your machine. All profiling occurs inside an isolated Blob Web Worker in your browser's RAM — no server requests, no telemetry, no data logging. Your code is completely private.

Execution Payload

Paste a JavaScript event handler. The profiler executes it in an isolated Web Worker and measures real hardware latency.

Quick Examples
Analysis Mode
INP Thresholds (CWV 2024)
≤ 200msGood ✓
201ms – 500msNeeds Improvement
> 500msPoor ✗ SEO Penalty
Session INP History 0 runs

V8 Profiler Output

V8 Execution Time
0ms
Awaiting Web Worker...
Total INP Score
0ms
Input + Process + Render
CWV Status
AWAITING
Run analysis to begin
Main Thread Interaction Breakdown Target Frame Budget: 16.6ms | INP Threshold: 200ms
200ms — Good
500ms — Poor
Input Delay (0ms)
V8 Execution (0ms)
Render Delay (0ms)
PhaseDurationDescriptionStatus
Run analysis to see phase breakdown...
Methodology: Your code executes in a sandboxed Blob Web Worker, measured by performance.now() for true hardware latency. Input Delay (~40ms) and Render Delay (~16ms) are simulated based on Chrome's P75 baseline to produce a realistic estimated INP score. Enable Multi-Run for statistical averaging.
Run analysis to detect AST anti-patterns and layout thrashing.

Long Animation Frames (LoAF) API — Chrome 123+ exposes the exact breakdown of long rendering tasks. Based on your execution data, this is what the LoAF PerformanceObserverEntry JSON trace would look like in production DevTools.

Animation Frame (total duration) 0ms
blockingDuration (ms beyond 50ms threshold) 0ms
PerformanceObserverEntry entryType: "long-animation-frame"
{ "duration": 0, "renderStart": 0, "styleAndLayoutStart": 0, "firstUIEventTimestamp": 0, "blockingDuration": 0, "scripts": [] }
Production LoAF Observer Code
const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { // entry.duration > 50ms = Long Animation Frame // entry.scripts[] = culprit functions with source URLs console.log('LoAF:', entry.toJSON()); } }); observer.observe({ type: 'long-animation-frame', buffered: true });
No structural fixes recommended yet. Run an analysis first.

1INP vs. FID: The 2024 Core Web Vitals Revolution

In March 2024, Google permanently retired First Input Delay (FID) from its Core Web Vitals program and replaced it with Interaction to Next Paint (INP). This was one of the most significant algorithmic ranking signal updates in years. Understanding why this change happened requires a deep understanding of FID's fundamental measurement flaws.

FID's Fatal Flaw: FID only measured the browser's latency to begin processing the very first click on a page — a pure "hardware interrupt delay" metric. It measured nothing about JavaScript processing time or the final screen repaint. A page could pass FID with flying colors while still delivering catastrophic UX on all subsequent interactions.

INP's Total-Lifecycle Model: INP measures the complete end-to-end latency of every user interaction across the entire page session. For each click, keypress, or tap, INP captures all three critical phases:

  • Input Delay: Time from hardware interrupt to browser event dispatch.
  • Processing Time: All JavaScript event handlers executing synchronously.
  • Presentation Delay: Browser rendering pipeline time to final pixel paint.
MetricMeasuresScopeStatus
FID (retired)Input Delay only — first interactionFirst click onlyRetired Mar 2024
INP (active)Input + Processing + Render — all interactionsFull page lifecycle, P98Active Ranking Signal

Google reports a page's INP as the worst-case interaction at the 98th percentile from real CrUX user sessions. A single catastrophically slow interaction — even rare — can fail your entire INP score.

2The Three INP Phases: A Hardware-Level Breakdown

Google's INP specification divides the interaction timeline into three discrete, measurable phases. Our profiler tool visualizes all three simultaneously. Optimizing INP requires understanding and attacking each phase independently.

Phase 1: Input Delay

Input Delay begins the moment a user performs a physical action (touch, mouse click) and ends when the browser's event loop dispatches the event. It is almost entirely determined by what is currently blocking the Main Thread. If V8 is executing a 300ms Long Task when a user clicks, that click waits 300ms — entirely Input Delay. Primary causes: third-party analytics, ad scripts, synchronous import() hydration chains, and heavy CSS parsing. Our profiler uses a baseline ~40ms Input Delay matching Chrome's P75 measurement for typical applications.

Phase 2: Processing Time (V8 Execution)

This is the phase directly measured by our Blob Web Worker profiler. Processing Time encompasses all JavaScript that runs synchronously in response to the user's interaction. Heavy array operations (.filter(), .map() on 50k+ items), deep object cloning (JSON.parse(JSON.stringify())), and synchronous API calls all directly inflate this phase. The Web Worker isolates your code from browser scheduling jitter, giving you a near-perfect measurement of pure V8 execution cost.

Phase 3: Presentation Delay (Render Pipeline)

After JavaScript completes, the browser re-enters the full rendering pipeline: Style Recalculation → Layout → Paint → Composite → GPU Upload → Frame Scan-Out. Any DOM mutations during Processing Time trigger this pipeline. If you read and write geometry properties in a loop (Layout Thrashing), the browser runs Style Recalculation and Layout synchronously inside your JavaScript, massively inflating both Processing Time and Presentation Delay simultaneously. A single Layout Thrash loop can add 100–500ms+ of Presentation Delay.

Golden Rule: The sum of all three phases must be ≤ 200ms for a GOOD INP score. Budget each phase: Input Delay ≤ 40ms, Processing ≤ 100ms, Render ≤ 60ms.

3The LoAF API: Google's Most Powerful INP Debugging Primitive

For years, developers used the Long Tasks API ({ type: 'longtask' }) to identify INP problems. But Long Tasks had a critical flaw: it only measured JavaScript execution and completely ignored the rendering phase. Since INP measures time to "Next Paint," Long Tasks was fundamentally broken as an INP debugging tool.

In Chrome 123 (March 2024), Google shipped the Long Animation Frames API (LoAF) ({ type: 'long-animation-frame' }). A LoAF entry captures the complete frame story:

  • duration: Total wall-clock time from frame start to frame end, including rendering. Maps almost directly to INP.
  • blockingDuration: Portion of duration exceeding the 50ms Long Task threshold.
  • renderStart: Timestamp when the browser began its style and layout pass.
  • styleAndLayoutStart: When expensive CSS Box Model recalculation began — the key Layout Thrashing signal.
  • scripts[]: Attribution array with exact function name, source URL, line/column number, and duration for every script that ran. This is the data you need to write a precise bug ticket.
Our LoAF tab simulates what this JSON payload would look like in production based on your execution data, training you to recognize these signatures in real Chrome DevTools Performance panel sessions.

4Layout Thrashing: The Silent INP Killer

Layout Thrashing occurs when JavaScript alternates between reading and writing geometry-dependent DOM properties inside a loop, forcing the browser to synchronously recalculate CSS layout on every iteration. The culprit properties that trigger a Forced Synchronous Layout (FSL) include: offsetWidth, offsetHeight, clientWidth, clientHeight, scrollTop, scrollLeft, getBoundingClientRect(), and getComputedStyle().

The Thrashing Pattern (100+ reflows/frame)

StepCodeBrowser Cost
Iteration 1 — Readel.offsetHeightForces Style + Layout pass (≈2–10ms)
Iteration 1 — Writeel.style.height = 'X'Invalidates layout tree
Iteration 2 — Readel.offsetHeightForces ANOTHER Style + Layout (≈2–10ms)
Iteration N…RepeatN × layout cost = 200–2000ms total

The Fix: Batch-Read → requestAnimationFrame → Batch-Write

The solution is to separate all DOM reads into a single batch (allowing one layout pass), then schedule all DOM writes inside requestAnimationFrame() after the read phase completes. The FastDOM library automates this pattern. For virtualized lists, switch to CSS content-visibility: auto to skip layout for off-screen items entirely — a zero-JavaScript fix that reduces layout work by 60–90% on long pages.

5V8 JIT Compilation & JavaScript Parse Cost

Google's V8 JavaScript engine uses a multi-tier compilation pipeline that significantly impacts INP Processing Time. Understanding these tiers is essential for production performance budgeting.

V8 TierNameSpeedWarmup
Tier 0Ignition (Bytecode Interpreter)SlowestInstant — first call
Tier 1Sparkplug (Baseline JIT)Fast~100 function calls
Tier 2Maglev (Mid-Tier JIT)Very Fast~1,000 calls
Tier 3TurboFan (Optimizing JIT)Fastest (~30% C++ speed)~10,000+ calls

Deoptimization (DeoOpt): If V8 detects that a TurboFan assumption is wrong (e.g., an object changes shape — adding a new property), it deoptimizes back to Ignition. This is an invisible, catastrophic performance cliff. Our profiler's execution time variance between runs often reveals deopt behavior — enable Multi-Run Averaging to detect this.

Parse Cost: JavaScript is parsed synchronously on the Main Thread before execution begins. A 200KB minified bundle can take 40–120ms to parse on a mid-range Android device. Use dynamic import() for code splitting and avoid shipping unused code. Chrome's V8 uses lazy parsing for functions inside functions, but top-level code is always parsed eagerly.

Use performance.mark() and performance.measure() in production code to capture real-user V8 execution timings in your analytics pipeline.

6scheduler.yield() — The Production-Grade Main Thread Yielding API

The Scheduler API (scheduler.yield()) is the most important new browser primitive for INP optimization. Introduced in Chrome 115 and now available in all modern browsers, it allows long JavaScript tasks to voluntarily yield control back to the Main Thread's event queue, giving the browser time to process pending user interactions and paint frames before the task resumes.

Unlike setTimeout(fn, 0) (which yields to a low-priority macrotask queue and can be delayed by other tasks), scheduler.yield() has a unique property: the continuation of your task is scheduled at a priority higher than most other queued tasks, ensuring it resumes quickly after the browser processes pending input events. This makes it ideal for chunking long loops.

Yielding MethodResumes At PriorityAllows Input?Recommended
setTimeout(fn, 0)Low (macrotask)Yes, but slowlyAvoid
requestAnimationFrame(fn)Before next paintYes, but waits for vsyncFor DOM writes only
scheduler.postTask(fn, {priority:'user-blocking'})HighYesGood
await scheduler.yield()Highest (continues ahead of most tasks)YesBest ✓

The ideal yield interval is approximately every 50ms of processing, which is the Long Task threshold. Any contiguous block of JavaScript exceeding 50ms on the Main Thread constitutes a Long Task and blocks all input handling. A production implementation: if ((performance.now() - start) > 50) { await scheduler.yield(); start = performance.now(); }

7Web Workers & OffscreenCanvas — True Zero-INP Architecture

Web Workers are the only mechanism that provides a guaranteed 0ms INP Processing Time for heavy computation. By moving all CPU-intensive work off the Main Thread into a Worker thread, the Main Thread remains perpetually free to dispatch input events, execute handlers, and commit frames — achieving the lowest possible INP baseline.

Transferable Objects are critical for performance when communicating between the Main Thread and Workers. Instead of copying large ArrayBuffers (which is O(n) on both threads), use the transfer mechanism: worker.postMessage(data, [data.buffer]). This transfers ownership of the buffer in O(1) time, zeroing out the Main Thread's reference.

Transferable TypeUse CaseTransfer Cost
ArrayBufferImage data, audio samples, typed arraysO(1) — instant
ImageBitmapDecoded image data for canvas renderingO(1) — instant
OffscreenCanvasWebGL/2D canvas rendering in a WorkerO(1) — instant
MessagePortBidirectional inter-worker communication channelsO(1) — instant
Plain JS ObjectsAnything not transferableO(n) — structured clone copy

OffscreenCanvas: Chrome 69+ supports rendering an HTML Canvas entirely inside a Web Worker via canvas.transferControlToOffscreen(). This moves the entire 2D/WebGL render pipeline off the Main Thread, preventing canvas rendering from ever causing INP failures — critical for data visualization dashboards and games.

8React 18 Concurrent Mode & useTransition() for INP

React 18's Concurrent Mode introduces a cooperative rendering model where React can interrupt, pause, and resume render work. This directly maps to INP's Presentation Delay reduction. The key API is useTransition()/startTransition().

Without startTransition(), every setState() call is treated as urgent and blocks the Main Thread until the full VDOM reconciliation and DOM commit complete. For a dataset of 50,000 items being filtered on each keystroke, this can mean 200–800ms of blocked Processing Time per character typed.

With startTransition(): The state update is marked as a non-urgent "Transition." React renders it at low priority. If the user types again before the render completes, React throws away the in-progress render and starts fresh with the new input — never blocking the Main Thread for a stale result.

APIPriorityInterruptible?Use Case
setState() (legacy)Urgent (synchronous commit)NoNever for heavy updates
startTransition()Non-urgent (concurrent render)Yes — React can discardSearch results, filters, list updates
useDeferredValue()Derived state — deferred versionYesInput → deferred display value
React.lazy() + SuspenseAsync code splittingYes — shows fallbackRoute-level code splitting

Additionally, use React.memo(), useMemo(), and useCallback() to prevent unnecessary re-renders of child components when parent state changes. In React 19, the new React Compiler (formerly React Forget) automatically memoizes components at the compiler level, eliminating manual memoization overhead.

9INP's Direct Impact on Google Search Rankings

INP became an official Google Search ranking factor in March 2024 as part of the Page Experience signal. Google has confirmed that Core Web Vitals are a "tiebreaker" signal — when two pages have similar relevance, the page with better CWV scores ranks higher. For competitive keywords with many high-quality pages, INP can be the decisive factor.

⚡ Ranking Signal Architecture
Google's Page Experience signal aggregates: INP (replaced FID), LCP (Largest Contentful Paint ≤ 2.5s), CLS (Cumulative Layout Shift ≤ 0.1), HTTPS, Mobile Usability, and No Intrusive Interstitials. All six must pass for the Page Experience badge. INP failure removes the badge entirely, even if LCP and CLS are perfect.

A 2024 industry study by SEMrush analyzing 500,000 URLs found that pages passing all Core Web Vitals ranked 1.4 positions higher on average than non-passing equivalents. For e-commerce sites, Deloitte found that a 100ms improvement in load performance correlated with 8.4% increase in conversion rates and 9.2% increase in average order value. INP directly impacts bounce rate — slow interactions cause immediate abandonment before the conversion event fires.

INP ScoreCWV StatusSearch Ranking ImpactEstimated Bounce Rate Increase
≤ 200msGOODFull Page Experience bonusBaseline
201–500msNEEDS IMPROVEMENTPartial penalty — no experience badge+12–18%
> 500msPOORActive ranking penalty in competitive SERPs+30–55%

10Field Data vs. Lab Data: CrUX, PageSpeed Insights & RUM

The most critical INP distinction is the difference between Lab Data (synthetic, controlled test) and Field Data (real user measurements from the Chrome User Experience Report — CrUX). Google only uses Field Data for ranking. A perfect lab score does not guarantee a passing field score.

CrUX Data Collection: Chrome collects anonymized performance data from opted-in users and aggregates it into the CrUX dataset. Data is collected over a 28-day rolling window at the 75th percentile (P75) for LCP and CLS, and the 98th percentile (P98) for INP. This means a single catastrophically slow interaction that occurs in only 2% of sessions can fail your entire INP score.

Data SourceTypeUsed for RankingsDevice Coverage
PageSpeed Insights (Lab)Lighthouse synthetic testNo — diagnostic onlyDesktop + Mobile profiles
PageSpeed Insights (Field)CrUX P75/P98Yes — Google SearchAll Chrome users (opt-in)
Chrome DevTools / LighthouseLab (throttled CPU/network)No — diagnostic onlySimulated device profiles
Your RUM (web-vitals.js)Real User MonitoringNo — but mirrors CrUXAll browsers with permissions

Real User Monitoring (RUM) Setup: Install Google's web-vitals npm package to capture INP in your own analytics: import { onINP } from 'web-vitals'; onINP(console.log);. This gives you attribution data showing which element and which interaction type caused each INP event — actionable signal to direct your engineering effort precisely where it matters for your real users.

11Production INP Audit Workflow: A 6-Step Engineering Process

Improving INP from Poor to Good requires a systematic, evidence-driven audit workflow. Follow this process to locate, prioritize, and fix the specific interactions causing INP failures in production.

  1. Establish Baseline with CrUX: Check PageSpeed Insights and the CrUX dashboard for your domain. Identify if the INP failure is mobile-only, desktop-only, or universal. Mobile P98 is almost always worse — a mid-range Android device has ~4–6× slower JavaScript execution than a MacBook Pro.
  2. Deploy web-vitals.js RUM: Install onINP(sendToAnalytics, { reportAllChanges: true }) to capture the attribution.interactionTarget (the CSS selector of the clicked element) and attribution.eventEntry (the PerformanceEventTiming entry with exact phase durations) for every interaction in production.
  3. Identify the Worst Interactions: Sort your RUM data by INP duration descending. The top 3–5 interactions are responsible for 80%+ of your INP failures. Common culprits: site-wide navigation menus, search inputs, filter checkboxes, and cart/checkout buttons with heavy event handlers.
  4. Profile with Chrome DevTools: Open DevTools → Performance panel → Enable LoAF tracks → Record a session while performing the failing interaction. Look for Long Animation Frames (red blocks) and their scripts[] attribution to identify the exact function and source line.
  5. Paste into this Tool for Analysis: Copy the identified event handler into our profiler above. The AST Analyzer will detect synchronous patterns and the Auto-Fix tab will generate the appropriate patch (scheduler.yield(), structuredClone, Web Worker, etc.).
  6. Deploy Fix & Monitor CrUX: After deploying the fix, CrUX data takes 28 days to fully update. Monitor weekly via PageSpeed Insights API or the CrUX History API to track the improvement trend.

12Third-Party Scripts & INP Destruction — A Complete Risk Matrix

Third-party JavaScript is the single largest category of INP failures across the web. Unlike first-party code you control, third-party scripts load on the Main Thread, compete for CPU time during interactions, and are invisible to your DevTools profiles until you know where to look. A 2024 HTTP Archive study found that the median website loads 24 third-party requests adding an average of 375ms of Main Thread blocking time.

Third-Party CategoryINP RiskTypical Blocking CostMitigation Strategy
Google Tag Manager + 10+ tagsCritical120–400ms on interactionServer-side tagging (sGTM)
Chat Widgets (Intercom, Drift, Zendesk)Critical80–200ms Main ThreadLazy-load via IntersectionObserver
Display Advertising (GPT, AdSense)Critical50–300ms per ad slotasync/defer + facade patterns
Analytics (GA4, Amplitude, Mixpanel)Medium20–60ms on interactionEvent batching + web workers
A/B Testing (Optimizely, VWO)Critical30–150ms (FOOC risk)CDN edge-side experiments
Social Share Buttons (Facebook, Twitter)Medium15–40msFacade + click-to-load
Fonts (Google Fonts, Adobe Typekit)Low<10ms after cachingfont-display: swap
CDN-hosted libraries (lodash, jQuery)Medium10–30ms parse costSelf-host + tree-shake
Isolation Technique: To isolate third-party INP impact, use Chrome DevTools → Network panel → Block Request URL → block your third-party domains one by one. Re-profile the interaction. If INP drops by 50%+, that third-party is your primary INP attacker. Escalate to the vendor for async/worker loading options or evaluate alternatives.

FAQFrequently Asked Questions

What exactly is Interaction to Next Paint (INP) and why did it replace First Input Delay (FID)?
INP is a Core Web Vital that assesses a page's overall responsiveness to user interactions by observing the latency of all click, tap, and keyboard interactions. It replaced FID in March 2024 because FID only measured the first interaction's processing delay (ignoring event handlers and rendering time), making it an unreliable metric for modern Single Page Applications (SPAs) where users interact continuously.
What are the three distinct phases of an INP measurement?
Every interaction measured by INP consists of three phases: 1. Input Delay (time waiting for background tasks on the main thread to clear so the event handler can run), 2. Processing Time (the actual execution time of your JavaScript event callbacks), and 3. Presentation Delay (the time it takes the browser to recalculate layout, paint, and commit the frame to the screen). INP is the sum of all three.
How does the Long Animation Frames (LoAF) API help debug INP?
The legacy Long Tasks API only flagged JavaScript execution exceeding 50ms, ignoring rendering delays. The modern LoAF API measures the entire animation frame (including style calculation, layout, and paint). By exposing the specific script URLs and function names that caused the frame to exceed 50ms, LoAF provides the granular attribution necessary to isolate exact INP bottlenecks in production.
What is Layout Thrashing and why is it fatal for INP?
Layout Thrashing occurs when JavaScript writes to the DOM and immediately reads a geometry-dependent property (like `offsetHeight` or `getBoundingClientRect()`) within a loop. This forces the browser to halt JavaScript execution, synchronously recalculate the entire page layout to return the requested value, and then resume. This synchronous block massively inflates the Presentation Delay phase of INP.
How can I optimize React applications for INP?
React developers should leverage React 18's Concurrent Mode. Wrapping heavy, non-urgent state updates in startTransition() or useTransition() tells React to yield the main thread during rendering. This cooperative multitasking ensures that if a user clicks a button while React is rendering a massive list, React will pause the list render, process the click event immediately, and then resume rendering, drastically reducing INP.
Does third-party JavaScript affect my site's INP score?
Absolutely. Third-party scripts (like analytics, chat widgets, and ad networks) run on the same Main Thread as your application code. If a user clicks a button precisely while a heavy tracking script is executing, the browser must wait for the tracking script to finish before it can dispatch the click event. This directly inflates the Input Delay phase, causing immediate INP failures.
What is scheduler.yield() and how does it fix main thread blocking?
scheduler.yield() is a modern browser API that allows developers to voluntarily pause a long-running JavaScript task. By awaiting scheduler.yield() inside a heavy `while` loop, you surrender control back to the browser's event queue. The browser immediately processes any pending user clicks and paints visual updates, then returns execution to your script. This eliminates the frozen UI effect.
Can Web Workers completely eliminate INP failures?
For computationally heavy tasks, yes. JavaScript running in a Web Worker operates on a completely separate background thread. Since it does not share the Main Thread, it cannot block user input or browser rendering. Offloading complex data parsing, encryption, or heavy math to a Web Worker guarantees a 0ms Processing Time penalty for INP.
Why does my PageSpeed Insights INP score differ from my local Chrome DevTools trace?
Chrome DevTools provides Lab Data from your specific machine, network, and browser state. PageSpeed Insights reports Field Data (CrUX) representing the 75th percentile of actual users interacting with your site in the real world on diverse hardware (e.g., 5-year-old Android devices). You cannot reliably test INP locally without extreme CPU throttling to simulate real-world conditions.
Does a poor INP score directly penalize my Google Search ranking?
Yes. Since March 2024, INP is an official component of Google's Core Web Vitals ranking signal. While content relevance remains king, Google uses CWV as a 'tiebreaker'. If your page and a competitor's page have equally valuable content, the page with a 'Good' INP score (<200ms) will outrank the page with a 'Poor' INP score (>500ms).

Rate Core Web Vitals INP Debugger

Help us improve by rating this tool.

4.6/5
630 reviews