Skip to content
Zumkai

INP and animation: what actually counts for the metric

Scrolling enters no INP. What enters is the animation running at the moment of the click. The table of what touches each phase, and 50% against 1% of frames.

  • inp
  • core web vitals
Card comparing the frames lost when animating top and left, 50%, against animating transform, 1%.
Contents
  1. What INP measures, across three phases
  2. Why scrolling stayed out, and what that frees
  3. What actually costs: the animation running at the click
  4. Compositor against main thread, with Chrome's number
  5. The table: which technique touches which phase
  6. will-change is a last resort, not an optimization
  7. What the field data shows
  8. How to diagnose it on your site
  9. How this blog handles it
  10. Frequently asked questions
  11. What to take away

The animation that frightens motion people most sits nowhere in your INP.

Scrolling counts for nothing. The metric watches clicks, taps and keys, and ignores scroll, hover and zoom. That comes from the definition, not from interpretation. So that reveal you built with care, firing as the page scrolls, the hero parallax, the progress bar following the reading: none of it enters the count at first hand.

The thing that enters is another, and a far more specific one: the animation running the instant the person clicks.

This post separates one from the other. With the definition from the source, Chrome's official number for the cost of animating wrong, and a table mapping each motion technique against the INP phase it touches. It is the natural continuation of the scroll foundation, now from the metric's side.

What INP measures, across three phases

INP is the time between the interaction and the moment the browser delivers the next frame. That interval splits into three parts, and knowing which one you are worsening is what makes the diagnosis possible (web.dev).

The three phases of an interaction measured by INP INP sums three phases: input delay, which is the wait until the handler starts; processing duration, which is the execution of the handlers; and presentation delay, which is the time until the browser paints the next frame. The good threshold is 200 milliseconds at the 75th percentile. From the click to the next frame input delay waiting for the thread processing the handlers running presentation delay render and paint Animation enters here, in the third phase Good: ≤ 200 ms · needs improvement: up to 500 · poor: above 500 measured at the 75th percentile of real traffic
Source: the INP documentation on web.dev. The thresholds hold at the 75th percentile.

Input delay is the wait until the handler starts running, in most cases because the main thread is busy. Processing duration is the execution of that frame's handlers. Presentation delay is what comes after: the browser has to recompute what changed and paint.

The good threshold is 200 milliseconds, measured at the 75th percentile of real traffic. Above 500 is poor.

And here is the part that changes everything for motion people:

Counts for INPCounts for nothing
A mouse clickScrolling
A screen tapHover
A key pressZoom

INP replaced FID, which measured the first interaction alone and the input delay alone. The swap matters because every interaction in the visit is now under observation, not the opening one alone.

Why scrolling stayed out, and what that frees

Scrolling is no interaction for INP purposes. The practical consequence is large and almost nobody states it: scroll animation appears nowhere in the metric.

A reveal that fires when the section enters the screen, a parallax, a reading progress bar, an image that scales as the page descends. If those animations run during the scroll and the person is not clicking, they have no way to worsen INP, because no interaction is under measurement at that moment.

That makes them free in no way. A sloppy animation during the scroll causes visible stutter, and stutter is a problem even with no metric measuring it. The difference is one of diagnosis: if your INP is poor, hunting for the blame in the parallax is hunting in the wrong place.

The distinction changes the priority of the work. The eight ScrollTrigger patterns that cover most cases are all scroll ones, and none of them needs sacrificing for INP. If they stutter, the problem is fluidity and a compositor property solves it. If INP is poor, the cause sits elsewhere on the page.

The scroll techniques with no JavaScript, covered in scroll animation with no JavaScript, run on the compositor for the express purpose of avoiding stutter. They solve the fluidity problem. INP is another problem, and it is the subject from here on.

What actually costs: the animation running at the click

The case that charges is this one: an animation is under way, and the person clicks.

The click arrives, the handler runs, and then the browser has to deliver the next frame. Except that it already had rendering work queued because of the animation. That work enters the third phase, the presentation delay, and the INP clock keeps running until the frame comes out.

The frame arithmetic explains why that weighs. At 60 frames per second, the browser has around 16.7 milliseconds to produce each one, and everything fits in that budget: style, layout, paint and composition. An animation that triggers layout consumes part of that time on every frame while it runs. Once the click arrives, it finds no idle thread: it finds a thread whose schedule is already full until the animation ends.

The 200-millisecond threshold amounts to around twelve frames. That sounds like a lot, and it is, since what makes a poor INP is a queue of frames rather than one expensive frame. That is why a two-second animation with layout on every frame is far worse than one expensive single operation: it keeps the thread busy for a hundred and twenty consecutive frames.

Three situations concentrate the problems:

