Skip to content
Core Web Vitals specialists
Core Web Vitals & measurement

Long Animation Frames: Find the Scripts Slowing INP

The Long Animation Frames API reveals since Chrome 123 exactly which script causes a slow INP interaction -- so you optimize deliberately instead of blindly.

13 min read INPLong Animation FramesCore Web Vitalsweb-vitalsDiagnose

A click that visibly hangs, a menu that opens only after a pause, a form that stutters as you type -- these are the moments the Core Web Vital INP, Interaction to Next Paint, measures. It captures how long a page takes to respond visibly to an input, and in spring 2024 it replaced the older First Input Delay as one of the three central user-experience signals (web.dev). The problem: a poor INP value tells you that something stutters, but not which script blocks the frame. This is exactly the gap the Long Animation Frames API, or LoAF, closes. Shipped since Chrome 123, it names the long frame behind a sluggish interaction, along with the scripts that caused it -- with file name, duration and calling function (Chrome for Developers). Together with the open-source web-vitals library in version 4, this frame analysis can be attached directly to the slowest measured interaction. This article shows how an anonymous INP value becomes a concrete file you can act on -- the decisive diagnostic step in 2026, because INP is the most frequently missed of the three Core Web Vitals. In our Core Web Vitals optimization this attribution comes first, before we touch a single line of JavaScript.

Long Animation Frame: the INP brake on screenweb-vitals v4 attaches the offending script to every slow click (Chrome for Developers)INP interaction: 280 ms -- too slow200-ms target0 ms40 ms180 ms60 msInput delayProcessing (JavaScript)Presentationscripts[] in the long frame -- LoAF names file and durationanalytics-bundle.js92 msapp.js (click handler)41 mswidget-embed.js24 msScript time in the frame: 157 ms -- more than half from a single third-party script.About 23 percent of mobile pages miss the INP threshold of 200 ms (Web Almanac 2025).Know the blocking script and split exactly that task instead of optimizing blindly.

Why INP Is So Hard to Pin Down

Of the three Core Web Vitals, INP is the one most sites fail. While loading time and visual stability sit in the green on many websites, about 23 percent (Web Almanac 2025) of mobile pages miss the INP threshold of 200 milliseconds at the 75th percentile. An INP counts as good when the near-slowest interaction of a session is answered visibly within 200 ms; between 200 and 500 ms the scale calls for improvement, above that it is poor (web.dev). What is measured, then, is not the average but the outlier -- the one janky interaction that sticks in the user's memory. What INP measures precisely and which fundamental levers exist is summed up in our guide to INP optimization.

The reason for its awkwardness lies in the nature of the metric. INP arises not during loading but during use -- with every click, tap or keystroke across the entire dwell time (web.dev). It depends on the amount and distribution of JavaScript, on event handlers, on embedded third-party scripts and on how heavily the browser's main thread is loaded. Unlike Largest Contentful Paint, which we break into four subparts, INP cannot be pinned to a single resource. The cause can lie in any of the many scripts that run during an interaction -- and for a long time there was no tool that named them unambiguously.

The Three Phases of an Interaction

Every interaction that INP measures breaks into three consecutive phases -- much as LCP breaks into its subparts. First, the input delay: the time from the tap until the associated event handler actually starts. It is often large when the main thread is still busy with another task. Then the processing time: the duration in which the event handlers run and do their work. Finally, the presentation delay: the time until the browser paints the result as the next frame on the screen (web.dev). Added together, the three phases make up the latency that INP records for this interaction.

For diagnosis it is decisive in which phase the time is lost. A high input delay points to an overloaded main thread -- often caused by scripts still being processed at interaction time, such as late-loaded third-party scripts that slow the page down. A high processing time reveals heavy event handlers that do too much at once. A high presentation delay points to expensive layout or render work after the handler. The Long Animation Frames API targets exactly where most of the time goes: the scripts that make the frame long.

Input Delay

The time from the tap until the event handler starts. If it is high, another task is blocking the main thread -- the response begins late.

Processing Time

The duration in which the event handlers run. Heavy handlers that do too much at once drive this phase up.

