Skip to main content
Systems Engineering

Diagnosing INP Regressions via the LoAF API

Long Animation Frames API entries expose script attribution, style/layout cost and presentation delay hidden by the Long Tasks API's flat 50ms bucket.

Diagnosing INP Regressions via the LoAF API
Eleanor HayesEleanor Hayes10 min read

In this guide

Share

A p75 Interaction to Next Paint (INP) score of 340ms with a Long Tasks API report showing nothing over 60ms is not a contradiction — it is a measurement gap. The Long Tasks API flags any main-thread block exceeding 50ms, but it treats the entire task as an opaque scripting blob. It cannot tell you whether the delay came from a third-party analytics script, a synchronous layout recalculation triggered by a CSS-in-JS library, or presentation delay waiting on the compositor. When you’re chasing INP regressions in a production SPA, that opacity is the bottleneck. The Long Animation Frames API (LoAF) exists specifically to close this attribution gap by decomposing a slow frame into its constituent phases: script execution, style/layout work, and render delay.

This article covers the architecture of LoAF entries, how to wire a PerformanceObserver pipeline that correlates them with real user interactions, and how to enforce frame-budget gates in CI before a regression reaches production RUM dashboards.

#Why the Long Tasks API Under-Reports INP Cost

INP measures the latency between a user interaction (click, keypress, tap) and the next paint that visually reflects the result. That latency spans four phases: input delay (main thread busy when the event fires), processing time (event handler execution), presentation delay (style recalculation, layout, paint, compositing, and the wait for the next vsync). The Long Tasks API only observes the second phase reliably. Presentation delay — frequently the largest contributor on layout-heavy dashboards — is invisible to it entirely.

The Long Animation Frames API specification addresses this by emitting a single PerformanceLongAnimationFrameTiming entry per slow frame, with a nested scripts array attributing duration to individual invokers (event handlers, promise resolutions, requestAnimationFrame callbacks) and explicit styleAndLayoutStart / renderStart timestamps. This turns a single flat 50ms number into a phase-resolved trace you can act on.

#Architectural Breakdown of a LoAF Entry

Each frame the browser renders passes through a defined pipeline: input handling, JavaScript execution (including microtasks and rAF callbacks), style recalculation, layout, paint, and compositing, followed by presentation to the screen. A LoAF entry is only generated when total frame duration exceeds the 50ms threshold, mirroring the Long Tasks API cutoff, but unlike Long Tasks it captures the full pipeline rather than just the scripting segment.

Rendering diagram...

Run the trace five times minimum per route and take the median rather than a single pass; frame timing on CI runners is noisy due to shared CPU contention, and a single-run gate produces flaky failures that erode trust in the check.

#Failure Modes and Edge Cases

The Long Animation Frames API has real limitations that will bite you in production if unaccounted for.

Cross-origin script attribution collapses. If the dominant script in a slow frame originates from a third-party origin without the Timing-Allow-Origin header, sourceURL and sourceFunctionName return empty strings. You’ll see the frame but not the culprit — a common scenario with ad tech, chat widgets, and analytics SDKs.

Long Animation Frames API

Buffer eviction under high-frequency interaction. The performance entry buffer has a bounded size (typically 200 entries pre-observer-registration). On interaction-heavy SPAs — infinite-scroll feeds, drag-and-drop builders — early frames get evicted before your observer flushes, silently truncating attribution data. Register the observer as early as possible in the document head, before hydration begins.

Browser support asymmetry. LoAF is Chromium-only as of current shipping versions. Firefox and Safari fall back to the Long Tasks API or nothing. Any INP diagnostics dashboard built on LoAF needs a feature-detection fallback path, otherwise your Safari traffic silently disappears from attribution reports rather than reporting degraded data.

rAF callback misattribution. Animation-driven UI libraries schedule work inside requestAnimationFrame, which LoAF attributes to the frame it executes in, not the frame that scheduled it. If your interaction handler defers heavy work via rAF to “avoid blocking,” the LoAF trace will still show it as the dominant script — deferral doesn’t remove the cost, it just moves the attribution window.

#
Diagnostic Comparison Table

APICapture ScopeThresholdAttribution GranularityBest Use Case
Long Tasks APIScript execution only50msNone (opaque task)Coarse main-thread blocking alerts
Long Animation Frames APIFull pipeline: script, style, layout, render50msPer-script invoker, source, functionRoot-causing INP regressions
Event Timing APIInteraction lifecycleNone (all interactions)Input delay, processing, presentation delay totalsComputing INP score itself
Layout Instability APIUnexpected layout shiftsNoneAffected DOM nodesCLS diagnostics, not INP

#Scaling and Security Trade-offs

Deploying LoAF-based attribution at fleet scale introduces cost and privacy considerations that need explicit sign-off from your platform and security teams before rollout.

  • RUM ingestion volume — on high-traffic routes, batching every LoAF entry into your analytics pipeline can multiply event volume 3–5x versus Web Vitals alone; sample at 10–20% for high-cardinality routes and retain 100% only for checkout/critical-path flows.
  • Source attribution as a data leak vectorsourceFunctionName and stack-adjacent fields can expose internal code structure or third-party vendor names in client-side network requests; strip or hash these fields before they leave the browser if your CSP or data governance policy restricts vendor disclosure.
  • Sampling bias on low-end devices — devices already struggling with main-thread contention generate disproportionately more LoAF entries, skewing your p75 upward if your sampling isn’t device-class stratified; segment aggregation by device memory/CPU tier rather than a flat global percentile.
  • CI trace noise vs production fidelity — synthetic Puppeteer traces on shared CI runners rarely match real device thermal and CPU throttling behaviour, so a budget that passes in CI can still regress in the field; treat CI gating as a regression tripwire, not a substitute for RUM validation post-deploy.

Treat LoAF as the attribution layer that sits underneath your INP score rather than a replacement metric. The score tells stakeholders whether the experience is acceptable; the LoAF trace tells your engineering team exactly which invoker, source file, and pipeline phase to fix. Wiring both into the same dashboard, with CI gating catching regressions before they reach field measurement, closes the loop between synthetic testing and real-user telemetry that most performance monitoring stacks currently leave disconnected.

Evidence trail

Sources and verification

Primary documentation and external technical references used in this article.

  1. 01Long Animation Frames API specificationdeveloper.chrome.com
Eleanor Hayes

Eleanor Hayes

Systems Engineering Editor

Dr Eleanor Hayes is a veteran cryptography researcher and enterprise security architect specialising in zero-trust network implementations.

Published Last changed
View Profile
Reader Interaction

Comments

Add a thoughtful note on Diagnosing INP Regressions via the LoAF API. Comments are checked for spam and held for moderation before appearing.

Loading comments...
Comment submission is disabled until Cloudflare Turnstile keys are configured.

Learn More About KBY

Was this useful?

Engineering insights, direct to you.

Receive the latest Systems Engineering tutorials, production guides, Engineering Labs and operational best practices.