The animation runs while the user clicks. A carousel on auto-rotation, an animated counter, a decorative loop. If the animation sits on the main thread, it competes with the click.

The click fires the animation. Here the cost appears earlier, in the processing duration: the handler that starts the animation does work, and only then comes the rendering. Menus that open, accordions, modals and page transitions land in that group.

The library loads on the click. If the animation depends on a package fetched at interaction time alone, the cost appears in the first phase, in the input delay, and it tends to be the worst of the three.

Compositor against main thread, with Chrome's number

The difference between animating right and wrong has an official measurement, and it is larger than most people imagine.

Per Chrome's animation performance guide, animating with top and left results in 50% of frames dropped. The same animation done with transform drops 1% (web.dev).

Frames dropped by animated property Animating with the top and left properties results in 50% of frames dropped. The same animation with transform drops only 1% of the frames, because it runs on the compositor without triggering layout. Frames dropped in the same animation top / left 50% triggers layout on the main thread transform 1% runs on the compositor, no layout
Source: Chrome's animation performance guide, on web.dev.

The reason is simple. Only transform and opacity animate on the compositor without triggering layout or paint. Any other property forces the browser to recompute positions, and that recomputation happens on the main thread, the very place where the click needs serving.

The documentation's own recommendation is direct: avoid every property that triggers layout or paint, unless it is necessary for real.

The swaps that solve it without changing the visual result

Almost every animation that triggers layout has a compositor equivalent, and the difference is seldom noticeable to whoever is looking.

Instead of animatingAnimateNote
top, left, right, bottomtransform: translate()The same displacement, no layout
width, heighttransform: scale()It scales the content along; for a box needing a real reflow, no equivalent exists
margin, paddingtransform: translate()If the goal is moving, rather than pushing neighbors
visibility, displayopacity plus pointer-eventsdisplay animates in no way; the swap also avoids a layout jump
background-positiontransform on an inner layerIt triggers paint, cheaper than layout, and still main thread
box-shadow, filterpre-render on two layers and cross opacityThey are expensive in paint when animated

One honest exception: width and height when the goal is pushing the surrounding content for real, as in an accordion that opens. There no substitute exists: scale deforms the content and moves no neighbors. In that case, the way out is limiting the affected area and accepting the cost, rather than pretending it is absent.

The complete catalog of techniques, with the code for each, sits in the guide to motion design for the web. Here the choice criterion is what matters.

The table: which technique touches which phase

<!-- [UNIQUE INSIGHT] -->

Joining the two pieces of documentation, mapping each motion technique against the INP phase it affects becomes possible. That table exists nowhere else, and it answers the practical question.

TechniquePhase it touchesWhy
Scroll animationNoneScrolling is no measured interaction
Hover with transformNoneHover is no measured interaction
transform/opacity running at the clickPresentation, littleThe work goes to the compositor
top/left/width/height at the clickPresentation, a lotIt triggers layout on the main thread
Animation fired by the click, in JSProcessing and presentationThe handler runs before the frame
Library loaded at the clickInput delayIt blocks before the handler starts

The reading that comes out: most of a site's motion appears nowhere in INP. The part that appears is what coincides with the click and what the click fires. If you have to cut animation to improve the metric, cut from those two categories, and leave the rest alone.

Text animation illustrates both sides well: an entrance effect fired by scroll costs the metric nothing; the same effect fired by a click, with the library loading on the spot, costs in all three phases.

will-change is a last resort, not an optimization

Here the current advice is wrong, and saying so in full pays off.

will-change promotes the element to a layer of its own, which takes the paint work off the main thread. That looks like a free optimization, and that is how most texts present it.

Chrome's documentation recommends the opposite: use it with restraint, because creating a layer has a cost of its own and can generate other performance problems. The guidance is applying it after observing the problem, not before. If it makes sense, apply it to the element right before the change and remove it when the change ends.

Where support is missing, transform: translateZ(0) forces the layer creation with the same effect.

The decision of which tool to animate with, covered in GSAP, Motion or pure CSS, changes little here: the property rule holds the same in any library. The change is how much JavaScript comes along, and that weighs on the first phase.

What the field data shows

The web's real situation has a contrast that helps calibrate the worry.

Per the Web Almanac 2025 performance chapter, with CrUX data from July 2025, 77% of origins record good INP on mobile and 97% on desktop. Across all three Core Web Vitals together, 48% pass on mobile and 56% on desktop (Web Almanac).

And, in the same report: non-composited animations appear on 40% of mobile pages.

MetricMobileDesktop
Good INP77%97%
Pass all three CWV48%56%
Pages with non-composited animation40%

The first two numbers and the third coexist, and the explanation is this post's thesis: an animation off the compositor charges only when it coincides with the interaction. Forty percent of mobile pages animate wrong, and most of them pass INP anyway, because the user seldom clicks mid-animation.

