Finally seeing whether rendering or script is delaying the interaction
The Long Animation Frames API delivers a far more granular breakdown of blocking frames than the older Long Tasks API, showing exactly how much of each long frame comes from script execution versus rendering. That makes it a central tool for precisely diagnosing INP regressions.
Inhaltsverzeichnis
- 1. A Recap: The Long Tasks API and Its Limits
- 2. What is the Long Animation Frames API
- 3. Code Example: Setting Up a PerformanceObserver for LoAF
- 4. Breaking Down the Frame Phases in Detail
- 5. Correctly Diagnosing Rendering vs. Script Share
- 6. A Practical Debugging Example for an INP Regression
- 7. Integration Into Real-User Monitoring and web-vitals
- 8. Browser Support for the Long Animation Frames API
- 9. Conclusion
- 10. Zusammenfassung
- 11. FAQ
1. A Recap: The Long Tasks API and Its Limits
The Long Tasks API reports, via a PerformanceObserver, every main-thread task that runs longer than fifty milliseconds, providing a start and end time along with a rough attribution of which context, such as a specific iframe, the task came from. For years this was the only standardized tool that made long blocking tasks visible at all, without relying on manual profiling in the DevTools.
The decisive weakness of the Long Tasks API, though, is its lack of internal breakdown: it reports that a task took a long time, but not how much of that came from JavaScript execution, how much from style recalculation, and how much from layout. For targeted troubleshooting of interaction delays that was often too coarse, which is why developers frequently still needed manual profiling in the DevTools Performance tab.
2. What is the Long Animation Frames API
The Long Animation Frames API, LoAF for short, addresses exactly this weakness by providing a detailed breakdown of the individual phases for every blocking animation frame of fifty milliseconds or more. It's registered through a PerformanceObserver with the entry type long-animation-frame, where each entry carries fields such as renderStart, styleAndLayoutStart, and a total duration for style and layout computation.
Additionally, every LoAF entry provides an array called scripts containing individual PerformanceScriptTiming objects, which for every script executed within the frame include details such as the invoker, source file, function location, and individual execution duration. For the first time, this makes it possible to pinpoint exactly which script or event handler was responsible for a given delay, without manual profiling.
3. Code Example: Setting Up a PerformanceObserver for LoAF
The setup follows the familiar pattern of the Performance Observer APIs: you register an observer for the entry type long-animation-frame and process the reported entries in the callback function. The scripts array is particularly valuable here, since it allows attribution down to individual function calls instead of just knowing the total duration of the frame.
The example below determines the most expensive script for every detected long frame and logs it together with its source, which in practice very quickly points to the actual culprits behind interaction delays.
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
const totalDuration = entry.duration;
const renderPortion = entry.styleAndLayoutStart
? entry.duration - (entry.styleAndLayoutStart - entry.startTime)
: 0;
// Find the most expensive script within the frame
const worstScript = entry.scripts
.sort((a, b) => b.duration - a.duration)[0];
console.warn('Long animation frame detected', {
durationMs: totalDuration,
renderingPortionMs: renderPortion,
source: worstScript?.sourceURL,
functionName: worstScript?.sourceFunctionName,
invoker: worstScript?.invoker,
});
}
});
observer.observe({ type: 'long-animation-frame', buffered: true });
4. Breaking Down the Frame Phases in Detail
A LoAF entry decomposes a blocking frame into clearly defined time segments: the time until script execution starts, the script execution itself with all involved function calls, the subsequent style and layout computation, and finally the time until the actual rendering hits the screen. Each of these phases is available as its own timestamp or duration within the entry.
Particularly useful is the forcedStyleAndLayoutDuration field within the individual script entries, which shows how much time was lost to forced synchronous layout calculations, typically caused when a script queries a layout property like offsetHeight right after a DOM change. This so-called layout thrashing was essentially invisible with the Long Tasks API and can now be pinpointed directly with LoAF.
5. Correctly Diagnosing Rendering vs. Script Share
If the script portion of a long frame dominates, that usually points to inefficient JavaScript logic, such as an overly complex calculation inside an event handler, or a third-party library doing too much work synchronously. In that case the scripts array helps directly, since it names the exact source and function that should be optimized or split into smaller chunks.
If, on the other hand, the style and layout portion dominates, the cause is usually extensive DOM changes, complex CSS selectors, or a forced reflow from interleaved reads and writes of layout properties. Here the precise timestamp analysis helps determine whether the problem genuinely lies in the rendering itself, or in a script that unnecessarily forces that rendering early.
6. A Practical Debugging Example for an INP Regression
Suppose real-user monitoring shows a noticeable jump in Interaction to Next Paint after a release, specifically for the click on the add-to-cart button. The first step is to use the Event Timing API to find the exact time window of the affected interaction, and then filter all LoAF entries whose time window overlaps with it.
In practice, this often reveals that a newly added analytics library synchronously serializes a large JSON object on click, which shows up in the scripts array as the dominant entry with a high duration, while the rendering portion of the frame barely increased. Without LoAF, the Long Tasks API would only have reported a longer overall task without naming the responsible library, which would have made troubleshooting take much longer.
7. Integration Into Real-User Monitoring and web-vitals
For production use, it pays off to send LoAF data not just to the local console but aggregated to a real-user monitoring system, in order to spot systematic patterns across many user sessions. Google's web-vitals library already offers experimental attribution extensions that automatically tie LoAF data to INP measurements.
When aggregating, it's important not to transmit every single LoAF entry with full detail, since that quickly generates large volumes of data, but instead to selectively collect only the entries that actually overlap in time with a poorly measured interaction. That keeps the data volume manageable while still preserving enough context for later troubleshooting.
8. Browser Support for the Long Animation Frames API
The Long Animation Frames API is currently supported by Chrome and other Chromium-based browsers, while Firefox and Safari haven't implemented it yet. Since this is a pure diagnostic API with no effect on the user experience, a feature test is straightforward, and the lack of support elsewhere poses no functional risk.
For projects measuring users across different browsers, falling back to the still-available Long Tasks API in non-supporting browsers is a sensible approach, even though it naturally provides less detailed data. That way, at least a coarse detection of blocking tasks remains available across all browsers.
9. Conclusion
The Long Animation Frames API closes a long-standing gap between the coarse signal of the Long Tasks API and the labor-intensive manual profiling in the DevTools, delivering, automatically and usable in production, exactly the information needed for INP debugging. Anyone serious about analyzing interaction delays in the field can hardly avoid LoAF anymore.
Its practical value shows up especially in complex applications with many third-party scripts, where attribution down to individual source files and functions can be the difference between hours of troubleshooting and a root cause identified in minutes. Gradually integrating it into existing real-user monitoring is the obvious next step for any team that takes INP seriously.
| Feature | Long Tasks API | Long Animation Frames API |
|---|---|---|
| Threshold | 50ms total duration | 50ms total frame duration |
| Script attribution | Only rough context (e.g. iframe) | Individual scripts with source and function |
| Rendering portion visible | No | Yes, as a separate phase |
| Forced layout detectable | No | Yes, via forcedStyleAndLayoutDuration |
| Browser support | Broad (partial Firefox included) | Currently Chromium-based browsers |
Mironsoft
Web performance, Core Web Vitals, and load time optimization
Load times that don't make users bounce before the page is even visible?
We review existing websites for slow Core Web Vitals, bloated JavaScript bundles, and unnecessary render blockers, then build a performance foundation that stays measurable instead of just looking good once.
Performance Audit
Systematically measuring and fixing Core Web Vitals, load waterfall, and render blockers.
Bundle Optimization
Specifically reducing JavaScript and CSS bundle size and improving code splitting.
Monitoring Setup
Establishing continuous performance monitoring instead of a one-time snapshot.
10. Zusammenfassung
Long Animation Frames API
Purpose
Detailed attribution of blocking main-thread frames
New vs. Long Tasks
Breakdown by script and rendering share
Application
Targeted debugging of INP regressions
Support
Chromium browsers, Firefox and Safari pending