PLAINFINCH learn by building

The three numbers that matter

You will learn the three numbers Google uses to judge how a page feels, and move each one from poor to good on a real page.

Which numbers does Google call Core Web Vitals?

Three numbers, each about a different way a page can feel bad. Largest Contentful Paint (LCP) measures loading: when did the biggest thing in the first screen appear? Cumulative Layout Shift (CLS) measures stability: how much did visible content jump around? Interaction to Next Paint (INP) measures responsiveness: after a tap, how long until the screen showed a response?

Each has two lines. LCP is good at 2.5 seconds or less and poor above 4 seconds. CLS is good at 0.1 or less and poor above 0.25. INP is good at 200 milliseconds or less and poor above 500. Anything in between needs work. Google judges a page on the 75th percentile of real visits, so three out of four visitors must get a good result for the page to pass.

The starting bird club page is poor on all three: LCP about 5.5 seconds, CLS about 0.21, INP about 340 milliseconds. Each has a different cause, and each needs a different fix.

FigureThree scales with good, needs work and poor zones. LCP moves from 5.5 s to 1.5 s, CLS from 0.21 to 0, and INP from 336 ms to 16 ms, each into the good zone.Three scales with good, needs work and poor zones. LCP moves from 5.5 s to 1.5 s, CLS from 0.21 to 0, and INP from 336 ms to 16 ms, each into the good zone.
Each vital has its own cause and its own fix on the bird club page. The red markers are the starting page, the dark ones the solution.

What counts as the largest contentful paint?

The browser keeps track of the biggest image or block of text inside the first screen as the page loads, and records when it painted. Early on, the headline may be the largest thing. When a big photo arrives it takes over, and its paint time becomes the LCP. Elements below the first screen don't count, and neither do purely decorative things such as an empty box with a colour.

On this page the largest element is the kingfisher photo, a 900 KB file 2,400 pixels wide. The screen shows it at about 328 CSS pixels, so the phone throws away most of what it paid to download. The Vitals view names the element so you know what to fix; in the starting page the bar for kingfisher.jpg runs until about 5.5 seconds.

Switching to the 800 pixel copy, 90 KB, brings LCP to about 1.5 seconds. When LCP is slow, find the LCP element first and then ask the waterfall questions: is it too big, found too late, or waiting behind something else?

How is layout shift scored?

Every time something already on screen moves without the visitor asking for it, the browser scores the shift. The score multiplies two fractions: how much of the screen the moving content covers, and how far it moved as a share of the screen height. The shifts are added up, grouped into bursts, and the worst burst is the page's CLS.

On the starting page the <img> has no width or height, so the browser gives it no space at all. The text and the button sit at the top of the screen until the photo arrives at about 5.5 seconds, then everything below it drops by about 231 pixels. The Filmstrip outlines the moved boxes. That one jump scores about 0.21, close to poor.

Shifts the visitor caused, such as opening a menu they tapped, don't count if they happen within half a second of the tap. Shifts from late images, banners, adverts and embeds do.

<img src="/img/kingfisher-800.jpg" alt="A kingfisher on the willow by the weir"
     width="800" height="533">

What does INP measure after a tap?

INP follows a single tap, click or key press from start to finish. It adds three parts: the input delay, while the tap waits for the main thread to finish whatever it was doing; the processing time, while your event handlers run; and the presentation delay, while the browser works out and draws the next frame. Real browsers report the slowest interaction of a visit, roughly, and Google uses the 75th percentile of those across visits.

Here the simulator taps “I saw it too” and reports about 340 milliseconds. The main thread was idle, so the input delay is 0. Almost all the time is processing: the handler calls work(320), standing in for code that rebuilds the whole sightings archive, before it updates the count. The visitor sees nothing change until that work has finished.

The fix is to show the result first and do the slow work afterwards. Update the count, then await scheduler.yield(). The yield ends the current task, so the browser can paint the new count; the archive work carries on in a new task straight after. INP counts only up to that first paint, so it drops to about 16 milliseconds, while the archive still gets rebuilt.