That is no permission to animate wrong. It is the reason the problem is hard to find: it appears now and then, at the 75th percentile, and vanishes when you go to reproduce it.

How to diagnose it on your site

The routine that works goes from the metric to the cause, never the reverse.

Start with the field, not the lab. INP is a real-user metric. Lighthouse measures no field INP, and one session of yours on your machine represents no 75th percentile. Without field data you are guessing.

The three sources of field data, from the easiest to the most precise. The Core Web Vitals report in Search Console, which groups pages by similar behavior. CrUX, which brings your domain's public 75th percentile when it has enough traffic. And the web-vitals library installed on the site, the only one that tells you which element caused the worst interaction. The first two say a problem exists. The third alone says where.

Identify the guilty interaction. A page's INP is the worst interaction, not the average. Finding out which element sits behind it before touching any animation pays off.

Separate the three phases. If the problem sits in the input delay, the blame belongs to JavaScript blocking before the click, and animation has nothing to do with it. If it sits in processing, look at the handler. Only if it sits in the presentation delay does animation enter as a suspect.

Only then hunt the property. Check whether what is animating is transform or opacity. Anything else is a candidate.

One observation that sets expectations: Google's official INP optimization guide mentions animation almost nowhere. It covers DOM size, content-visibility for deferring off-screen rendering, and HTML rendering through JavaScript. If your INP is poor, the odds of the cause being heavy JavaScript are far higher than the odds of it being your parallax.

How this blog handles it

<!-- [PERSONAL EXPERIENCE] -->

I have to start with a limitation, because it changes what I can assert: this blog has no field analytics installed. No real INP for ZUMKAI exists for me to show. I have lab measurement and the design decisions, and that is all I talk about here.

The decisions I made follow this post's rule without my having that clarity at the time. The post's table of contents indicator, which follows the reading, stopped using an animation library and became plain transform. The article bodies and the catalogs use content-visibility so nothing off screen gets rendered. The charts are static SVG, with no animation at all.

One overlap serves both sides. Respecting the preference for less movement is more than accessibility: for anyone who enabled that preference in their system, the browser stops doing the rendering work of the switched-off animations. It is the same cost cut, applied to the very people who asked. A rule that should already exist out of respect ends up delivering performance as a bonus.

The explicit judgment: the decision with the best claim to having helped was no motion decision, it was a JavaScript one. Taking a library out of the loading path reduces the input delay, which is the phase where INP tends to get lost. The animated property choices came later, and they would have a smaller effect.

And the part that bothers me: with no field data, all of that is a well-founded hypothesis, not a measured result. Installing the measurement is on the list, and until then any INP number I published about this site would be invention.

Frequently asked questions

Does animation hurt INP?

Only when it runs at the moment of the interaction or gets fired by it. Animation enters the metric's third phase, the presentation delay, because the browser has to finish the rendering work before delivering the frame. Animation running at another moment goes unmeasured.

Does scrolling count for INP?

No. INP watches mouse clicks, screen taps and key presses. Scrolling, hover and zoom stay out by the metric's definition. That means scroll animations, parallax and reveals enter the INP count in no direct way.

Which properties are safe to animate?

Only transform and opacity animate on the compositor without triggering layout or paint. In Chrome's documentation, the same animation done with top and left drops 50% of the frames, while with transform it drops 1%. Any property that alters geometry forces a recomputation on the main thread.

Does will-change help or hurt?

It depends on how you use it. It promotes the element to a layer of its own, which helps, and creating a layer has a cost, so Chrome's documentation recommends applying it with restraint, after observing the problem. Applying it as a precaution across many elements tends to make things worse.

What INP threshold counts as good?

Up to 200 milliseconds, measured at the 75th percentile of real traffic. Between 200 and 500 lands as "needs improvement", and above 500 counts as poor.

My animation is beautiful and my INP is poor. Where do I start?

Somewhere other than the animation, in all probability. Start by separating the three phases in the field data: if the problem sits in the input delay or in the processing, the cause is JavaScript, not motion. The official INP optimization guide covers DOM size and JavaScript rendering above all, and it mentions animation almost nowhere.

What to take away

  • Scrolling, hover and zoom count for nothing. INP measures clicks, taps and keys.
  • What costs is the animation at the moment of the click, or fired by it.
  • transform and opacity go to the compositor. Everything else triggers layout.
  • 50% against 1% of frames dropped is the difference between top/left and transform.
  • will-change is medicine, not a vitamin: apply it after observing the problem alone.
  • Before blaming the motion, separate the three phases. The cause tends to be JavaScript.

If you change one thing after reading this, swap the animated property. It is the cheapest change, it has an official number behind it, and it costs nothing in fluidity, since the animation stays identical to whoever is watching.