Presentation Delay

The time until the next frame is painted. Expensive layout and render work after the handler noticeably lengthens it.

What a Long Animation Frame Is

A long animation frame is a browser render cycle that took longer than 50 milliseconds (Chrome for Developers). The browser tries to paint a new image at short intervals; if a frame takes markedly longer, the interface stutters and interactions respond sluggishly. The Long Animation Frames API reports each such long frame as an entry with a series of timestamps: the start time, the total duration, the beginning of the render and layout phase and -- most importantly -- a blocking duration that states how long the main thread was blocked for other work in that frame (Chrome for Developers).

The real progress, however, lies in the scripts field: for each long frame the API provides a list of the scripts that contributed to its length -- each with source URL, duration, calling function and the type of the call, such as an event handler or a timer (Chrome for Developers). For the first time this makes it possible to say from the outside, without laborious profiling: this frame was long, and this concrete file is responsible for it. The API also records whether a user interaction took place in the frame -- so it links the long frame to exactly the response the visitor experienced as janky.

What the LoAF API Provides per Long Frame

For each frame over 50 ms the interface names the total duration, the blocking duration (main-thread blockage minus the first 50 ms per task), the start of style and layout work, and a list of the scripts involved. For each script it provides source URL, executed duration, calling context and the triggering function. This turns 'a script blocked' into the precise statement 'this file, this function, this many milliseconds' (Chrome for Developers).

Why the Old Long Tasks API Was Not Enough

A tool for long tasks existed before: the Long Tasks API. It too reports every task on the main thread that lasts longer than 50 milliseconds (MDN Web Docs). But it had a decisive weakness for finding causes: it told you that a long task ran and how long, but practically nothing about which code triggered it. As attribution it usually delivered only a coarse container hint -- for example that the task came from a same-origin script -- without a file name and without a function. In practice that meant: you knew something was stuck, but not where.

The Long Animation Frames API closes this gap twice over. First, it looks not at individual tasks but at the whole frame -- so it also captures the interplay of several short tasks, layout and rendering that together make a frame long, even though no single task crosses the 50-millisecond mark. Second, it delivers the script attribution the Long Tasks API lacked. This makes it the more precise tool for getting to the bottom of INP -- precisely because INP often does not hang on a single long task but on many small ones that add up to a sluggish frame.

FeatureLong Tasks APILong Animation Frames API
Unit of captureSingle task over 50 msWhole frame over 50 ms
Script attributionOnly a coarse origin hintSource URL, function and duration per script
Render and layout timenot included Included as its own timestamp
Link to the interactionNone directInteraction in the frame is flagged
AvailabilityBroadly supportedChromium-based browsers (from Chrome 123)

web-vitals v4 Attaches LoAF to the Interaction

Using the raw LoAF API is powerful but cumbersome. This is where the open-source web-vitals library comes in: its attribution build measures the INP of a real user session and, since version 4, attaches the matching long animation frame entries directly to the measured interaction (web-vitals documentation). So you get not just the number but the element that was tapped, the split into input delay, processing and presentation -- and the scripts of the associated long frame. This is the bridge from lab measurement to real user experience: the attribution comes from the field, from real devices and networks.

inp-attribution.js
import { onINP } from 'web-vitals/attribution';

onINP((metric) => {
  const a = metric.attribution;
  // interaction target and the three phases
  console.log(a.interactionTarget);      // e.g. button#buy
  console.log(a.inputDelay);             // input delay in ms
  console.log(a.processingDuration);     // processing time in ms
  console.log(a.presentationDelay);      // presentation delay in ms

  // long animation frames around the interaction, with scripts
  for (const frame of a.longAnimationFrameEntries) {
    for (const s of frame.scripts) {
      console.log(s.sourceURL, Math.round(s.duration), s.invoker);
    }
  }
});

In practice this means: for every slow interaction that occurs in the field, the target button, the phase split and the list of the script URLs involved land in your analytics stream. Whoever collects this data -- for instance through their own real-user monitoring, as we describe in the relationship between field data from RUM and CrUX and performance budgets -- sees not only that INP is poor on certain pages but which file regularly causes the long frame there. An anonymous percentile value becomes a nameable task.