button.addEventListener('click', async () => {
  seen += 1;
  count.textContent = seen + ' members have seen it.';
  await scheduler.yield();
  work(320); // rebuild the whole sightings archive
});

Where do FCP and TBT fit in?

First Contentful Paint (FCP) is when anything first appeared. It isn't a Core Web Vital, but it is useful: if FCP is slow, LCP can't be fast, and the cause is usually something blocking the first paint. On this page FCP is about 1 second in both versions, because the fixes didn't touch the stylesheet or the HTML's arrival.

Total Blocking Time (TBT) adds up how much each main-thread task ran past 50 milliseconds after the first paint. Lab tools such as Lighthouse use it as a stand-in for INP, because a lab test has no real visitor to tap anything. A page full of long tasks has high TBT and usually poor INP. The simulator shows TBT when no tap is set up, and INP when one is.

Which number should you fix first?

Start with whichever is poor, and then with whichever is cheapest to fix. Layout shift from images is nearly always the cheapest: two attributes. A huge LCP image is next: a smaller file. Slow interactions take more thought, because you have to understand what the code is doing and decide what can wait.

The three numbers also interact. Shrinking the photo fixed LCP but not CLS; without width and height the smaller photo still made the text jump, just sooner. Adding the sizes fixed CLS but not LCP. Treat each number as its own question, and recheck all three after every change.

How do real tools report these numbers?

PageSpeed Insights shows two sets of numbers for a page. The top part, when the site has enough traffic, is field data from the Chrome UX Report: real Chrome users' LCP, CLS and INP over the past 28 days, at the 75th percentile. Below it is a Lighthouse lab test on a simulated phone, with LCP, CLS, TBT and suggestions.

In Chrome DevTools the Performance panel shows live LCP, CLS and INP as you use the page, which is the quickest way to test a tap. Search Console groups a site's pages by vitals status. The next lesson explains why the lab and field numbers rarely match, and which one to trust for what.

Where does the simulator simplify the vitals?

Real CLS groups shifts into windows of up to five seconds and takes the worst window; the simulator adds up shifts during the load. Real INP is taken from many interactions over a visit; the simulator taps once. Real presentation delay depends on how much the page has to redraw; the simulator uses a fixed 16 milliseconds, one frame. The directions all match: a smaller image paints sooner, reserved space stops jumps, and showing feedback before slow work makes taps feel quick.

What does AI often get wrong about Core Web Vitals?

AI answers still mention First Input Delay, which INP replaced as a Core Web Vital in 2024, and sometimes quote old thresholds. They also tend to treat a Lighthouse score as the goal, when the vitals that matter are those from real visitors. A score of 100 on a fast laptop says little about a 4G phone.

On code, the common slip is moving slow work into a setTimeout without updating the screen first, or wrapping everything in requestAnimationFrame and hoping. Check the order in the handler: visible change first, then yield, then the work that can wait.

Field notes

  • LCP measures when the biggest thing on the first screen appears.
  • CLS scores how much visible content jumps around while you read.
  • INP measures how long a tap takes to show its result on screen.
  • Fix whatever is poor first, starting with the cheapest change.

Easy to get wrong

The three numbers have different causes. A smaller image fixes loading but leaves the jump; sizes fix the jump but not the loading.

The screen can't update until the handler's task ends. Doing the slow work first means the visitor stares at an unchanged page.

The score is a lab summary on one simulated device. Google's assessment uses real-visitor LCP, CLS and INP.

Questions people ask

Is First Input Delay still used?

No. INP replaced it as the responsiveness vital in March 2024. FID measured only the delay before the first tap's handler started; INP measures the whole wait until the next paint, for every interaction.

Can a page pass with one poor number?

For Google's assessment, a page needs all three vitals in the good range at the 75th percentile. Each poor number is also its own problem for visitors, whatever the assessment says.

Does `scheduler.yield()` work in every browser?

Chromium browsers and Firefox support it. A fallback is to wrap the slow part in setTimeout(..., 0), which also ends the task, though the work may wait behind other queued tasks.

Why is CLS a number with no unit?

It multiplies two fractions of the screen, so it has no unit. Small shifts add up to small numbers; one large jump of most of the screen can score above 0.5.

Keep going