How to Read the LoAF Data in Practice

For manual diagnosis a short PerformanceObserver in the browser console is enough. It subscribes to the long frames and prints, for each, the blocking duration and the scripts involved -- ideal for deliberately reproducing an interaction on a suspect page and seeing what happens in the frame.

loaf-observer.js
const obs = new PerformanceObserver((list) => {
  for (const frame of list.getEntries()) {
    if (frame.blockingDuration > 0) {
      console.log('Frame', Math.round(frame.duration) + ' ms',
                  'blocking', Math.round(frame.blockingDuration) + ' ms');
      for (const s of frame.scripts) {
        console.log(' -', s.sourceURL, Math.round(s.duration) + ' ms', s.invoker);
      }
    }
  }
});
obs.observe({ type: 'long-animation-frame', buffered: true });

In addition, the performance panel of the Chrome developer tools now shows long frames visually on the timeline, and the Lighthouse diagnostic report flags long main-thread tasks together with the scripts that cause them. Which findings there point to an INP problem and how to order the list sensibly is covered in our article on reading and prioritizing the Lighthouse report. What remains important: measure in the lab with realistic throttling, because a fast office laptop hides exactly the long frames that hurt most on an average smartphone.

LoAF Is a Chromium Tool

The Long Animation Frames API is available in Chromium-based browsers from Chrome 123, not in every engine (Chrome for Developers). For finding causes this is rarely an obstacle, because a brake found and fixed there improves the response time for all visitors. For verifying success in the field, however, keep in mind that the script attribution comes only from a portion of users -- INP itself, by contrast, is measured broadly.

From Frame to a Prioritized Measure

The value of the attribution lies in the order it forces. Instead of demanding 'less JavaScript' across the board, it names the one file that shows up repeatedly in the long frames of the slowest interactions. If it comes from an embedded third party, the question is: does the page need this script here, can it be deferred or swapped for a lighter integration? If it comes from your own code, the lever is usually to relieve the heavy event handler -- do less work synchronously and defer the rest.

The most effective technique against a high processing time is to actively break long tasks into smaller chunks so the browser can respond to input in between. How to do this with modern built-in means is shown in our article on breaking up long tasks with scheduler.yield. So that a good value once reached does not tip over again at the next deployment, an upper limit for script time and long frames belongs in the delivery chain -- how a performance budget in CI stops regressions automatically we describe separately.

  1. Measure INP in the field and filter out the pages with the worst values
  2. Use the web-vitals attribution to capture the slowest interaction per page with its phase split
  3. From the associated long animation frames, determine the script with the largest time share
  4. Check in which phase the time is lost -- input, processing or presentation
  5. Address only that one cause: tame the third-party script, relieve the handler or split the task
  6. Measure again in the field after the change -- does a different script appear as the new bottleneck?

The Core in One Sentence

A poor INP is not a diffuse 'too much JavaScript' but, as a rule, a nameable file in a long frame -- and it is exactly this file that the Long Animation Frames API makes visible before you optimize.

Before the LoAF API we groped in the fog with INP problems and tried script after script. Today we read the offending script straight from the frame and know, before the first change, where the lever is.

Project experience from 50+ performance projects

This is exactly how we proceed in a structured performance audit: gather INP in the field, trace the slowest interactions back to their scripts via the Long Animation Frames API, and build an action list sorted by impact -- instead of a blanket rebuild of the frontend delivery. The bottleneck often shifts to the next script after the first change, which is why the measurement is repeated until the interactions stay reliably under the threshold. Which steps this includes is shown in our overview of performance services; how INP fits into the interplay of the three metrics is explored in the article on Core Web Vitals 2026.

This article is based on data from: Chrome for Developers ('The Long Animation Frame API has now shipped' and the LoAF documentation, Google Chrome), web.dev (Interaction to Next Paint, Google Chrome), the web-vitals library (attribution documentation, open source) and the Web Almanac 2025 (HTTP Archive, performance chapter). All statistics cited were verified at the time of publication.

Related Articles