# Metrics

# index

---
layout: collection
override:tags: []
date: 2019-11-07
pathName: metrics
---

# Cumulative Layout Shift (CLS)

{% BrowserCompat 'api.LayoutShift' %}

{% Aside 'key-term' %}
  Cumulative Layout Shift (CLS) is a stable Core Web Vital metric. It is an important, user-centric metric for
  measuring [visual
  stability](/user-centric-performance-metrics/#types-of-metrics) because it
  helps quantify how often users experience unexpected layout shifts&mdash;a low
  CLS helps ensure that the page is
  [delightful](/user-centric-performance-metrics/#questions).
{% endAside %}

Have you ever been reading an article online when something suddenly changes on
the page? Without warning, the text moves, and you've lost your place. Or even
worse: you're about to tap a link or a button, but in the instant before your
finger lands&mdash;BOOM&mdash;the link moves, and you end up clicking something
else!

Most of the time these kinds of experiences are just annoying, but in some
cases, they can cause real damage.

<figure>
  <video autoplay controls loop muted
    poster="https://storage.googleapis.com/web-dev-assets/layout-instability-api/layout-instability-poster.png"
    width="658" height="510">
    <source
      src="https://storage.googleapis.com/web-dev-assets/layout-instability-api/layout-instability2.webm"
      type="video/webm; codecs=vp8">
    <source
      src="https://storage.googleapis.com/web-dev-assets/layout-instability-api/layout-instability2.mp4"
      type="video/mp4; codecs=h264">
  </video>
  <figcaption>
    A screencast illustrating how layout instability can negatively affect
    users.
  </figcaption>
</figure>

Unexpected movement of page content usually happens because resources are loaded
asynchronously or DOM elements get dynamically added to the page above existing
content. The culprit might be an image or video with unknown dimensions, a font
that renders larger or smaller than its fallback, or a third-party ad or widget
that dynamically resizes itself.

What makes this issue even more problematic is that how a site functions in
development is often quite different from how users experience it. Personalized
or third-party content often doesn't behave the same in development as it does
in production, test images are often already in the developer's browser cache,
and API calls that run locally are often so fast that the delay isn't
noticeable.

The Cumulative Layout Shift (CLS) metric helps you address this problem by
measuring how often it's occurring for real users.

## What is CLS?

CLS is a measure of the largest burst of _layout shift scores_ for every
[unexpected](/cls/#expected-vs-unexpected-layout-shifts) layout shift that
occurs during the entire lifespan of a page.

A _layout shift_ occurs any time a visible element changes its position from one
rendered frame to the next. (See below for details on how individual [layout
shift scores](#layout-shift-score) are calculated.)

A burst of layout shifts, known as a [_session
window_](/evolving-cls/#why-a-session-window), is when one or more individual
layout shifts occur in rapid succession with less than 1-second in between each
shift and a maximum of 5 seconds for the total window duration.

The largest burst is the session window with the maximum cumulative score of all
layout shifts within that window.

<figure>
  <video controls autoplay loop muted
    width="658" height="452">
    <source src="https://storage.googleapis.com/web-dev-assets/better-layout-shift-metric/session-window.webm" type="video/webm">
    <source src="https://storage.googleapis.com/web-dev-assets/better-layout-shift-metric/session-window.mp4" type="video/mp4">
  </video>
  <figcaption>
    Example of session windows. Blue bars represent the scores of each individual layout shift.
  </figcaption>
</figure>

{% Aside 'caution' %}
Previously CLS measured the sum total of _all individual layout shift scores_
that occurred during the entire lifespan of the page.
To see which tools still provide the ability to benchmark against the original
implementation, check out [Evolving Cumulative Layout Shift in web tooling](/cls-web-tooling).
{% endAside %}

### What is a good CLS score?

To provide a good user experience, sites should strive to have a CLS score of
**0.1** or less. To ensure you're hitting this target for most of your users, a
good threshold to measure is the **75th percentile** of page loads, segmented
across mobile and desktop devices.

<figure>
  <picture>
    <source
      srcset="{{ "image/tcFciHGuF3MxnTr1y5ue01OGLBn2/9mWVASbWDLzdBUpVcjE1.svg" | imgix }}"
      media="(min-width: 640px)"
      width="800"
      height="200">
    {%
      Img
        src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/uqclEgIlTHhwIgNTXN3Y.svg",
        alt="Good CLS values are 0.1 or less, poor values are greater than 0.25, and anything in between needs improvement",
        width="640",
        height="480"
    %}
  </picture>
</figure>

{% Aside %}
  To learn more about the research and methodology behind this recommendation,
  see: [Defining the Core Web Vitals metrics
  thresholds](/defining-core-web-vitals-thresholds/)
{% endAside %}

## Layout shifts in detail

Layout shifts are defined by the [Layout Instability
API](https://github.com/WICG/layout-instability), which reports `layout-shift`
entries any time an element that is visible within the viewport changes its
start position (for example, its top and left position in the default [writing
mode](https://developer.mozilla.org/docs/Web/CSS/writing-mode)) between
two frames. Such elements are considered _unstable elements_.

Note that layout shifts only occur when existing elements change their start
position. If a new element is added to the DOM or an existing element changes
size, it doesn't count as a layout shift&mdash;as long as the change doesn't
cause other visible elements to change their start position.

### Layout shift score

To calculate the _layout shift score_, the browser looks at the viewport size
and the movement of _unstable elements_ in the viewport between two rendered
frames. The layout shift score is a product of two measures of that movement:
the _impact fraction_ and the _distance fraction_ (both defined below).

```text
layout shift score = impact fraction * distance fraction
```

### Impact fraction

The [impact
fraction](https://github.com/WICG/layout-instability#Impact-Fraction) measures
how _unstable elements_ impact the viewport area between two frames.

The union of the visible areas of all _unstable elements_ for the previous frame
_and_ the current frame&mdash;as a fraction of the total area of the
viewport&mdash;is the _impact fraction_ for the current frame.

{% Img src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/BbpE9rFQbF8aU6iXN1U6.png", alt="Impact fraction example with one _unstable element_", width="800", height="600", linkTo=true %}

In the image above there's an element that takes up half of the viewport in one
frame. Then, in the next frame, the element shifts down by 25% of the viewport
height. The red, dotted rectangle indicates the union of the element's visible
area in both frames, which, in this case, is 75% of the total viewport, so its
_impact fraction_ is `0.75`.

### Distance fraction

The other part of the layout shift score equation measures the distance that
unstable elements have moved, relative to the viewport. The _distance fraction_
is the greatest distance any _unstable element_ has moved in the frame (either
horizontally or vertically) divided by the viewport's largest dimension (width
or height, whichever is greater).

{% Img src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/ASnfpVs2n9winu6mmzdk.png", alt="Distance fraction example with one _unstable element_", width="800", height="600", linkTo=true %}

In the example above, the largest viewport dimension is the height, and the
unstable element has moved by 25% of the viewport height, which makes the
_distance fraction_ 0.25.

So, in this example the _impact fraction_ is `0.75` and the _distance fraction_
is `0.25`, so the _layout shift score_ is `0.75 * 0.25 = 0.1875`.

{% Aside %}
  Initially, the layout shift score was calculated based only on _impact
  fraction_. The _distance fraction_ was introduced to avoid overly penalizing
  cases where large elements shift by a small amount.
{% endAside %}

The next example illustrates how adding content to an existing element affects
the layout shift score:

{% Img src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xhN81DazXCs8ZawoCj0T.png", alt="Layout shift example with multiple stable and _unstable elements_", width="800", height="600", linkTo=true %}

The "Click Me!" button is appended to the bottom of the gray box with black
text, which pushes the green box with white text down (and partially out of the
viewport).

In this example, the gray box changes size, but its start position does not
change so it's not an _unstable element_.

The "Click Me!" button was not previously in the DOM, so its start position
doesn't change either.

The start position of the green box, however, does change, but since it's been
moved partially out of the viewport, the invisible area is not considered when
calculating the _impact fraction_. The union of the visible areas for the green
box in both frames (illustrated by the red, dotted rectangle) is the same as the
area of the green box in the first frame&mdash;50% of the viewport. The _impact
fraction_ is `0.5`.

The _distance fraction_ is illustrated with the purple arrow. The green box has
moved down by about 14% of the viewport so the _distance fraction_ is `0.14`.

The layout shift score is `0.5 x 0.14 = 0.07`.

This last example illustrates multiple _unstable elements_:

{% Img src="image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/J8AWG72qYlmbAHxjxuLg.png", alt="Layout shift example with stable and _unstable elements_ and viewport clipping", width="800", height="600", linkTo=true %}

In the first frame above there are four results of an API request for animals,
sorted in alphabetical order. In the second frame, more results are added to the
sorted list.

The first item in the list ("Cat") does not change its start position between
frames, so it's stable. Similarly, the new items added to the list were not
previously in the DOM, so their start positions don't change either. But the
items labelled "Dog", "Horse", and "Zebra" all shift their start positions,
making them _unstable elements_.

Again, the red, dotted rectangles represent the union of these three _unstable
elements_' before and after areas, which in this case is around 60% of the
viewport's area (_impact fraction_ of `0.60`).

The arrows represent the distances that _unstable elements_ have moved from
their starting positions. The "Zebra" element, represented by the blue arrow,
has moved the most, by about 30% of the viewport height. That makes the
_distance fraction_ in this example `0.3`.

The layout shift score is `0.60 x 0.3 = 0.18`.

### Expected vs. unexpected layout shifts

Not all layout shifts are bad. In fact, many dynamic web applications frequently
change the start position of elements on the page.

#### User-initiated layout shifts

A layout shift is only bad if the user isn't expecting it. On the other hand,
layout shifts that occur in response to user interactions (clicking a link,
pressing a button, typing in a search box and similar) are generally fine, as
long as the shift occurs close enough to the interaction that the relationship
is clear to the user.

For example, if a user interaction triggers a network request that may take a
while to complete, it's best to create some space right away and show a loading
indicator to avoid an unpleasant layout shift when the request completes. If the
user doesn't realize something is loading, or doesn't have a sense of when the
resource will be ready, they may try to click something else while
waiting&mdash;something that could move out from under them.

Layout shifts that occur within 500 milliseconds of user input will have the
[`hadRecentInput`](https://wicg.github.io/layout-instability/#dom-layoutshift-hadrecentinput)
flag set, so they can be excluded from calculations.

{% Aside 'caution' %}
  The `hadRecentInput` flag will only be true for discrete input events like tap,
  click, or keypress. Continuous interactions such as scrolls, drags, or pinch
  and zoom gestures are not considered "recent input". See the
  [Layout Instability Spec](https://github.com/WICG/layout-instability#recent-input-exclusion)
  for more details.
{% endAside %}


#### Animations and transitions

Animations and transitions, when done well, are a great way to update content on
the page without surprising the user. Content that shifts abruptly and
unexpectedly on the page almost always creates a bad user experience. But
content that moves gradually and naturally from one position to the next can
often help the user better understand what's going on, and guide them between
state changes.

Be sure to respect [`prefers-reduced-motion`](/prefers-reduced-motion/) browser settings, as some site visitors
can experience ill effects or attention issues from animation.

CSS [`transform`](https://developer.mozilla.org/docs/Web/CSS/transform)
property allows you to animate elements without triggering layout shifts:

* Instead of changing the `height` and `width` properties, use `transform:
  scale()`.
* To move elements around, avoid changing the `top`, `right`, `bottom`, or
  `left` properties and use `transform: translate()` instead.

## How to measure CLS

CLS can be measured [in the lab](/user-centric-performance-metrics/#in-the-lab)
or [in the field](/user-centric-performance-metrics/#in-the-field), and it's
available in the following tools:

{% Aside 'caution' %}
  Lab tools typically load pages in a synthetic environment and are thus only
  able to measure layout shifts that occur during page load. As a result, CLS
  values reported by lab tools for a given page may be less than what real users
  experience in the field.
{% endAside %}

### Field tools

- [Chrome User Experience
  Report](https://developer.chrome.com/docs/crux/)
- [PageSpeed Insights](https://pagespeed.web.dev/)
- [Search Console (Core Web Vitals
  report)](https://support.google.com/webmasters/answer/9205520)
- [`web-vitals` JavaScript library](https://github.com/GoogleChrome/web-vitals)

### Lab tools

- [Chrome DevTools](https://developer.chrome.com/docs/devtools/)
- [Lighthouse](https://developer.chrome.com/docs/lighthouse/overview/)
- [PageSpeed Insights](https://pagespeed.web.dev/)
- [WebPageTest](https://webpagetest.org/)

### Measure layout shifts in JavaScript

To measure layout shifts in JavaScript, you use the [Layout Instability API](https://github.com/WICG/layout-instability).

The following example shows how to create a [`PerformanceObserver`](https://developer.mozilla.org/docs/Web/API/PerformanceObserver) to log `layout-shift` entries to the console:

```js
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('Layout shift:', entry);
  }
}).observe({type: 'layout-shift', buffered: true});
```

### Measure CLS in JavaScript

To measure CLS in JavaScript, you need to group these unexpected `layout-shift` entries into sessions, and calculate the maximum session value. You can refer to the [`web vitals` JavaScript library source code](https://github.com/GoogleChrome/web-vitals/blob/main/src/onCLS.ts) which contains a reference implementation on how CLS is calculated.

In most cases, the current CLS value at the time the page is being unloaded is the final CLS value for that page, but there are a few important exceptions as noted in the next section. The `web vitals` JavaScript library accounts for these as much as possible, within the limitations of the Web APIs.

#### Differences between the metric and the API

- If a page is loaded in the background, or if it's backgrounded prior to the
  browser painting any content, then it should not report any CLS value.
- If a page is restored from the [back/forward
  cache](/bfcache/#impact-on-core-web-vitals), its CLS value should be reset to
  zero since users experience this as a distinct page visit.
- The API does not report `layout-shift` entries for shifts that occur within
  iframes but the metric does as they are part of the user experience of the
  page. This can
  [show as a difference between CrUX and RUM](/crux-and-rum-differences/#iframes).
  To properly measure CLS you should consider them. Sub-frames can
  use the API to report their `layout-shift` entries to the parent frame for
  [aggregation](https://github.com/WICG/layout-instability#cumulative-scores).

In addition to these exceptions, CLS has some added complexity due to the fact
that it measures the entire lifespan of a page:

- Users might keep a tab open for a _very_ long time&mdash;days, weeks, months.
  In fact, a user might never close a tab.
- On mobile operating systems, browsers typically do not run page unload
  callbacks for background tabs, making it difficult to report the "final"
  value.

To handle such cases, CLS should be reported any time a page is
background&mdash;in addition to any time it's unloaded (the [`visibilitychange`
event](https://developer.chrome.com/blog/page-lifecycle-api/#event-visibilitychange)
covers both of these scenarios). And analytics systems receiving this data will
then need to calculate the final CLS value on the backend.

Rather than memorizing and grappling with all of these cases yourself, developers can use the
[`web-vitals` JavaScript library](https://github.com/GoogleChrome/web-vitals) to
measure CLS, which accounts for everything mentioned above:

```js
import {onCLS} from 'web-vitals';

// Measure and log CLS in all situations
// where it needs to be reported.
onCLS(console.log);
```

{% Aside %}
  In some cases (such as cross-origin iframes) it's not possible to measure CLS
  in JavaScript. See the
  [limitations](https://github.com/GoogleChrome/web-vitals#limitations) section
  of the `web-vitals` library for details.
{% endAside %}

## How to improve CLS

A full guide on [optimizing CLS](/optimize-cls/) is available to guide you through the process of identifying layout shifts in the field and using lab data to drill down and optimize them.

## Additional resources

- Google Publisher Tag's guidance on
  [minimizing layout shift](https://developers.google.com/doubleclick-gpt/guides/minimize-layout-shift)
- [Understanding Cumulative Layout Shift](https://youtu.be/zIJuY-JCjqw) by
  [Annie Sullivan](https://anniesullie.com/) and [Steve
  Kobes](https://kobes.ca/) at [#PerfMatters](https://perfmattersconf.com/)
  (2020)

{% include 'content/metrics/metrics-changelog.njk' %}

# Custom metrics

There's a lot of value in having [user-centric metrics](/user-centric-performance-metrics/)
that you can measure, universally, on any given website. These metrics allow you
to:

- Understand how real users experience the web as a whole
- Easily compare your site to a competitor's
- Track useful and actionable data in your analytics tools without needing to
  write custom code

Universal metrics offer a good baseline, but in many cases you need to measure
_more_ than just these metrics in order to capture the full experience for your
particular site.

Custom metrics allow you to measure aspects of your site's experience that may
only apply to your site, such as:

- How long it takes for a single page app (SPA) to transition from one "page" to
  another
- How long it takes for a page to display data fetched from a database for
  logged-in users
- How long it takes for a server-side-rendered (SSR) app to
  [hydrate](https://addyosmani.com/blog/rehydration/)
- The cache hit rate for resources loaded by returning visitors
- The event latency of click or keyboard events in a game

## APIs to measure custom metrics

Historically web developers haven't had many low-level APIs to measure
performance, and as a result they've had to resort to hacks in order to measure
whether a site was performing well.

For example, it's possible to determine whether the main thread is blocked due
to long-running JavaScript tasks by running a `requestAnimationFrame` loop and
calculating the delta between each frame. If the delta is significantly longer
than the display's framerate, you can report that as a long task. Such hacks are
not recommended, though, because they actually affect performance themselves
(by draining battery, for example).

The first rule of effective performance measurement is to make sure your
performance measurement techniques aren't causing performance issues themselves.
So for any custom metrics you measure on your site, it's best to use one of the
following APIs if possible.

### Performance Observer
{% BrowserCompat 'api.PerformanceObserver' %}

Understanding the PerformanceObserver API is critical to creating custom
performance metrics because it's the mechanism by which you get data from all
other performance APIs discussed in this article.

With `PerformanceObserver` you can subscribe passively to performance-related
events, which means these APIs generally will not interfere with the performance
of the page, as their callbacks are generally fired during [idle
periods](https://w3c.github.io/requestidlecallback/#idle-periods).

You create a `PerformanceObserver` by passing it a callback to be run whenever
new performance entries are dispatched. Then you tell the observer what types of
entries to listen for via the
[`observe()`](https://developer.mozilla.org/docs/Web/API/PerformanceObserver/observe)
method:

```js
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });

  po.observe({type: 'some-entry-type'});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
```

The sections below list all the various entry types available for observing, but
in newer browsers you can inspect what entry types are available via the static
[`PerformanceObserver.supportedEntryTypes`](https://w3c.github.io/performance-timeline/#supportedentrytypes-attribute)
property.

{% Aside %}
  The object passed to the `observe()` method can also specify an `entryTypes`
  array (in order to observe more than one entry type via the same observer).
  While specifying `entryTypes` is an older option with wider browser support,
  using `type` is now preferred, as it allows for specifying additional
  entry-specific observation configuration (such as the `buffered` flag, discussed
  next).
{% endAside %}

#### Observing entries that already happened

By default, `PerformanceObserver` objects can only observe entries as they
occur. This can be problematic if you want to load your performance analytics
code lazily (to not block higher-priority resources).

To get historical entries (after they've occurred), set the `buffered` flag to
`true` when you call `observe()`. The browser will include historical entries
from its [performance entry
buffer](https://w3c.github.io/performance-timeline/#dfn-performance-entry-buffer)
the first time that your `PerformanceObserver` callback is called.

```js
po.observe({
  type: 'some-entry-type',
  buffered: true,
});
```

{% Aside %}
  To avoid memory issues, the performance entry buffer is not unlimited. For
  most typical page loads it's unlikely that the buffer will fill up and entries
  will be missed.
{% endAside %}

#### Legacy performance APIs to avoid

Prior to the Performance Observer API, developers could access performance
entries using the following three methods defined on the
[`performance`](https://w3c.github.io/performance-timeline/) object:

- [`getEntries()`](https://developer.mozilla.org/docs/Web/API/Performance/getEntries)
- [`getEntriesByName()`](https://developer.mozilla.org/docs/Web/API/Performance/getEntriesByName)
- [`getEntriesByType()`](https://developer.mozilla.org/docs/Web/API/Performance/getEntriesByType)

While these APIs are still supported, their usage is not recommended because
they don't allow you to listen for when new entries are emitted. In addition,
many new APIs (such as Long Tasks) are not exposed via the `performance` object,
they're only exposed via `PerformanceObserver`.

Unless you specifically need Internet Explorer compatibility, it's best to avoid
these methods in your code and use `PerformanceObserver` going forward.

### User Timing API

The [User Timing API](https://w3c.github.io/user-timing/) is your general
purpose measurement API for time-based metrics. It allows you to arbitrarily
mark points in time and then later measure the duration between those marks.

```js
// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();
// Record the time immediately after running a task.
performance.mark('myTask:end');

// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');
```

While APIs like `Date.now()` or `performance.now()` give you similar abilities,
the benefit of using the User Timing API is it integrates well with performance
tooling. For example, Chrome DevTools visualizes [User Timing measurements in the
Performance panel][devtools], and many analytics providers will also automatically track
any measurements you make and send the duration data to their analytics back
end.

To report User Timing measurements, you can use
[PerformanceObserver](#performance-observer) and register to observe entries of
type `measure`:

```js
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });
  // Start listening for `measure` entries to be dispatched.
  po.observe({type: 'measure', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
```

### Long Tasks API
{% BrowserCompat 'api.PerformanceLongTaskTiming' %}

The [Long Tasks API](https://w3c.github.io/longtasks/) is useful for knowing
when the browser's main thread is blocked for long enough to affect frame rate
or input latency. Currently the API will report any tasks that execute for
longer than 50 milliseconds (ms).

Anytime you need to run expensive code (or load and execute large scripts) it's
useful to track whether or not that code blocked the main thread. In fact, many
higher-level metrics are built on top of the Long Tasks API themselves (such as
[Time to Interactive (TTI)](/tti/) and [Total Blocking Time
(TBT)](https://developer.chrome.com/docs/lighthouse/performance/lighthouse-total-blocking-time/)).

To determine when long tasks happen, you can use
[PerformanceObserver](https://developer.mozilla.org/docs/Web/API/PerformanceObserver)
and register to observe entries of type `longtask`:

```js
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });
  // Start listening for `longtask` entries to be dispatched.
  po.observe({type: 'longtask', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
```

### Element Timing API
{% BrowserCompat 'api.PerformanceElementTiming' %}

The [Largest Contentful Paint (LCP)](/lcp/) metric is
useful for knowing when the largest image or text block was painted to the
screen, but in some cases you want to measure the render time of a different
element.

For these cases, you can use the [Element Timing
API](https://wicg.github.io/element-timing/). In fact, the Largest Contentful
Paint API is actually built on top of the Element Timing API and adds automatic
reporting of the largest contentful element, but you can report on additional
elements by explicitly adding the `elementtiming` attribute to them, and
registering a PerformanceObserver to observe the element entry type.

```html
<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
...
<script>
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((entryList) => {
    for (const entry of entryList.getEntries()) {
      // Log the entry and all associated details.
      console.log(entry.toJSON());
    }
  });
  // Start listening for `element` entries to be dispatched.
  po.observe({type: 'element', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
</script>
```

{% Aside 'gotchas' %}
  The types of elements considered for Largest Contentful Paint are
  the same as those observable via the Element Timing API. If you add the
  `elementtiming` attribute to an element that isn't one of those types, the
  attribute will be ignored.
{% endAside %}

### Event Timing API

The [First Input Delay (FID)](/fid/) metric measures the time from when a user
first interacts with a page to the time when the browser is actually able to
begin processing event handlers in response to that interaction. However, in
some cases it may also be useful to measure the event processing time itself as
well as the time until the next frame can be rendered.

This is possible with the [Event Timing
API](https://wicg.github.io/event-timing/) (which is used to measure FID) as it
exposes a number of timestamps in the event lifecycle, including:

- [`startTime`](https://w3c.github.io/performance-timeline/#dom-performanceentry-starttime):
  the time when the browser receives the event.
- [`processingStart`](https://wicg.github.io/event-timing/#dom-performanceeventtiming-processingstart):
  the time when the browser is able to begin processing event handlers for
  the event.
- [`processingEnd`](https://wicg.github.io/event-timing/#dom-performanceeventtiming-processingend):
  time when the browser finishes executing all synchronous code initiated from
  event handlers for this event.
- [`duration`](https://wicg.github.io/event-timing/#dom-performanceeventtiming-processingstart):
  the time (rounded to 8ms for security reasons) between when the browser
  receives the event until it's able to paint the next frame after finishing
  executing all synchronous code initiated from the event handlers.

The following example shows how to use these these values to create custom
measurements:

```js
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  const po = new PerformanceObserver((entryList) => {
    const firstInput = entryList.getEntries()[0];

    // Measure First Input Delay (FID).
    const firstInputDelay = firstInput.processingStart - firstInput.startTime;

    // Measure the time it takes to run all event handlers
    // Note: this does not include work scheduled asynchronously using
    // methods like `requestAnimationFrame()` or `setTimeout()`.
    const firstInputProcessingTime = firstInput.processingEnd - firstInput.processingStart;

    // Measure the entire duration of the event, from when input is received by
    // the browser until the next frame can be painted after processing all
    // event handlers.
    // Note: similar to above, this value does not include work scheduled
    // asynchronously using `requestAnimationFrame()` or `setTimeout()`.
    // And for security reasons, this value is rounded to the nearest 8ms.
    const firstInputDuration = firstInput.duration;

    // Log these values the console.
    console.log({
      firstInputDelay,
      firstInputProcessingTime,
      firstInputDuration,
    });
  });

  po.observe({type: 'first-input', buffered: true});
} catch (error) {
  // Do nothing if the browser doesn't support this API.
}
```

### Resource Timing API

The [Resource Timing API](https://w3c.github.io/resource-timing/) gives
developers detailed insight into how resources for a particular page were
loaded. Despite the name of the API, the information it provides is not just
limited to timing data (though there's [plenty of
that](https://w3c.github.io/resource-timing/#processing-model)). Other data you
can access includes:

- [initiatorType](https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-initiatortype):
  how the resource was fetched: such as from a `<script>` or `<link>` tag, or
  from `fetch()`
- [nextHopProtocol](https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-nexthopprotocol):
  the protocol used to fetch the resource, such as `h2` or `quic`
- [encodedBodySize](https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-encodedbodysize)/[decodedBodySize](https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-decodedbodysize)]:
  the size of the resource in its encoded or decoded form (respectively)
- [transferSize](https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-transfersize):
  the size of the resource that was actually transferred over the network. When
  resources are fulfilled via the cache, this value can be much smaller than the
  `encodedBodySize`, and in some cases it can be zero (if no cache revalidation
  is required).

Note, you can use the `transferSize` property of resource timing entries to
measure a _cache hit rate_ metric or perhaps even a _total cached resource size_
metric, which may be useful in understanding how your resource caching strategy
affects performance for repeat visitors.

The following example logs all resources requested by the page and indicates
whether or not each resource was fulfilled via the cache.

```js
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log(entry.name, entry.transferSize === 0);
    }
  });
  // Start listening for `resource` entries to be dispatched.
  po.observe({type: 'resource', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
```

### Navigation Timing API
{% BrowserCompat 'api.PerformanceNavigationTiming' %}

The [Navigation Timing API](https://w3c.github.io/navigation-timing/) is similar
to the Resource Timing API, but it reports only [navigation
requests](https://developer.chrome.com/docs/workbox/caching-strategies-overview).
The `navigation` entry type is also similar to the `resource` entry type, but it
contains some [additional
information](https://w3c.github.io/navigation-timing/#sec-PerformanceNavigationTiming)
specific to only navigation requests (such as when the `DOMContentLoaded` and
`load` events fire).

One metric many developers track to understand server response time ([Time to
First Byte](https://en.wikipedia.org/wiki/Time_to_first_byte)) is available via
the Navigation Timing API—specifically it's entry's `responseStart` timestamp.

```js
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // If transferSize is 0, the resource was fulfilled via the cache.
      console.log('Time to first byte', entry.responseStart);
    }
  });
  // Start listening for `navigation` entries to be dispatched.
  po.observe({type: 'navigation', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
```

Another metric developers who use service worker may care about is the service
worker startup time for navigation requests. This is the amount of time it takes
the browser to start the service worker thread before it can start intercepting
fetch events.

The service worker startup time for a particular navigation request can be
determined from the delta between `entry.responseStart` and `entry.workerStart`.

```js
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      console.log('Service Worker startup time:',
          entry.responseStart - entry.workerStart);
    }
  });
  // Start listening for `navigation` entries to be dispatched.
  po.observe({type: 'navigation', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
```

### Server Timing API

The [Server Timing API](https://w3c.github.io/server-timing/) allows you to pass
request-specific timing data from your server to the browser via response
headers. For example, you can indicate how long it took to lookup data in a
database for a particular request—which can be useful in debugging performance
issues caused by slowness on the server.

For developers who use third-party analytics providers, the Server Timing API is
the only way to correlate server performance data with other business metrics
that these analytics tools may be measuring.

To specify server timing data in your responses, you can use the `Server-Timing`
response header. Here's an example.

```http
HTTP/1.1 200 OK

Server-Timing: miss, db;dur=53, app;dur=47.2
```

Then, from your pages, you can read this data on both `resource` or `navigation`
entries from the Resource Timing and Navigation Timing APIs.

```js
// Catch errors since some browsers throw when using the new `type` option.
// https://bugs.webkit.org/show_bug.cgi?id=209216
try {
  // Create the performance observer.
  const po = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      // Logs all server timing data for this response
      console.log('Server Timing', entry.serverTiming);
    }
  });
  // Start listening for `navigation` entries to be dispatched.
  po.observe({type: 'navigation', buffered: true});
} catch (e) {
  // Do nothing if the browser doesn't support this API.
}
```

[devtools]: https://developer.chrome.com/blog/new-in-devtools-67/#tabs

# First Contentful Paint (FCP)

{% BrowserCompat 'api.PerformancePaintTiming' %}

{% Aside %}
  First Contentful Paint (FCP) is an important, user-centric metric for
  measuring [perceived load
  speed](/user-centric-performance-metrics/#types-of-metrics) because it marks
  the first point in the page load timeline where the user can see anything on
  the screen&mdash;a fast FCP helps reassure the user that something is
  [happening](/user-centric-performance-metrics/#questions).
{% endAside %}

## What is FCP?

The First Contentful Paint (FCP) metric measures the time from when the page
starts loading to when any part of the page's content is rendered on the screen.
For this metric, "content" refers to text, images (including background images),
`<svg>` elements, or non-white `<canvas>` elements.

{% Img src="image/admin/3UhlOxRc0j8Vc4DGd4dt.png", alt="FCP timeline from google.com", width="800", height="311", linkTo=true %}

In the above load timeline, FCP happens in the second
frame, as that's when the first text and image elements are rendered to the
screen.

You'll notice that though some of the content has rendered, not all of it has
rendered. This is an important distinction to make between _First_ Contentful
Paint (FCP) and _[Largest Contentful Paint (LCP)](/lcp/)_
&mdash;which aims to measure when the page's main contents have finished
loading.

### What is a good FCP score?

To provide a good user experience, sites should strive to have a First
Contentful Paint of **1.8 seconds** or less. To ensure you're hitting this
target for most of your users, a good threshold to measure is the **75th
percentile** of page loads, segmented across mobile and desktop devices.

<figure>
  <picture>
    <source
      srcset="{{ "image/eqprBhZUGfb8WYnumQ9ljAxRrA72/V1mtKJenViYAhn05WxqR.svg" | imgix }}"
      media="(min-width: 640px)"
      width="800"
      height="200">
    {%
      Img
        src="image/eqprBhZUGfb8WYnumQ9ljAxRrA72/vQKpz0S2SGnnoXHMDidj.svg",
        alt="Good FCP values are 1.8 seconds or less, poor values are greater than 3.0 seconds, and anything in between needs improvement",
        width="640",
        height="480"
    %}
  </picture>
</figure>

## How to measure FCP

FCP can be measured [in the lab](/user-centric-performance-metrics/#in-the-lab)
or [in the field](/user-centric-performance-metrics/#in-the-field), and it's
available in the following tools:

### Field tools

- [PageSpeed Insights](https://pagespeed.web.dev/)
- [Chrome User Experience
  Report](https://developer.chrome.com/docs/crux/)
- [Search Console (Speed
  Report)](https://webmasters.googleblog.com/2019/11/search-console-speed-report.html)
- [`web-vitals` JavaScript library](https://github.com/GoogleChrome/web-vitals)

### Lab tools

- [Lighthouse](https://developer.chrome.com/docs/lighthouse/overview/)
- [Chrome DevTools](https://developer.chrome.com/docs/devtools/)
- [PageSpeed Insights](https://pagespeed.web.dev/)

### Measure FCP in JavaScript

To measure FCP in JavaScript, you can use the [Paint Timing
API](https://w3c.github.io/paint-timing/). The following example shows how to
create a
[`PerformanceObserver`](https://developer.mozilla.org/docs/Web/API/PerformanceObserver)
that listens for a `paint` entry with the name `first-contentful-paint` and logs
it to the console.

```js
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntriesByName('first-contentful-paint')) {
    console.log('FCP candidate:', entry.startTime, entry);
  }
}).observe({type: 'paint', buffered: true});
```

{% Aside 'warning' %}

  This code shows how to log the `first-contentful-paint` entry to the console,
  but measuring FCP in JavaScript is more complicated. See below for details:

{% endAside %}

In the above example, the logged `first-contentful-paint` entry will tell you
when the first contentful element was painted. However, in some cases this entry
is not valid for measuring FCP.

The following section lists the differences between what the API reports and how
the metric is calculated.

#### Differences between the metric and the API

- The API will dispatch a `first-contentful-paint` entry for pages loaded in a
  background tab, but those pages should be ignored when calculating FCP (first
  paint timings should only be considered if the page was in the foreground the
  entire time).
- The API does not report `first-contentful-paint` entries when the page is
  restored from the [back/forward cache](/bfcache/#impact-on-core-web-vitals),
  but FCP should be measured in these cases since users experience them as
  distinct page visits.
- The API [may not report paint timings from cross-origin
  iframes](https://w3c.github.io/paint-timing/#:~:text=cross-origin%20iframes),
  but to properly measure FCP you should consider all frames. Sub-frames can use
  the API to report their paint timings to the parent frame for aggregation.

Rather than memorizing all these subtle differences, developers can use the
[`web-vitals` JavaScript library](https://github.com/GoogleChrome/web-vitals) to
measure FCP, which handles these differences for you (where possible):

```js
import {onFCP} from 'web-vitals';

// Measure and log FCP as soon as it's available.
onFCP(console.log);
```

You can refer to [the source code for
`onFCP()`](https://github.com/GoogleChrome/web-vitals/blob/main/src/onFCP.ts)
for a complete example of how to measure FCP in JavaScript.

{% Aside %}
  In some cases (such as cross-origin iframes) it's not possible to measure FCP
  in JavaScript. See the
  [limitations](https://github.com/GoogleChrome/web-vitals#limitations) section
  of the `web-vitals` library for details.
{% endAside %}

## How to improve FCP

To learn how to improve FCP for a specific site, you can run a Lighthouse
performance audit and pay attention to any specific
[opportunities](https://developer.chrome.com/docs/lighthouse/performance/#opportunities) or
[diagnostics](https://developer.chrome.com/docs/lighthouse/performance/#diagnostics) the audit suggests.

To learn how to improve FCP in general (for any site), refer to the following
performance guides:

- [Eliminate render-blocking resources](https://developer.chrome.com/docs/lighthouse/performance/render-blocking-resources/)
- [Minify CSS](https://developer.chrome.com/docs/lighthouse/performance/unminified-css/)
- [Remove unused CSS](https://developer.chrome.com/docs/lighthouse/performance/unused-css-rules/)
- [Remove unused JavaScript](https://developer.chrome.com/docs/lighthouse/performance/unused-javascript/)
- [Preconnect to required origins](https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preconnect/)
- [Reduce server response times (TTFB)](/ttfb/)
- [Avoid multiple page redirects](https://developer.chrome.com/docs/lighthouse/performance/redirects/)
- [Preload key requests](https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preload/)
- [Avoid enormous network payloads](https://developer.chrome.com/docs/lighthouse/performance/total-byte-weight/)
- [Serve static assets with an efficient cache policy](https://developer.chrome.com/docs/lighthouse/performance/uses-long-cache-ttl/)
- [Avoid an excessive DOM size](https://developer.chrome.com/docs/lighthouse/performance/dom-size/)
- [Minimize critical request depth](https://developer.chrome.com/docs/lighthouse/performance/critical-request-chains/)
- [Ensure text remains visible during webfont load](https://developer.chrome.com/docs/lighthouse/performance/font-display/)
- [Keep request counts low and transfer sizes small](https://developer.chrome.com/docs/lighthouse/performance/resource-summary/)

{% include 'content/metrics/metrics-changelog.njk' %}

# First Input Delay (FID)

{% BrowserCompat 'api.PerformanceEventTiming' %}

{% Aside %}
  First Input Delay (FID) is the [stable](/vitals/#stable) Core Web Vital metric for measuring load responsiveness because it quantifies the experience users feel when trying to interact with unresponsive pages—a low FID helps ensure that the page is usable. FID will be [replaced by Interaction to Next Paint (INP)](/inp-cwv/) as a Core Web Vital in March 2024.
{% endAside %}

We all know how important it is to make a good first impression. It's important
when meeting new people, and it's also important when building experiences on
the web.

On the web, a good first impression can make the difference between someone
becoming a loyal user or them leaving and never coming back. The question is,
what makes for a good impression, and how do you measure what kind of impression
you're likely making on your users?

On the web, first impressions can take a lot of different forms&mdash;we have
first impressions of a site's design and visual appeal as well as first
impressions of its speed and responsiveness.

While it is hard to measure how much users like a site's design with web APIs,
measuring its speed and responsiveness is not!

The first impression users have of how fast your site loads can be measured with
[First Contentful Paint (FCP)](/fcp/). But how fast your site can paint pixels
to the screen is just part of the story. Equally important is how responsive
your site is when users try to interact with those pixels!

The First Input Delay (FID) metric helps measure your user's first impression of
your site's interactivity and responsiveness.

## What is FID?

FID measures the time from when a user first interacts with a page (that is, when
they click a link, tap on a button, or use a custom, JavaScript-powered control)
to the time when the browser is actually able to begin processing event handlers
in response to that interaction.

### What is a good FID score?

To provide a good user experience, sites should strive to have a First Input
Delay of **100 milliseconds** or less. To ensure you're hitting this target for
most of your users, a good threshold to measure is the **75th percentile** of
page loads, segmented across mobile and desktop devices.

<figure>
  <picture>
    <source
      srcset="{{ "image/tcFciHGuF3MxnTr1y5ue01OGLBn2/eXyvkqRHQZ5iG38Axh1Z.svg" | imgix }}"
      media="(min-width: 640px)"
      width="800"
      height="200">
    {%
      Img
        src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/Se4TiXIdp8jtLJVScWed.svg",
        alt="Good FID values are 2.5 seconds or less, poor values are greater than 4.0 seconds, and anything in between needs improvement",
        width="640",
        height="480"
    %}
  </picture>
</figure>

{% Aside %}
  To learn more about the research and methodology behind this recommendation,
  see: [Defining the Core Web Vitals metrics
  thresholds](/defining-core-web-vitals-thresholds/)
{% endAside %}

## FID in detail

As developers who write code that responds to events, we often assume our code
is going to be run immediately&mdash;as soon as the event happens. But as users,
we've all frequently experienced the opposite&mdash;we've loaded a web page on
our phone, tried to interact with it, and then been frustrated when nothing
happened.

In general, input delay (a.k.a. input latency) happens because the browser's
main thread is busy doing something else, so it can't (yet) respond to the user.
One common reason this might happen is the browser is busy parsing and executing
a large JavaScript file loaded by your app. While it's doing that, it can't run
any event listeners because the JavaScript it's loading might tell it to do
something else.

{% Aside 'gotchas' %} FID only measures the "delay" in event processing. It does
  not measure the event processing time itself nor the time it takes the browser
  to update the UI after running event handlers. While this time does affect the
  user experience, including it as part of FID would incentivize developers to
  respond to events asynchronously&mdash;which would improve the metric but
  likely make the experience worse. See [why only consider the input
  delay](#why-only-consider-the-input-delay) below for more details. {% endAside
  %}

Consider the following timeline of a typical web page load:

{% Img src="image/admin/9tm3f6pwlHMqNKuFvaP0.svg", alt="Example page load trace", width="800", height="260", linkTo=true %}

The above visualization shows a page that's making a couple of network requests
for resources (most likely CSS and JS files), and&mdash;after those resources
are finished downloading&mdash;they're processed on the main thread.

This results in periods where the main thread is momentarily busy, which is
indicated by the beige-colored
[task](https://html.spec.whatwg.org/multipage/webappapis.html#concept-task)
blocks.

Long first input delays typically occur between [First Contentful Paint
(FCP)](/fcp/) and [Time to Interactive (TTI)](/tti/) because the page has
rendered some of its content but isn't yet reliably interactive. To illustrate
how this can happen, FCP and TTI have been added to the timeline:

{% Img src="image/admin/24Y3T5sWNuZD9fKhkuER.svg", alt="Example page load trace with FCP and TTI", width="800", height="340", linkTo=true %}

You may have noticed that there's a fair amount of time (including three [long
tasks](/custom-metrics/#long-tasks-api)) between FCP and TTI, if a user tries to
interact with the page during that time (for example, by clicking on a link), there will be a
delay between when the click is received and when the main thread is able to
respond.

Consider what would happen if a user tried to interact with the page near the
beginning of the longest task:

{% Img src="image/admin/krOoeuQ4TWCbt9t6v5Wf.svg", alt="Example page load trace with FCP, TTI, and FID", width="800", height="380", linkTo=true %}

Because the input occurs while the browser is in the middle of running a task,
it has to wait until the task completes before it can respond to the input. The
time it must wait is the FID value for this user on this page.

{% Aside %}
  In this example the user just happened to interact with the
  page at the beginning of the main thread's most busy period. If the user had
  interacted with the page just a moment earlier (during the idle period) the
  browser could have responded right away. This variance in input delay
  underscores the importance of looking at the distribution of FID values when
  reporting on the metric. You can read more about this in the section below on
  analyzing and reporting on FID data.
{% endAside %}

### What if an interaction doesn't have an event listener?

FID measures the delta between when an input event is received and when the main
thread is next idle. This means FID is measured **even in cases where an event
listener has not been registered.** The reason is because many user interactions
do not require an event listener but _do_ require the main thread to be idle in
order to run.

For example, all of the following HTML elements need to wait for
in-progress tasks on the main thread to complete prior to responding to user
interactions:

- Text fields, checkboxes, and radio buttons (`<input>`, `<textarea>`)
- Select dropdowns (`<select>`)
- links (`<a>`)

### Why only consider the first input?

While a delay from any input can lead to a bad user experience, we primarily
recommend measuring the first input delay for a few reasons:

- The first input delay will be the user's first impression of your site's
  responsiveness, and first impressions are critical in shaping our overall
  impression of a site's quality and reliability.
- The biggest interactivity issues we see on the web today occur during page
  load. Therefore, we believe initially focusing on improving site's first user
  interaction will have the greatest impact on improving the overall
  interactivity of the web.
- The recommended solutions for how sites should fix high first input delays
  (code splitting, loading less JavaScript upfront, etc.) are not necessarily
  the same solutions for fixing slow input delays after page load. By separating
  out these metrics we'll be able to provide more specific performance
  guidelines to web developers.

### What counts as a first input?

FID is a metric that measures a page's responsiveness during load. As such, it
only focuses on input events from discrete actions like clicks, taps, and key
presses.

Other interactions, like scrolling and zooming, are continuous actions and have
completely different performance constraints (also, browsers are often able to
hide their latency by running them on a separate thread).

To put this another way, FID focuses on the R (responsiveness) in the [RAIL
performance
model](/rail/), whereas
scrolling and zooming are more related to A (animation), and their performance
qualities should be evaluated separately.

### What if a user never interacts with your site?

Not all users will interact with your site every time they visit. And not all
interactions are relevant to FID (as mentioned in the previous section). In
addition, some user's first interactions will be at bad times (when the main
thread is busy for an extended period of time), and some user's first
interactions will be at good times (when the main thread is completely idle).

This means some users will have no FID values, some users will have low FID
values, and some users will probably have high FID values.

How you track, report on, and analyze FID will probably be quite a bit different
from other metrics you may be used to. The next section explains how best to do
this.

### Why only consider the input delay?

As mentioned above, FID only measures the "delay" in event processing. It does
not measure the event processing time itself nor the time it takes the browser
to update the UI after running event handlers.

Even though this time is important to the user and _does_ affect the experience,
it's not included in this metric because doing so could incentivize developers
to add workarounds that actually make the experience worse&mdash;that is, they
could wrap their event handler logic in an asynchronous callback (via
`setTimeout()` or `requestAnimationFrame()`) in order to separate it from the
task associated with the event. The result would be an improvement in the metric
score but a slower response as perceived by the user.

However, while FID only measure the "delay" portion of event latency, developers
who want to track more of the event lifecycle can do so using the [Event Timing
API](https://wicg.github.io/event-timing/). See the guide on [custom
metrics](/custom-metrics/#event-timing-api) for more details.

## How to measure FID

FID is a metric that can only be measured [in the
field](/user-centric-performance-metrics/#in-the-field), as it requires a real
user to interact with your page. You can measure FID with the following tools.

{% Aside %}
  FID requires a real user and thus cannot be measured in the lab. However, the
  [Total Blocking Time (TBT)](/tbt/) metric is lab-measurable, correlates well
  with FID in the field, and also captures issues that affect interactivity.
  Optimizations that improve TBT in the lab should also improve FID for your
  users.
{% endAside %}

### Field tools

- [Chrome User Experience
  Report](https://developer.chrome.com/docs/crux/)
- [PageSpeed Insights](https://pagespeed.web.dev/)
- [Search Console (Core Web Vitals
  report)](https://support.google.com/webmasters/answer/9205520)
- [`web-vitals` JavaScript library](https://github.com/GoogleChrome/web-vitals)

### Measure FID in JavaScript

To measure FID in JavaScript, you can use the [Event Timing
API](https://wicg.github.io/event-timing). The following example shows how to
create a
[`PerformanceObserver`](https://developer.mozilla.org/docs/Web/API/PerformanceObserver)
that listens for
[`first-input`](https://wicg.github.io/event-timing/#sec-performance-event-timing)
entries and logs them to the console:

```js
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    const delay = entry.processingStart - entry.startTime;
    console.log('FID candidate:', delay, entry);
  }
}).observe({type: 'first-input', buffered: true});
```

{% Aside 'warning' %}
  This code shows how to log `first-input` entries to the console and calculate
  their delay. However, measuring FID in JavaScript is more complicated. See
  below for details:
{% endAside %}

In the above example, the `first-input` entry's delay value is measured by
taking the delta between the entry's `startTime` and `processingStart`
timestamps. In most cases this will be the FID value; however, not all
`first-input` entries are valid for measuring FID.

The following section lists the differences between what the API reports and how
the metric is calculated.

#### Differences between the metric and the API

- The API will dispatch `first-input` entries for pages loaded
  in a background tab but those pages should be ignored when calculating FID.
- The API will also dispatch `first-input` entries if the page was backgrounded
  prior to the first input occurring, but those pages should also be ignored
  when calculating FID (inputs are only considered if the page was in the
  foreground the entire time).
- The API does not report `first-input` entries when the page is restored from
  the [back/forward cache](/bfcache/#impact-on-core-web-vitals), but FID should
  be measured in these cases since users experience them as distinct page
  visits.
- The API does not report inputs that occur within iframes but the metric does
  as they are part of the user experience of the page. This can
  [show as a difference between CrUX and RUM](/crux-and-rum-differences/#iframes).
  To properly measure FID you should consider them. Sub-frames can use the API
  to report their `first-input` entries to the parent frame for aggregation.

Rather than memorizing all these subtle differences, developers can use the
[`web-vitals` JavaScript library](https://github.com/GoogleChrome/web-vitals) to
measure FID, which handles these differences for you (where possible):

```js
import {onFID} from 'web-vitals';

// Measure and log FID as soon as it's available.
onFID(console.log);
```

You can refer to [the source code for
`onFID()`](https://github.com/GoogleChrome/web-vitals/blob/main/src/onFID.ts)
for a complete example of how to measure FID in JavaScript.

{% Aside %}
  In some cases (such as cross-origin iframes) it's not possible to measure FID
  in JavaScript. See the
  [limitations](https://github.com/GoogleChrome/web-vitals#limitations) section
  of the `web-vitals` library for details.
{% endAside %}

### Analyzing and reporting on FID data

Due to the expected variance in FID values, it's critical that when reporting on
FID you look at the distribution of values and focus on the higher percentiles.

While [choice of
percentile](/defining-core-web-vitals-thresholds/#choice-of-percentile) for all
Core Web Vitals thresholds is the 75th, for FID in particular we still strongly
recommend looking at the 95th–99th percentiles, as those will correspond to the
particularly bad first experiences users are having with your site. And it will
show you the areas that need the most improvement.

This is true even if you segment your reports by device category or type. For
example, if you run separate reports for desktop and mobile, the FID value you
care most about on desktop should be the 95th–99th percentile of desktop users,
and the FID value you care about most on mobile should be the 95th–99th
percentile of mobile users.

## How to improve FID

A full guide on [optimizing FID](/optimize-fid/) is available to guide you through techniques to improve this metric.

{% include 'content/metrics/metrics-changelog.njk' %}

# Interaction to Next Paint (INP)

{% BrowserCompat 'api.PerformanceEventTiming.interactionId' %}

{% Aside %}
Interaction to Next Paint (INP) is a [pending](/vitals/#pending) Core Web Vital metric that will [replace First Input Delay (FID)](/inp-cwv/) in March 2024. INP assesses responsiveness using data from the Event Timing API. When an interaction causes a page to become unresponsive, that is a poor user experience. INP observes the latency of all interactions a user has made with the page, and reports a single value which all (or nearly all) interactions were below. A low INP means the page was consistently able to respond quickly to all&mdash;or the vast majority&mdash;of user interactions.
{% endAside %}

Chrome usage data shows that 90% of a user's time on a page is spent _after_ it loads, Thus, careful measurement of responsiveness _throughout_ the page lifecycle is important. This is what the INP metric assesses.

Good responsiveness means that a page responds quickly to interactions made with it. When a page responds to an interaction, the result is _visual feedback_, which is presented by the browser in the next frame the browser presents. Visual feedback tells you if, for example, an item you add to an online shopping cart is indeed being added, whether a mobile navigation menu has opened, if a login form's contents are being authenticated by the server, and so forth.

Some interactions will naturally take longer than others, but for especially complex interactions, it's important to quickly present some initial visual feedback as a cue to the user that something is _happening_.  The time until the next paint is the earliest opportunity to do this. Therefore, the intent of INP is not to measure all the eventual effects of the interaction (such as network fetches and UI updates from other asynchronous operations), but the time in which the _next_ paint is being blocked. By delaying visual feedback, you may be giving users the impression that the page is not responding to their actions.

The goal of INP is to ensure the time from when a user initiates an interaction until the next frame is painted is as short as possible, for all or most interactions the user makes.

In the following video, the example on the right gives immediate visual feedback that an accordion is opening. It also demonstrates how poor responsiveness can cause multiple unintended responses to input because the user thinks the experience is broken.

<style>
  #responsiveness-video {
    height: auto;
    aspect-ratio: 1445 / 370;
  }
</style>
<figure>
  {% Video src="video/jL3OLOhcWUQDnR4XjewLBx4e3PC3/WSmcjiQC4lyLxGoES4dd.mp4", autoplay="true", loop="true", muted="true", playsinline="true", width="1445", height="370", id="responsiveness-video" %}
  <figcaption>
    An example of poor versus good responsiveness. At left, long tasks block the accordion from opening. This causes the user to click multiple times, thinking the experience is broken. When the main thread catches up, it processes the delayed inputs, resulting in the accordion opening and closing unexpectedly.
  </figcaption>
</figure>

This article explains how INP works, how to measure it, and points to resources for improving it.

## What is INP?

INP is a metric that assesses a page's overall responsiveness to user interactions by observing the latency of all click, tap, and keyboard interactions that occur throughout the lifespan of a user's visit to a page. The final INP value is the longest interaction observed, ignoring outliers.

{% Details %}

{% DetailsSummary %}
A note on how INP is calculated
{% endDetailsSummary %}

As stated above, INP is calculated by observing all the interactions made with a page. For most sites the interaction with the worst latency is reported as INP. However, for pages with large numbers of interactions, random hiccups can result in an unusually high interaction on an otherwise responsive site. The more interactions, the more likely this is to happen. To counter this, and give a better measure of the actual responsiveness for those types of pages, we ignore one highest interaction for every 50 interactions. The vast majority of page experiences do not have over 50 interactions so will report the worst interaction. The 75th percentile of all the page views is then reported as usual, which further removes outliers to give a value that the vast majority of users experience or better.
{% endDetails %}

An _interaction_ is a group of event handlers that fire during the same logical user gesture. For example, "tap" interactions on a touchscreen device include multiple events, such as `pointerup`, `pointerdown`, and `click`. An interaction can be driven by JavaScript, CSS, built-in browser controls (such as form elements), or a combination thereof.

An interaction's latency consists of the single longest [duration](https://w3c.github.io/event-timing/#ref-for-dom-performanceentry-duration%E2%91%A1:~:text=The%20Event%20Timing%20API%20exposes%20a%20duration%20value%2C%20which%20is%20meant%20to%20be%20the%20time%20from%20when%20user%20interaction%20occurs%20(estimated%20via%20the%20Event%27s%20timeStamp)%20to%20the%20next%20time%20the%20rendering%20of%20the%20Event%27s%20relevant%20global%20object%27s%20associated%20Document%E2%80%99s%20is%20updated) of a group of event handlers that drives the interaction, from the time the user begins the interaction to the moment the next frame is presented with visual feedback.

{% Aside 'important' %}
For more details on how INP is measured, read the ["What's in an interaction?"](#whats-in-an-interaction) section.
{% endAside %}

### What is a good INP score?

Pinning labels such as "good" or "poor" on a responsiveness metric is difficult. On one hand, you want to encourage development practices that prioritize good responsiveness. On the other hand, you must account for the fact that there's considerable variability in the capabilities of devices people use to set achievable development expectations.

To ensure you're delivering user experiences with good responsiveness, a good threshold to measure is the **75th percentile** of page loads recorded in the field, segmented across mobile and desktop devices:

- An INP below or at **200 milliseconds** means that your page has **good responsiveness**.
- An INP above **200 milliseconds** and below or at **500 milliseconds** means that your page's responsiveness **needs improvement**.
- An INP above **500 milliseconds** means that your page has **poor responsiveness**.

<style>
  .inp-mobile {
    display: inline;
  }

  .inp-desktop {
    display: none;
  }

  @media screen and (min-width: 640px) {
    .inp-mobile {
      display: none;
    }

    .inp-desktop {
      display: inline;
    }
  }
</style>
<figure>
  <svg title="A diagram of the INP thresholds. An INP at or below 200 milliseconds is considered good. Between 200 and 500 milliseconds suggests a page's responsiveness needs improvement. Anything over 500 milliseconds means that a page's responsiveness is poor." class="inp-mobile" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 296.6 220.2" style="enable-background:new 0 0 296.6 220.2" xml:space="preserve"><style>.st0{fill:#2979FF} .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#0CCE6B} .st2{fill:#191919} .st3{fill-rule:evenodd;clip-rule:evenodd;fill:#FFA400} .st4{fill-rule:evenodd;clip-rule:evenodd;fill:#FF4E42} @media screen and (prefers-color-scheme: light){.st2{fill:#191919}} [data-user-theme=light] .st2 {fill:#191919} @media screen and (prefers-color-scheme: dark){.st2{fill:#fff}} [data-user-theme=dark] .st2{fill:#fff}</style><path class="st0" d="M83.3 63V0h11.9v63H83.3zm26.3 0V0h13.8l25.3 42.2h.7l-.7-12.1V0h11.8v63H148l-26.8-44.6h-.7l.7 12.1V63h-11.6zm65.4 0V0h22.2c4.1 0 7.7.8 10.9 2.5s5.8 4 7.7 7 2.9 6.4 2.9 10.4c0 3.9-1 7.4-2.9 10.4s-4.5 5.4-7.7 7c-3.2 1.7-6.9 2.5-10.9 2.5h-15.6V28.6h15.9c2.1 0 3.8-.4 5.2-1.2s2.5-1.9 3.2-3.2c.7-1.3 1.1-2.8 1.1-4.3s-.4-2.9-1.1-4.2c-.7-1.3-1.8-2.3-3.2-3.2s-3.1-1.2-5.2-1.2h-10.7V63H175z"/><path class="st1" d="M0 137.1h96v38.4H0v-38.4z"/><path class="st2" d="M34 161c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-.9-.4-.4-.7-.9-1-1.4-.2-.6-.3-1.1-.3-1.8 0-.6.1-1.2.3-1.8.2-.6.6-1 1-1.4.4-.4.9-.7 1.4-.9.5-.2 1.1-.3 1.8-.3s1.3.1 1.8.3c.6.2 1 .6 1.4 1l-.9.9c-.2-.2-.4-.4-.6-.5-.2-.1-.5-.3-.8-.3-.3-.1-.6-.1-.9-.1-.4 0-.8.1-1.2.2-.4.1-.7.4-1 .6-.3.3-.5.6-.7 1-.2.4-.2.8-.2 1.3s.1.9.2 1.3c.2.4.4.7.7 1 .3.3.6.5 1 .7.4.1.8.2 1.2.2.4 0 .8-.1 1.1-.2.3-.1.6-.3.9-.5.3-.2.5-.5.6-.8.2-.3.3-.6.3-1H34v-1.2h4.2v.7c0 .6-.1 1.2-.3 1.7-.2.5-.5.9-.9 1.3s-.8.6-1.3.8c-.5.3-1.1.4-1.7.4zm9.5 0c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8 0-.6.1-1.2.3-1.8.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8 0 .6-.1 1.2-.3 1.8-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.5.2-1.1.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm9.9 1.2c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8 0-.6.1-1.2.3-1.8.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8 0 .6-.1 1.2-.3 1.8-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.6.2-1.2.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm6 1.1v-8.6h2.8c.9 0 1.7.2 2.3.5.7.4 1.2.9 1.5 1.5.4.6.5 1.4.5 2.2 0 .8-.2 1.6-.5 2.2-.4.6-.9 1.2-1.5 1.5-.6.4-1.4.5-2.3.5h-2.8zm1.3-1.3h1.4c.6 0 1.2-.1 1.7-.4.5-.3.8-.6 1.1-1 .2-.5.4-1 .4-1.6 0-.6-.1-1.2-.4-1.6-.2-.5-.6-.8-1.1-1-.5-.3-1-.4-1.7-.4h-1.4v6z"/><path class="st3" d="M96 137.1h105.6v38.4H96v-38.4z"/><path class="st2" d="M130.8 153.9v-8.6h1.6l3.9 6.3h.1l-.1-1.7v-4.7h1.3v8.6h-1.4l-4.1-6.6h-.1l.1 1.7v5h-1.3zm8.7 0v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V149h4.1v1.2h-4.1zm6.2 3.7v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V149h4.1v1.2h-4.1zm6.1 3.7v-8.6h2.8c.9 0 1.7.2 2.3.5.7.4 1.2.9 1.5 1.5.4.6.5 1.4.5 2.2s-.2 1.6-.5 2.2c-.4.6-.9 1.2-1.5 1.5-.6.4-1.4.5-2.3.5h-2.8zm1.3-1.3h1.4c.6 0 1.2-.1 1.7-.4.5-.2.8-.6 1.1-1 .2-.5.4-1 .4-1.6 0-.6-.1-1.2-.4-1.6-.2-.5-.6-.8-1.1-1-.5-.2-1-.4-1.7-.4h-1.4v6zm9.9 1.4c-.5 0-.9-.1-1.3-.3-.4-.2-.8-.4-1.1-.8-.3-.4-.5-.8-.7-1.3l1.2-.5c.1.5.3.9.7 1.2.3.3.7.5 1.2.5.3 0 .5 0 .8-.1.2-.1.4-.2.6-.4s.2-.4.2-.7c0-.3-.1-.5-.2-.7-.1-.2-.3-.3-.6-.5s-.6-.3-1-.5l-.5-.2c-.2-.1-.5-.2-.7-.3-.2-.1-.5-.3-.7-.5-.2-.2-.4-.4-.5-.7-.1-.3-.2-.6-.2-.9 0-.4.1-.8.3-1.2.2-.4.5-.6.9-.8.4-.2.9-.3 1.4-.3.6 0 1 .1 1.4.3.4.2.7.4.9.7.2.3.4.5.4.8l-1.2.5c0-.2-.1-.3-.2-.5s-.3-.3-.5-.4c-.2-.1-.4-.2-.8-.2-.2 0-.5.1-.7.2-.2.1-.4.2-.5.4-.1.2-.2.3-.2.6s.1.6.4.8c.3.2.6.4 1.1.5l.6.2c.3.1.6.2.8.3.3.1.5.3.7.5.2.2.4.4.5.7.1.3.2.6.2 1s-.1.8-.3 1.2c-.2.3-.4.6-.7.8-.3.2-.6.3-.9.4s-.5.2-.8.2zM107 167.9v-8.6h1.3v8.6H107zm3.2 0v-8.6h1.8l2.5 6.5h.1l2.5-6.5h1.8v8.6h-1.3V163l.1-1.5h-.1l-2.5 6.4h-1l-2.5-6.4h-.1l.1 1.5v4.9h-1.4zm10.6 0v-8.6h3c.5 0 1 .1 1.4.3.4.2.8.5 1 .9.3.4.4.9.4 1.4 0 .5-.1 1-.4 1.4-.2.4-.6.7-1 .9-.4.2-.9.3-1.4.3h-2.2v-1.2h2.3c.3 0 .6-.1.8-.2.2-.1.4-.3.5-.5.1-.2.2-.4.2-.7 0-.2-.1-.4-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.3h-1.4zm7.2 0v-8.6h3c.5 0 1 .1 1.4.3.4.2.7.5 1 .9.2.4.4.8.4 1.4 0 .3-.1.7-.2 1s-.3.6-.6.8c-.3.2-.6.4-1 .6-.4.1-.8.2-1.2.2h-2.1v-1.2h2.3c.3 0 .5-.1.7-.2.2-.1.4-.3.5-.5.1-.2.2-.5.2-.7 0-.2-.1-.5-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.4H128zm2-4h1.5l2.7 3.9v.1h-1.5l-2.7-4zm9.3 4.1c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8s.1-1.2.3-1.8c.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8s-.1 1.2-.3 1.8c-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.6.2-1.2.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm7.9 1.1-3-8.6h1.4l1.9 5.7.3 1h.1l.3-1 2-5.7h1.4l-3.1 8.6h-1.3zm5.6 0v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V163h4.1v1.2h-4.1zm6.2 3.7v-8.6h1.8l2.5 6.5h.1l2.5-6.5h1.8v8.6H167V163l.1-1.5h-.1l-2.5 6.4h-1l-2.5-6.4h-.1l.1 1.5v4.9h-1.3zm10.6 0v-8.6h5.2v1.2h-3.9v6.1h3.9v1.2h-5.2zm.7-3.7V163h4.1v1.2H171zm6.1 3.7v-8.6h1.6l3.9 6.3h.1l-.1-1.7v-4.7h1.3v8.6h-1.4l-4.1-6.6h-.1l.1 1.7v5h-1.3zm10.5 0V160h1.3v7.9h-1.3zm-2.4-7.4v-1.2h6.1v1.2h-6.1z"/><path class="st4" d="M200.6 137.1h96v38.4h-96v-38.4z"/><path class="st2" d="M232.5 160.9v-8.6h3c.5 0 1 .1 1.4.3.4.2.8.5 1 .9.3.4.4.9.4 1.4 0 .5-.1 1-.4 1.4-.2.4-.6.7-1 .9-.4.2-.9.3-1.4.3h-2.2v-1.2h2.3c.3 0 .6-.1.8-.2.2-.1.4-.3.5-.5.1-.2.2-.4.2-.7 0-.2-.1-.4-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.3h-1.4zm11 .1c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8s.1-1.2.3-1.8c.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8s-.1 1.2-.3 1.8c-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.5.2-1.1.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.6.3 1.1.4 1.6.4zm9.9 1.2c-.6 0-1.2-.1-1.8-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-.9-.9-1.4-.2-.5-.3-1.1-.3-1.8s.1-1.2.3-1.8c.2-.5.5-1 .9-1.4.4-.4.9-.7 1.4-1 .5-.2 1.1-.3 1.8-.3.6 0 1.2.1 1.8.3.5.2 1 .6 1.4 1 .4.4.7.9.9 1.4.2.5.3 1.1.3 1.8s-.1 1.2-.3 1.8c-.2.5-.5 1-.9 1.4-.4.4-.9.7-1.4 1-.6.2-1.1.3-1.8.3zm0-1.2c.6 0 1.1-.1 1.6-.4.5-.3.8-.7 1.1-1.1.3-.5.4-1.1.4-1.7 0-.6-.1-1.2-.4-1.7-.3-.5-.7-.9-1.1-1.1-.5-.3-1-.4-1.6-.4-.6 0-1.1.1-1.6.4-.5.3-.8.7-1.1 1.1-.3.5-.4 1-.4 1.7 0 .6.1 1.2.4 1.7.3.5.7.9 1.1 1.1.5.3 1 .4 1.6.4zm6 1.1v-8.6h3c.5 0 1 .1 1.4.3.4.2.7.5 1 .9.2.4.4.8.4 1.4 0 .3-.1.7-.2 1s-.3.6-.6.8c-.3.2-.6.4-1 .6-.4.1-.8.2-1.2.2h-2.1v-1.2h2.3c.3 0 .5-.1.7-.2.2-.1.4-.3.5-.5.1-.2.2-.5.2-.7 0-.2-.1-.5-.2-.7-.1-.2-.3-.4-.5-.5-.2-.1-.5-.2-.8-.2h-1.7v7.4h-1.2zm2-4h1.5l2.7 3.9v.1H264l-2.6-4zM178.1 219.9c-.5 0-1-.1-1.5-.3-.5-.2-1-.5-1.4-1-.4-.4-.7-1-.8-1.7l1.5-.6c.1.6.4 1 .8 1.4.4.4.9.5 1.4.5.6 0 1.1-.2 1.5-.6.4-.4.6-.9.6-1.5s-.2-1.1-.6-1.5c-.4-.4-.9-.6-1.5-.6-.4 0-.7.1-1 .2-.3.1-.5.4-.7.6l-1.7-.8.7-5.5h5.9v1.6h-4.5l-.4 2.9h.1c.2-.2.5-.3.8-.5.3-.1.7-.2 1.2-.2.6 0 1.2.2 1.7.5s1 .7 1.3 1.3c.3.5.5 1.2.5 1.9s-.2 1.3-.5 1.9c-.3.6-.8 1-1.3 1.3-.7.5-1.4.7-2.1.7zm9.9 0c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.2-1.2.4-1.9.4zm.1-1.7c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.4.3.9.5 1.5.5zm10.5 1.7c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.2-1.2.4-1.9.4zm0-1.7c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.4.3.9.5 1.5.5zm10 1.4v-8.2h1.6v1.1h.1c.2-.3.4-.5.6-.7.3-.2.6-.4.9-.5.3-.1.7-.2 1-.2.6 0 1.1.1 1.5.4.4.3.7.7.9 1.1.3-.4.6-.8 1.1-1.1.5-.3 1-.5 1.7-.5 1 0 1.7.3 2.1.9.5.6.7 1.4.7 2.3v5.2h-1.7v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4h-1.7v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4h-1.8zm17.2.3c-.6 0-1.2-.1-1.7-.3-.5-.2-.9-.5-1.2-.8-.3-.3-.5-.7-.7-1.1l1.5-.7c.2.4.5.8.8 1 .4.2.8.3 1.2.3.4 0 .8-.1 1.1-.2.3-.2.5-.4.5-.8 0-.2-.1-.4-.2-.6-.1-.1-.3-.3-.6-.4-.2-.1-.5-.2-.8-.2l-1-.2c-.4-.1-.8-.3-1.1-.5-.3-.2-.6-.5-.8-.8-.2-.3-.3-.7-.3-1.1 0-.5.1-.9.4-1.3.3-.4.7-.6 1.1-.8.5-.2 1-.3 1.6-.3.5 0 1 .1 1.4.2.4.1.8.3 1.1.6.3.3.6.6.8 1l-1.5.7c-.2-.4-.4-.6-.7-.8-.3-.1-.6-.2-1-.2s-.7.1-1 .2c-.3.2-.4.4-.4.6 0 .3.1.5.4.7.2.2.5.3.9.4l1.2.3c.8.2 1.4.5 1.8.9.4.4.6.9.6 1.5 0 .5-.2 1-.5 1.4-.3.4-.7.7-1.2.9-.6.2-1.1.4-1.7.4zM69 220v-1.6s.2-.1.4-.4c.2-.2.5-.5.8-.9l1.1-1.1 1-1c.3-.3.5-.6.7-.8.3-.3.5-.6.7-.8.2-.2.3-.5.4-.7.1-.2.1-.5.1-.8 0-.3-.1-.5-.2-.8-.1-.3-.3-.4-.6-.6-.3-.1-.6-.2-1-.2s-.7.1-1 .2c-.3.1-.5.3-.6.6-.1.2-.3.4-.3.7l-1.5-.6c.1-.3.2-.5.4-.8.2-.3.4-.5.7-.8.3-.3.6-.5 1-.6.4-.2.9-.3 1.4-.2.7 0 1.3.2 1.9.5.5.3.9.7 1.2 1.2.3.5.4 1 .4 1.6 0 .5-.1.9-.2 1.3-.2.4-.4.8-.7 1.2-.3.4-.5.7-.8 1l-.5.5c-.2.2-.4.5-.7.7l-.7.7-.6.6-.4.4h4.8v1.6H69zm13.2.2c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.3-1.2.4-1.9.4zm0-1.6c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.4.3.9.5 1.5.5zm10.5 1.6c-.7 0-1.3-.1-1.9-.4-.5-.3-1-.7-1.4-1.2-.4-.5-.7-1.1-.9-1.8-.2-.7-.3-1.4-.3-2.2 0-.8.1-1.6.3-2.2.2-.7.5-1.3.9-1.8s.9-.9 1.4-1.2c.6-.3 1.2-.5 1.9-.5s1.3.2 1.9.5c.6.3 1 .7 1.4 1.2.4.5.7 1.1.9 1.8.2.7.3 1.4.3 2.2 0 .8-.1 1.6-.3 2.2-.2.7-.5 1.3-.9 1.8s-.9.9-1.4 1.2c-.6.3-1.2.4-1.9.4zm0-1.6c.6 0 1-.2 1.5-.5.4-.4.7-.9 1-1.5.2-.6.3-1.3.3-2.1s-.1-1.5-.3-2.1c-.2-.6-.5-1.1-1-1.5-.4-.4-.9-.5-1.5-.5s-1.1.2-1.5.5c-.4.4-.7.8-.9 1.5-.2.6-.3 1.3-.3 2.1s.1 1.5.3 2.1c.2.6.5 1.1.9 1.5.5.3 1 .5 1.5.5zm10 1.4v-8.2h1.6v1.1h.1c.2-.3.4-.5.6-.7.3-.2.6-.4.9-.5.3-.1.7-.2 1-.2.6 0 1.1.1 1.5.4.4.3.7.7.9 1.1.3-.4.6-.8 1.1-1.1.5-.3 1-.5 1.7-.5 1 0 1.7.3 2.1.9.5.6.7 1.4.7 2.3v5.2h-1.7v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4H108v-4.9c0-.7-.1-1.1-.4-1.4-.3-.3-.7-.5-1.2-.5-.4 0-.7.1-1 .3-.3.2-.5.5-.7.9-.2.4-.2.8-.2 1.2v4.4h-1.8zm17.2.2c-.6 0-1.2-.1-1.7-.3-.5-.2-.9-.5-1.2-.8-.3-.3-.5-.7-.7-1.1l1.5-.7c.2.4.5.8.8 1 .4.2.8.3 1.2.3.4 0 .8-.1 1.1-.2.3-.2.5-.4.5-.8 0-.2-.1-.4-.2-.6-.1-.1-.3-.3-.6-.4-.2-.1-.5-.2-.8-.2l-1-.2c-.4-.1-.8-.3-1.1-.5-.3-.2-.6-.5-.8-.8-.2-.3-.3-.7-.3-1.1 0-.5.1-.9.4-1.3.3-.4.7-.6 1.1-.8.5-.2 1-.3 1.6-.3.5 0 1 .1 1.4.2.4.1.8.3 1.1.6.3.3.6.6.8 1l-1.5.7c-.2-.4-.4-.6-.7-.8-.3-.1-.6-.2-1-.2s-.7.1-1 .2c-.3.2-.4.4-.4.6 0 .3.1.5.4.7.2.2.5.3.9.4l1.2.3c.8.2 1.4.5 1.8.9.4.4.6.9.6 1.5 0 .5-.2 1-.5 1.4-.3.4-.7.7-1.2.9-.5.3-1.1.4-1.7.4zM42.4 96.3V82h1.7v14.3h-1.7zm4.6 0V86.1h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V90c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7H47zm10-10.2h6v1.5h-6v-1.5zm1.8 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.5-.6-.7-1.3-.7-2.2zm10.3 3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7s1-1.5 1.7-2 1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5H65V90h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-.9.6-1.8.8-2.9.8zm6.7-.3V86.1h1.6v1.6h.1c.1-.4.4-.7.7-1 .3-.3.7-.5 1.1-.7s.8-.2 1.2-.2h.7c.2 0 .3.1.5.2v1.8c-.2-.1-.5-.2-.7-.2-.2-.1-.5-.1-.7-.1-.5 0-1 .1-1.4.4-.4.3-.7.7-1 1.1-.2.5-.4 1-.4 1.5v5.7h-1.7zm10.4.3c-.8 0-1.4-.1-2-.4s-1-.7-1.3-1.2c-.3-.5-.5-1.1-.5-1.8 0-.8.2-1.4.6-1.9.4-.5.9-.9 1.5-1.2.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1s.7.2 1 .3c.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7.5-.2 1.1-.2 1.6-.2 1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8s-.7.5-1.1.7c-.5.2-1 .2-1.5.2zm.1-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5s-1-.2-1.5-.2c-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4 1 .4 1.6.4zm11.8 1.5c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-2 .7-2.8.4-.8 1.1-1.5 1.8-1.9.8-.5 1.7-.7 2.7-.7 1.1 0 2.1.3 2.8.8.7.5 1.3 1.2 1.6 2l-1.5.6c-.2-.6-.6-1.1-1.1-1.4-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.7.5s-.9.8-1.2 1.4c-.3.6-.5 1.3-.5 2 0 .8.2 1.4.5 2 .3.6.7 1 1.2 1.4.5.3 1.1.5 1.7.5.7 0 1.3-.2 1.9-.5s.9-.8 1.2-1.4l1.5.6c-.3.8-.9 1.5-1.6 2s-1.9.8-3 .8zm5.5-10.5h6v1.5h-6v-1.5zm1.7 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8s-.7-1.3-.7-2.2zm6.5 2.7V86.1h1.7v10.2h-1.7zm.9-12c-.3 0-.6-.1-.9-.4s-.4-.5-.4-.9c0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.3.2-.6.4-.9.4zm8.1 12.3c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2s.7 1.7.7 2.7c0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.6-1.7.8-2.7.8zm0-1.5c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1-.8-1-1.3-1.3c-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3-.5 1.3-.5 2.1.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm7 1.2V86.1h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V90c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7h-1.7zM143 86.1h6v1.5h-6v-1.5zm1.7 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.6-.7-1.3-.7-2.2zm10.5 3c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2 .4.8.7 1.7.7 2.7 0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.6-1.7.8-2.7.8zm0-1.5c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1c-.3-.6-.8-1-1.3-1.3-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3c-.3.6-.5 1.3-.5 2.1s.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm12.2 1.2V82h2.1l7.2 11.4h.1l-.1-2.8V82h1.7v14.3h-1.8l-7.5-11.9h-.1l.1 2.8v9.2h-1.7zm18.2.3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7.4-.8 1-1.5 1.7-2s1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5h-8.8V90h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-1 .6-1.9.8-3 .8zm5.5-.3 4.1-5.9h.2l2.9-4.3h2l-4 5.6h-.1l-3.1 4.6h-2zm.1-10.2h1.9l3.2 4.5h.1l4 5.7h-2l-3-4.5h-.1l-4.1-5.7zm9.9 0h6v1.5h-6v-1.5zm1.8 7.5V83.3h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.6-.7-1.3-.7-2.2zm11.2 2.7V82h4.8c.8 0 1.5.2 2.2.5.7.4 1.2.8 1.6 1.5.4.6.6 1.4.6 2.2 0 .8-.2 1.6-.6 2.2-.4.6-.9 1.1-1.6 1.5-.7.4-1.4.5-2.2.5H215v-1.6h4c.6 0 1-.1 1.4-.4.4-.3.7-.6.9-1 .2-.4.3-.8.3-1.2s-.1-.8-.3-1.2c-.2-.4-.5-.7-.9-1-.4-.3-.9-.4-1.4-.4h-3.2v12.7h-1.7zm14.1.3c-.8 0-1.4-.1-2-.4s-1-.7-1.3-1.2-.5-1.1-.5-1.8c0-.8.2-1.4.6-1.9s.9-.9 1.5-1.2c.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1s.7.2 1 .3c.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7s1.1-.2 1.6-.2c1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8s-.7.5-1.1.7c-.6.2-1 .2-1.5.2zm.1-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5s-1-.2-1.5-.2c-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4 1 .4 1.6.4zm7.4 1.2V86.1h1.7v10.2h-1.7zm.8-12c-.3 0-.6-.1-.9-.4s-.4-.5-.4-.9c0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.2.2-.5.4-.9.4zm3.5 12V86.1h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V90c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7H240zm10.1-10.2h6v1.5h-6v-1.5zm1.7 7.5V83.3h1.7v10c0 .5.1.9.3 1.2s.6.4 1.1.4c.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8s-.7-1.3-.7-2.2zM96 172.8c-2.4 0-4.3 1.9-4.3 4.3 0 2 1.4 3.7 3.3 4.2v20.1h2v-20.1c1.9-.5 3.3-2.1 3.3-4.2 0-2.3-1.9-4.3-4.3-4.3zM201.6 172.6c-2.4 0-4.3 1.9-4.3 4.3 0 2 1.4 3.7 3.3 4.2v19.8h2v-19.8c1.9-.5 3.3-2.1 3.3-4.2 0-2.4-1.9-4.3-4.3-4.3z"/></svg>
  <svg title="A diagram of the INP thresholds. An INP at or below 200 milliseconds is considered good. Between 200 and 500 milliseconds suggests a page's responsiveness needs improvement. Anything over 500 milliseconds means that a page's responsiveness is poor." class="inp-desktop" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 658.4 113.6" style="enable-background:new 0 0 658.4 113.6" xml:space="preserve"><style>.st0{fill: #2979FF} v.st1{fill-rule: evenodd;clip-rule: evenodd;fill: #0CCE6B} .st2 {fill: #191919} .st3{fill-rule: evenodd;clip-rule: evenodd;fill: #FFA400} .st4{fill-rule: evenodd;clip-rule: evenodd;fill: #FF4E42} @media screen and (prefers-color-scheme: light){.st2{fill: #191919}} @media screen and (prefers-color-scheme: dark){.st2{fill: #fff}} [data-user-theme=dark] .st2{fill: #fff}</style><path class="st0" d="M30.2 68.7V0h13v68.7h-13zm28.7 0V0H74l27.7 46.1h.8l-.8-13.2V0h12.9v68.7H101L71.7 20.1h-.8l.8 13.2v35.4H58.9zm71.3 0V0h24.2c4.4 0 8.4.9 11.9 2.7 3.5 1.8 6.3 4.4 8.4 7.6 2.1 3.3 3.1 7 3.1 11.3 0 4.3-1 8.1-3.1 11.4-2.1 3.3-4.9 5.8-8.4 7.7-3.5 1.8-7.5 2.7-11.9 2.7h-17V31.2h17.4c2.2 0 4.1-.4 5.7-1.3 1.5-.9 2.7-2.1 3.5-3.5.8-1.4 1.2-3 1.2-4.7 0-1.7-.4-3.2-1.2-4.6-.8-1.4-1.9-2.6-3.5-3.5-1.5-.9-3.4-1.3-5.7-1.3h-11.6v56.5h-13z"/><path class="st1" d="M303.2 14.9h115.2v43.2H303.2V14.9z"/><path class="st2" d="M345.3 41.5c-.7 0-1.3-.1-1.9-.4-.6-.2-1.1-.6-1.6-1s-.8-1-1.1-1.6c-.3-.6-.4-1.3-.4-2s.1-1.4.4-2c.3-.6.6-1.1 1.1-1.6.5-.5 1-.8 1.6-1 .6-.2 1.2-.4 1.9-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1l-1 1c-.2-.2-.4-.4-.7-.6-.3-.2-.5-.3-.9-.4-.3-.1-.6-.1-1-.1-.5 0-.9.1-1.3.3-.4.2-.8.4-1.1.7-.3.3-.6.7-.8 1.1-.2.4-.3.9-.3 1.5s.1 1 .3 1.5c.2.4.5.8.8 1.1.3.3.7.6 1.1.7.4.2.9.2 1.3.2s.8-.1 1.2-.2c.4-.1.7-.3 1-.5.3-.2.5-.5.7-.8.2-.3.3-.7.3-1.1h-3.3v-1.3h4.6v.8c0 .7-.1 1.3-.4 1.9-.2.6-.6 1-1 1.5-.4.4-.9.7-1.5.9-.3.2-.9.3-1.6.3zm10.5 0c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1-1.2.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3s.5-1.2.5-1.9-.2-1.3-.5-1.9-.7-1-1.2-1.3c-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.8.5-.5.3-.9.7-1.2 1.3s-.5 1.2-.5 1.9.2 1.3.5 1.9.7 1 1.2 1.3c.6.4 1.2.5 1.8.5zm11 1.4c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1-1.3.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3s.5-1.2.5-1.9-.2-1.3-.5-1.9-.7-1-1.2-1.3c-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.8.5-.5.3-.9.7-1.2 1.3s-.5 1.2-.5 1.9.2 1.3.5 1.9.7 1 1.2 1.3c.6.4 1.2.5 1.8.5zm6.6 1.2v-9.5h3c1 0 1.9.2 2.6.6.7.4 1.3 1 1.7 1.7s.6 1.5.6 2.5c0 .9-.2 1.8-.6 2.5s-1 1.3-1.7 1.7c-.7.4-1.6.6-2.6.6h-3zm1.5-1.4h1.5c.7 0 1.3-.1 1.8-.4.5-.3.9-.7 1.2-1.2.3-.5.4-1.1.4-1.8s-.1-1.3-.4-1.8c-.3-.5-.7-.9-1.2-1.2-.5-.3-1.1-.4-1.8-.4h-1.5v6.8z"/><path class="st3" d="M418.4 14.9h124.8v43.2H418.4V14.9z"/><path class="st2" d="M460.8 33.3v-9.5h1.7l4.3 7h.1l-.1-1.8v-5.2h1.5v9.5h-1.5l-4.5-7.4h-.1l.1 1.8v5.5h-1.5zm9.7 0v-9.5h5.8v1.4H472V32h4.3v1.4h-5.8zm.7-4.1v-1.4h4.6v1.4h-4.6zm6.9 4.1v-9.5h5.8v1.4h-4.3V32h4.3v1.4h-5.8zm.8-4.1v-1.4h4.6v1.4h-4.6zm6.8 4.1v-9.5h3c1 0 1.9.2 2.6.6.7.4 1.3 1 1.7 1.7s.6 1.5.6 2.5c0 .9-.2 1.8-.6 2.5s-1 1.3-1.7 1.7c-.7.4-1.6.6-2.6.6h-3zm1.5-1.4h1.5c.7 0 1.3-.1 1.8-.4.5-.3.9-.7 1.2-1.2.3-.5.4-1.1.4-1.8s-.1-1.3-.4-1.8c-.3-.5-.7-.9-1.2-1.2-.5-.3-1.1-.4-1.8-.4h-1.5v6.8zm10.9 1.6c-.5 0-1-.1-1.5-.3-.5-.2-.9-.5-1.2-.9-.3-.4-.6-.9-.8-1.5l1.4-.6c.1.5.4.9.8 1.3.4.3.8.5 1.3.5.3 0 .6-.1.8-.2.3-.1.5-.3.6-.5.2-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.6-.5-.3-.2-.7-.3-1.1-.5l-.6-.2c-.3-.1-.5-.2-.8-.4-.3-.1-.5-.3-.7-.5-.2-.2-.4-.5-.5-.7-.1-.3-.2-.6-.2-1 0-.5.1-.9.4-1.3.2-.4.6-.7 1-.9.4-.2 1-.4 1.6-.4.6 0 1.1.1 1.5.3.4.2.7.5 1 .8.2.3.4.6.5.9l-1.3.6c-.1-.2-.2-.4-.3-.5-.1-.2-.3-.3-.5-.4-.2-.1-.5-.2-.8-.2-.3 0-.5.1-.8.2-.2.1-.4.2-.6.4-.1.2-.2.4-.2.6 0 .3.1.6.4.8s.7.4 1.2.6l.6.2c.3.1.6.2.9.4.3.1.6.3.8.6.2.2.4.5.6.8.1.3.2.7.2 1.2s-.1.9-.3 1.3c-.2.4-.4.6-.8.9-.3.2-.7.4-1 .5-.3 0-.7.1-1 .1zM434.4 49.3v-9.5h1.5v9.5h-1.5zm3.6 0v-9.5h2l2.8 7.3h.1l2.8-7.3h2v9.5h-1.4v-5.4l.1-1.7h-.1l-2.8 7.1h-1.2l-2.8-7.1h-.1l.1 1.7v5.4H438zm11.7 0v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.9.6 1.1 1 .3.4.4.9.4 1.5s-.1 1.1-.4 1.5c-.3.4-.7.8-1.1 1-.5.2-1 .4-1.6.4h-2.5v-1.4h2.5c.4 0 .7-.1.9-.2.2-.1.4-.3.5-.6.1-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.2-.5-.2-.9-.2h-1.8v8.2h-1.5zm8 0v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.8.6 1.1 1s.4.9.4 1.5c0 .4-.1.8-.2 1.1s-.4.7-.7.9c-.3.3-.6.5-1 .6-.4.1-.9.2-1.4.2h-2.3v-1.3h2.5c.3 0 .6-.1.8-.2.2-.1.4-.3.6-.6.2-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.1-.5-.2-.9-.2h-1.9v8.2h-1.4zm2.2-4.4h1.7l3 4.3v.1h-1.7l-3-4.4zm10.3 4.6c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1c-.6.3-1.2.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3.3-.5.5-1.2.5-1.9s-.2-1.3-.5-1.9c-.3-.5-.7-1-1.2-1.3-.5-.3-1.1-.5-1.8-.5s-1.2.2-1.8.5c-.5.3-.9.7-1.2 1.3-.3.5-.5 1.2-.5 1.9s.2 1.3.5 1.9c.3.5.7 1 1.2 1.3.6.4 1.2.5 1.8.5zm8.8 1.2-3.4-9.5h1.6l2.2 6.3.3 1.1h.1l.4-1.1 2.2-6.3h1.6l-3.5 9.5H479zm6.2 0v-9.5h5.8v1.4h-4.3V48h4.3v1.4h-5.8zm.8-4.1v-1.4h4.6v1.4H486zm6.8 4.1v-9.5h2l2.8 7.3h.1l2.8-7.3h2v9.5H501v-5.4l.1-1.7h-.1l-2.8 7.1H497l-2.8-7.1h-.1l.1 1.7v5.4h-1.4zm11.8 0v-9.5h5.8v1.4h-4.3V48h4.3v1.4h-5.8zm.8-4.1v-1.4h4.6v1.4h-4.6zm6.8 4.1v-9.5h1.7l4.3 7h.1l-.1-1.8v-5.2h1.5v9.5h-1.5l-4.5-7.4h-.1l.1 1.8v5.5h-1.5zm11.7 0v-8.8h1.5v8.8h-1.5zm-2.7-8.2v-1.4h6.8v1.4h-6.8z"/><path class="st4" d="M543.2 14.9h115.2v43.2H543.2V14.9z"/><path class="st2" d="M582.9 41.3v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.9.6 1.1 1 .3.4.4.9.4 1.5s-.1 1.1-.4 1.5c-.3.4-.7.8-1.1 1-.5.2-1 .4-1.6.4h-2.5v-1.4h2.5c.4 0 .7-.1.9-.2.2-.1.4-.3.5-.6.1-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.2-.5-.2-.9-.2h-1.8v8.2h-1.5zm12.3.2c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1c-.7.3-1.3.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3.3-.5.5-1.2.5-1.9s-.2-1.3-.5-1.9c-.3-.5-.7-1-1.2-1.3-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.8.5-.5.3-.9.7-1.2 1.3-.3.5-.5 1.2-.5 1.9s.2 1.3.5 1.9c.3.5.7 1 1.2 1.3.6.4 1.1.5 1.8.5zm10.9 1.4c-.7 0-1.4-.1-2-.4-.6-.3-1.1-.6-1.6-1.1-.4-.5-.8-1-1-1.6-.2-.6-.4-1.2-.4-1.9s.1-1.3.4-1.9c.2-.6.6-1.1 1-1.6s1-.8 1.6-1.1c.6-.3 1.3-.4 2-.4s1.4.1 2 .4c.6.2 1.1.6 1.6 1.1.5.5.8 1 1 1.6.2.6.4 1.2.4 1.9s-.1 1.3-.4 1.9c-.2.6-.6 1.1-1 1.6s-1 .8-1.6 1.1c-.6.3-1.3.4-2 .4zm0-1.4c.6 0 1.2-.2 1.8-.5.5-.3.9-.7 1.2-1.3.3-.5.5-1.2.5-1.9s-.2-1.3-.5-1.9c-.3-.5-.7-1-1.2-1.3-.5-.3-1.1-.5-1.8-.5s-1.2.2-1.8.5c-.5.3-.9.7-1.2 1.3-.3.5-.5 1.2-.5 1.9s.2 1.3.5 1.9c.3.5.7 1 1.2 1.3.6.4 1.2.5 1.8.5zm6.7 1.2v-9.5h3.3c.6 0 1.1.1 1.6.4.5.2.8.6 1.1 1s.4.9.4 1.5c0 .4-.1.8-.2 1.1s-.4.7-.7.9c-.3.3-.6.5-1 .6-.4.1-.9.2-1.4.2h-2.3v-1.3h2.5c.3 0 .6-.1.8-.2.2-.1.4-.3.6-.6.2-.2.2-.5.2-.8 0-.3-.1-.5-.2-.7-.1-.2-.3-.4-.5-.6-.2-.1-.5-.2-.9-.2h-1.9v8.2h-1.4zm2.2-4.4h1.7l3 4.3v.1H618l-3-4.4zM517.7 102.9c-.6 0-1.1-.1-1.7-.3-.6-.2-1-.6-1.5-1-.4-.5-.7-1.1-.9-1.8l1.7-.7c.2.6.4 1.1.8 1.5.4.4.9.6 1.6.6.7 0 1.2-.2 1.6-.6.4-.4.7-1 .7-1.6 0-.7-.2-1.2-.6-1.6-.4-.4-1-.7-1.6-.7-.4 0-.8.1-1.1.2-.3.2-.6.4-.8.7l-1.8-.8.7-6h6.4v1.7h-4.9l-.4 3.2h.1c.3-.2.6-.4.9-.5.4-.1.8-.2 1.3-.2.7 0 1.3.2 1.9.5.6.3 1 .8 1.4 1.4.4.6.5 1.3.5 2 0 .8-.2 1.5-.5 2.1-.4.6-.8 1.1-1.5 1.4-.7.3-1.5.5-2.3.5zm10.8 0c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.6.3-1.2.5-2 .5zm0-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.5.4 1 .6 1.6.6zm11.4 1.8c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.5.3-1.2.5-2 .5zm.1-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.4.4.9.6 1.6.6zm10.8 1.5v-8.8h1.8V95h.1c.2-.3.4-.5.7-.8s.6-.4.9-.5c.4-.1.7-.2 1.1-.2.7 0 1.2.2 1.7.5s.8.7 1 1.2c.3-.5.7-.8 1.2-1.2.5-.3 1.1-.5 1.8-.5 1 0 1.8.3 2.3 1 .5.6.8 1.5.8 2.5v5.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.7-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.8 1s-.3.9-.3 1.3v4.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.8-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.7 1s-.3.9-.3 1.3v4.7h-1.7zm18.6.3c-.7 0-1.3-.1-1.8-.3-.5-.2-.9-.5-1.3-.9-.3-.4-.6-.8-.7-1.2l1.7-.7c.2.5.5.8.9 1.1.4.2.8.4 1.3.4s.9-.1 1.2-.2c.3-.2.5-.4.5-.8 0-.2-.1-.5-.2-.6s-.4-.3-.6-.4l-.9-.3-1.1-.2c-.4-.1-.8-.3-1.2-.5-.4-.2-.7-.5-.9-.9-.2-.4-.3-.8-.3-1.2 0-.5.2-1 .5-1.4.3-.4.7-.7 1.2-.9.5-.2 1.1-.3 1.7-.3.6 0 1.1.1 1.5.2.5.1.9.3 1.2.6.3.3.6.6.8 1l-1.6.7c-.2-.4-.5-.7-.8-.8-.3-.2-.7-.2-1.1-.2-.4 0-.8.1-1.1.3-.3.2-.4.4-.4.7 0 .3.1.5.4.7.3.2.6.3 1 .4l1.3.3c.9.2 1.5.6 2 1 .4.4.7 1 .7 1.6 0 .6-.2 1.1-.5 1.5-.3.4-.8.7-1.3 1-.8.2-1.5.3-2.1.3zM389.2 102.6v-1.8l.4-.4.9-.9c.4-.4.7-.8 1.1-1.2l1.1-1.1.8-.8c.3-.3.6-.6.8-.9.2-.3.3-.5.4-.8.1-.2.1-.5.1-.8 0-.3-.1-.6-.2-.8-.1-.3-.4-.5-.7-.6-.3-.2-.6-.2-1.1-.2-.4 0-.7.1-1 .2-.3.2-.5.4-.7.6-.2.2-.3.5-.3.7l-1.7-.7c.1-.3.2-.6.4-.9.2-.3.4-.6.8-.9.3-.3.7-.5 1.1-.7.4-.2.9-.3 1.5-.3.8 0 1.4.2 2 .5.6.3 1 .7 1.3 1.2.3.5.5 1.1.5 1.7 0 .5-.1 1-.3 1.5-.2.5-.4.9-.7 1.3-.3.4-.6.7-.9 1-.2.1-.4.3-.6.6-.2.2-.5.5-.7.8l-.8.8-.7.7-.4.4h5.2v1.7h-7.6zm14.2.3c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.5.3-1.2.5-2 .5zm0-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.5.4 1 .6 1.6.6zm11.5 1.8c-.7 0-1.4-.2-2-.5-.6-.3-1.1-.8-1.5-1.3-.4-.6-.8-1.2-1-2-.2-.8-.3-1.6-.3-2.4 0-.9.1-1.7.3-2.5.2-.8.6-1.4 1-2 .4-.6.9-1 1.5-1.3.6-.3 1.3-.5 2-.5.8 0 1.4.2 2 .5.6.3 1.1.8 1.5 1.3.4.6.8 1.2 1 2 .2.8.3 1.6.3 2.5 0 .9-.1 1.7-.3 2.4-.2.8-.5 1.4-1 2-.4.6-.9 1-1.5 1.3-.6.3-1.3.5-2 .5zm0-1.8c.6 0 1.1-.2 1.6-.6.4-.4.8-.9 1-1.6.2-.7.4-1.4.4-2.2 0-.8-.1-1.6-.4-2.3-.2-.7-.6-1.2-1-1.6-.4-.4-1-.6-1.6-.6-.6 0-1.2.2-1.6.6-.4.4-.8.9-1 1.6-.2.7-.4 1.4-.4 2.3 0 .8.1 1.6.4 2.2.2.7.6 1.2 1 1.6.4.4.9.6 1.6.6zm10.8 1.5v-8.8h1.8V95h.1c.2-.3.4-.5.7-.8s.6-.4.9-.5c.4-.1.7-.2 1.1-.2.7 0 1.2.2 1.7.5s.8.7 1 1.2c.3-.5.7-.8 1.2-1.2.5-.3 1.1-.5 1.8-.5 1 0 1.8.3 2.3 1 .5.6.8 1.5.8 2.5v5.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.7-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.8 1s-.3.9-.3 1.3v4.7h-1.9v-5.4c0-.7-.2-1.2-.5-1.5-.3-.3-.8-.5-1.3-.5-.4 0-.8.1-1.1.4-.3.2-.6.6-.7 1s-.3.9-.3 1.3v4.7h-1.7zm18.6.3c-.7 0-1.3-.1-1.8-.3-.5-.2-.9-.5-1.3-.9-.3-.4-.6-.8-.7-1.2l1.7-.7c.2.5.5.8.9 1.1.4.2.8.4 1.3.4s.9-.1 1.2-.2c.3-.2.5-.4.5-.8 0-.2-.1-.5-.2-.6-.2-.2-.4-.3-.6-.4l-.9-.3-1.1-.2c-.4-.1-.8-.3-1.2-.5-.4-.2-.7-.5-.9-.9-.2-.4-.3-.8-.3-1.2 0-.5.2-1 .5-1.4.3-.4.7-.7 1.2-.9.5-.2 1.1-.3 1.7-.3.6 0 1.1.1 1.5.2.5.1.9.3 1.2.6.3.3.6.6.8 1l-1.6.7c-.2-.4-.5-.7-.8-.8-.3-.2-.7-.2-1.1-.2-.4 0-.8.1-1.1.3-.3.2-.4.4-.4.7 0 .3.1.5.4.7.3.2.6.3 1 .4l1.3.3c.9.2 1.5.6 2 1 .4.4.7 1 .7 1.6 0 .6-.2 1.1-.5 1.5-.3.4-.8.7-1.3 1-.8.2-1.4.3-2.1.3zM0 113.3V99h1.7v14.3H0zm4.6 0v-10.2h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V107c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7H4.6zm10-10.2h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.5-.5-.7-1.2-.7-2.1zm10.3 3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7.4-.8 1-1.5 1.7-2s1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5h-8.8V107h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-1 .5-1.9.8-3 .8zm6.7-.3v-10.2H35v1.6h.1c.1-.4.4-.7.7-1 .3-.3.7-.5 1.1-.7.4-.2.8-.2 1.2-.2h.7c.2 0 .3.1.5.2v1.8c-.2-.1-.5-.2-.7-.2-.2-.1-.5-.1-.7-.1-.5 0-1 .1-1.4.4-.4.3-.7.7-1 1.1-.2.5-.4 1-.4 1.5v5.7h-1.7zm10.4.3c-.8 0-1.4-.1-2-.4-.6-.3-1-.7-1.3-1.2-.3-.5-.5-1.1-.5-1.8 0-.8.2-1.4.6-1.9.4-.5.9-.9 1.5-1.2.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1.4.1.7.2 1 .3.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7.5-.2 1.1-.2 1.6-.2 1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8-.3.3-.7.5-1.1.7-.5.1-1 .2-1.5.2zm.1-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5-.5-.1-1-.2-1.5-.2-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4c.4.2 1 .4 1.6.4zm11.7 1.5c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-2 .7-2.8.4-.8 1.1-1.5 1.8-1.9.8-.5 1.7-.7 2.7-.7 1.1 0 2.1.3 2.8.8.7.5 1.3 1.2 1.6 2l-1.5.6c-.2-.6-.6-1.1-1.1-1.4-.5-.3-1.1-.5-1.8-.5-.6 0-1.2.2-1.7.5s-.9.8-1.2 1.4c-.3.6-.5 1.3-.5 2 0 .8.2 1.4.5 2 .3.6.7 1 1.2 1.4.5.3 1.1.5 1.7.5.7 0 1.3-.2 1.9-.5.5-.3.9-.8 1.2-1.4l1.5.6c-.3.8-.9 1.5-1.6 2-.9.5-1.8.8-3 .8zm5.5-10.5h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zm6.5 2.7v-10.2h1.7v10.2h-1.7zm.9-12.1c-.3 0-.6-.1-.9-.4-.2-.2-.4-.5-.4-.9 0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.3.3-.6.4-.9.4zm8.1 12.4c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2 .4.8.7 1.7.7 2.7 0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.5-1.7.8-2.7.8zm0-1.6c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1c-.3-.6-.8-1-1.3-1.3-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3c-.3.6-.5 1.3-.5 2.1s.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm7 1.3v-10.2H87v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V107c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7h-1.7zm15.1-10.2h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zm10.5 3c-1 0-1.9-.2-2.7-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.7-1.7-.7-2.8 0-1 .2-1.9.7-2.8.4-.8 1.1-1.5 1.8-2 .8-.5 1.7-.7 2.7-.7 1 0 1.9.2 2.7.7.8.5 1.4 1.1 1.9 2 .4.8.7 1.7.7 2.7 0 1-.2 1.9-.7 2.8-.4.8-1.1 1.5-1.9 1.9-.8.5-1.7.8-2.7.8zm0-1.6c.6 0 1.2-.2 1.7-.5s1-.8 1.3-1.3c.3-.6.5-1.3.5-2.1s-.2-1.5-.5-2.1c-.3-.6-.8-1-1.3-1.3-.5-.3-1.1-.5-1.7-.5-.6 0-1.2.2-1.7.5s-1 .7-1.3 1.3c-.3.6-.5 1.3-.5 2.1s.2 1.5.5 2.1c.3.6.8 1 1.3 1.3.5.4 1.1.5 1.7.5zm12.2 1.3V99h2.1l7.2 11.4h.1l-.1-2.8V99h1.7v14.3h-1.8l-7.5-11.9h-.1l.1 2.8v9.2H125zm18.2.3c-1 0-1.9-.2-2.6-.7-.8-.5-1.4-1.1-1.8-1.9-.4-.8-.6-1.7-.6-2.8 0-1 .2-1.9.6-2.7.4-.8 1-1.5 1.7-2s1.6-.8 2.6-.8 1.9.2 2.6.7c.7.4 1.3 1.1 1.7 1.8.4.8.6 1.7.6 2.7v.5h-8.8V107h7c0-.3-.1-.6-.2-.9-.1-.3-.3-.6-.5-.9-.2-.3-.6-.5-.9-.7-.4-.2-.8-.3-1.4-.3-.7 0-1.2.2-1.7.5s-.8.8-1.1 1.4c-.3.6-.4 1.2-.4 2 0 .9.2 1.6.5 2.2.3.6.8 1 1.3 1.3.5.3 1.1.4 1.7.4.8 0 1.4-.2 1.8-.5.5-.4.9-.8 1.2-1.3l1.4.7c-.4.8-1 1.4-1.7 1.9-1 .5-1.9.8-3 .8zm5.5-.3 4.1-5.9h.2l2.9-4.3h2l-4 5.6h-.1l-3.1 4.6h-2zm.1-10.2h1.9l3.2 4.5h.1l4 5.7h-2l-3-4.5h-.1l-4.1-5.7zm9.9 0h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zm11.2 2.7V99h4.8c.8 0 1.5.2 2.2.5.7.4 1.2.8 1.6 1.5.4.6.6 1.4.6 2.2 0 .8-.2 1.6-.6 2.2-.4.6-.9 1.1-1.6 1.5-.7.4-1.4.5-2.2.5h-3.9v-1.6h4c.6 0 1-.1 1.4-.4.4-.3.7-.6.9-1 .2-.4.3-.8.3-1.2 0-.4-.1-.8-.3-1.2-.2-.4-.5-.7-.9-1-.4-.3-.9-.4-1.4-.4h-3.2v12.7h-1.7zm14 .3c-.8 0-1.4-.1-2-.4-.6-.3-1-.7-1.3-1.2-.3-.5-.5-1.1-.5-1.8 0-.8.2-1.4.6-1.9.4-.5.9-.9 1.5-1.2.7-.3 1.4-.4 2.2-.4.4 0 .9 0 1.2.1.4.1.7.2 1 .3.3.1.5.2.7.3v-.6c0-.8-.3-1.4-.8-1.8-.5-.5-1.2-.7-2-.7-.6 0-1.1.1-1.6.4-.5.2-.9.6-1.1 1l-1.3-1c.3-.4.6-.7 1-1 .4-.3.9-.5 1.4-.7.5-.2 1.1-.2 1.6-.2 1.4 0 2.5.4 3.2 1.1.8.7 1.2 1.7 1.2 3v6.5h-1.6v-1.5h-.1c-.2.3-.4.6-.7.8-.3.3-.7.5-1.1.7-.5.1-.9.2-1.5.2zm.2-1.5c.6 0 1.1-.1 1.6-.4.5-.3.9-.7 1.2-1.2.3-.5.5-1 .5-1.6-.3-.2-.7-.4-1.2-.5-.5-.1-1-.2-1.5-.2-1 0-1.7.2-2.1.6-.4.4-.7.9-.7 1.5s.2 1 .6 1.4c.4.2 1 .4 1.6.4zm7.4 1.2v-10.2h1.7v10.2h-1.7zm.8-12.1c-.3 0-.6-.1-.9-.4-.2-.2-.4-.5-.4-.9 0-.3.1-.6.4-.9.2-.2.5-.4.9-.4.3 0 .6.1.9.4.2.2.4.5.4.9 0 .3-.1.6-.4.9-.3.3-.5.4-.9.4zm3.5 12.1v-10.2h1.6v1.5h.1c.3-.5.7-.9 1.3-1.3.6-.4 1.3-.5 2-.5 1.2 0 2.2.4 2.8 1.1.6.7 1 1.7 1 2.9v6.5h-1.7V107c0-1-.2-1.7-.7-2.1-.5-.4-1.1-.6-1.8-.6-.6 0-1.1.2-1.5.5-.4.3-.8.7-1 1.2-.2.5-.4 1-.4 1.6v5.7h-1.7zm10-10.2h6v1.5h-6v-1.5zm1.8 7.5v-10.4h1.7v10c0 .5.1.9.3 1.2.2.3.6.4 1.1.4.2 0 .4 0 .6-.1.2-.1.4-.2.5-.2v1.7c-.2.1-.4.1-.6.2-.2.1-.5.1-.8.1-.9 0-1.5-.2-2.1-.8-.4-.5-.7-1.2-.7-2.1zM418.1 55.2c-1.8 0-3.3 1.5-3.3 3.3 0 1.5 1 2.7 2.3 3.1V83h2V61.6c1.3-.4 2.3-1.7 2.3-3.1 0-1.8-1.5-3.3-3.3-3.3zM546.5 58.6c0-1.8-1.5-3.3-3.3-3.3s-3.3 1.5-3.3 3.3c0 1.5 1 2.7 2.3 3.1v21.2h2V61.7c1.3-.4 2.3-1.6 2.3-3.1z"/></svg>
</figure>

### What's in an interaction?

<figure>
  {% Img src="image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/Ng0j5yaGYZX9Bm3VQ70c.svg", alt="A diagram depicting an interaction on the main thread. The user makes an input while blocking tasks run. The input is delayed until those tasks complete, after which the pointerup, mouseup, and click event handlers run, then rendering and painting work is kicked off until the next frame is presented.", width="736", height="193.81333333333" %}
  <figcaption>
    The life of an interaction. An input delay occurs until event handlers begin running, which may be caused by factors such as long tasks on the main thread. The interaction's event handlers then run, and a delay occurs before the next frame is presented.
  </figcaption>
</figure>

The primary driver of interactivity is often JavaScript, though browsers do provide interactivity through controls _not_ powered by JavaScript, such as checkboxes, radio buttons, and controls powered by CSS.

As far as INP goes, **only the following interaction types are observed:**

- Clicking with a mouse.
- Tapping on a device with a touchscreen.
- Pressing a key on either a physical or onscreen keyboard.

{% Aside 'important' %}
Hovering and scrolling does not factor into INP. However, scrolling with the keyboard (space bar, page up, page down, and so forth) involves a keystroke, which may trigger other events that INP _does_ measure. Any resulting scrolling is _not_ factored into how INP is calculated.
{% endAside %}

Interactions happen in the main document or in iframes embedded in the document&mdash;for example clicking play on an embedded video. End users will not be aware what is in an iframe or not. Therefore, INP within iframes are needed to measure the user experience for the top level page. Note JavaScript Web APIs will not have access to the iframe contents so may not be able to measure INP within an iframe and this will [show as a difference between CrUX and RUM](/crux-and-rum-differences/#iframes).

Interactions may consist of two parts, each with multiple events. For example, a keystroke consists of the `keydown`, `keypress`, and `keyup` events. Tap interactions contain `pointerup` and `pointerdown` events. The event with the longest duration within the interaction is chosen as the interaction's latency.

<figure>
  {% Img src="image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/vNosnKDYgBRnFmEvwm0c.svg", alt="A depiction of more complex interaction containing two interactions. The first is a mousedown event, which produces a frame before the mouse button is let up, which kicks off more work until yet another frame is presented as the result.", width="736", height="164.37333333333" %}
  <figcaption>
    A depiction of an interaction with multiple event handlers. The first part of the interaction receives an input when the user clicks down on a mouse button. However, before they release the mouse button, a frame is presented. When the user releases the mouse button, another series of event handlers must run before the next frame is presented.
  </figcaption>
</figure>

INP is calculated when the user leaves the page, resulting in a single value that is representative of the page's overall responsiveness throughout the entire page's lifecycle. **A low INP means that a page is reliably responsive to user input.**

### How is INP different from First Input Delay (FID)?

Where INP considers _all_ page interactions, [First Input Delay (FID)](/fid/) only accounts for the _first_ interaction. It also only measures the first interaction's _input delay_, not the time it takes to run event handlers, or the delay in presenting the next frame.

Given that FID is also a [load responsiveness metric](/user-centric-performance-metrics/#types-of-metrics), the rationale behind it is that if the first interaction made with a page in the loading phase has little to no perceptible input delay, the page has made a good first impression.

INP is more than about first impressions. By sampling all interactions, responsiveness can be assessed comprehensively, making INP a more reliable indicator of overall responsiveness than FID.

### What if no INP value is reported?

It's possible that a page can return no INP value. This can happen for a number of reasons:

- The page was loaded, but the user never clicked, tapped, or pressed a key on their keyboard.
- The page was loaded, but the user interacted with the page using gestures that did not involve clicking, tapping, or using the keyboard. For example, scrolling or hovering over elements does not factor into how INP is calculated.
- The page is being accessed by a bot such as a search crawler or headless browser that has not been scripted to interact with the page.

## How to measure INP

INP can be measured both in [the field](/lab-and-field-data-differences/#field-data) and in [the lab](/lab-and-field-data-differences/#lab-data) through a variety of tools.

{% Aside 'important' %}
The best way to measure your website's INP is by gathering metrics from actual users in the field. If you're accustomed to relying on lab data for assessing performance, take some time to read [Why lab and field data can be different (and what to do about it)](/lab-and-field-data-differences/).
{% endAside %}

### In the field

Ideally, your journey in optimizing INP will start with field data. At its best, field data from Real User Monitoring (RUM) will give you not only a page's INP value, but also contextual data that highlights what specific interaction was responsible for the INP value itself, whether the interaction occurred during or after page load, the type of interaction (click, keypress, or tap), and other valuable information.

{% Aside 'objective' %}
**Read to learn more:**&nbsp;[Find slow interactions in the field](/find-slow-interactions-in-the-field/).
{% endAside %}

If your website qualifies for inclusion in the [Chrome User Experience Report (CrUX)](https://developer.chrome.com/docs/crux/), you can quickly get field data for INP [via CrUX in PageSpeed Insights](/find-slow-interactions-in-the-field/#get-field-data-quickly-with-crux) (and other Core Web Vitals). At a minimum, you can get an origin-level picture of your website's INP, but in some cases, you can also get page-level data as well.

However, while CrUX is useful to tell you that there _is_ a problem at a high level, it often doesn't provide enough detail to help fully understand what the problem is. A RUM solution can help you drill down into more detail as to the pages, users or user interactions which are experiencing slow interactions. Being able to attribute INP to individual interactions avoids guesswork and wasted effort.

### In the lab

Optimally, you'll want to start testing in the lab once you have field data that suggests you have slow interactions. In the absence of field data, however, there are some strategies for reproducing slow interactions in the lab. Such strategies include following common user flows and testing interactions along the way, as well as interacting with the page during load—when the main thread is often busiest—in order to surface slow interactions during that crucial part of the user experience.

{% Aside 'objective' %}
**Read to learn more:**&nbsp;[Diagnose slow interactions in the lab](/diagnose-slow-interactions-in-the-lab/).
{% endAside %}

## How to improve INP

A [collection of guides on optimizing INP](/optimize-inp/) is available to guide you through the process of identifying slow interactions in the field and using lab data to drill down and optimize them in a variety of ways.

{% include 'content/metrics/metrics-changelog.njk' %}

# Largest Contentful Paint (LCP)

{% BrowserCompat 'api.LargestContentfulPaint' %}

{% Aside %}
  Largest Contentful Paint (LCP) is an important, [stable](/vitals/#stable) Core Web Vital metric for
  measuring [perceived load
  speed](/user-centric-performance-metrics/#types-of-metrics) because it marks
  the point in the page load timeline when the page's main content has likely
  loaded&mdash;a fast LCP helps reassure the user that the page is
  [useful](/user-centric-performance-metrics/#questions).
{% endAside %}

Historically, it's been a challenge for web developers to measure how quickly
the main content of a web page loads and is visible to users.

Older metrics like
[load](https://developer.mozilla.org/docs/Web/Events/load) or
[DOMContentLoaded](https://developer.mozilla.org/docs/Web/Events/DOMContentLoaded)
are not good because they don't necessarily correspond to what the user sees on
their screen. And newer, user-centric performance metrics like [First Contentful
Paint (FCP)](/fcp/) only capture the very beginning of the loading experience.
If a page shows a splash screen or displays a loading indicator, this moment is
not very relevant to the user.

In the past we've recommended performance metrics like [First Meaningful Paint
(FMP)](https://developer.chrome.com/docs/lighthouse/performance/first-meaningful-paint/) and [Speed Index (SI)](https://developer.chrome.com/docs/lighthouse/performance/speed-index/) (both
available in Lighthouse) to help capture more of the loading experience after
the initial paint, but these metrics are complex, hard to explain, and often
wrong&mdash;meaning they still do not identify when the main content of the page
has loaded.

Sometimes simpler is better. Based on discussions in the [W3C Web
Performance Working Group](https://www.w3.org/webperf/) and research done at
Google, we've found that a more accurate way to measure when the main content
of a page is loaded is to look at when the largest element was rendered.

## What is LCP?

The Largest Contentful Paint (LCP) metric reports the render time of the largest
[image or text block](#what-elements-are-considered) visible within the
viewport, relative to when the page [first started
loading](https://w3c.github.io/hr-time/#timeorigin-attribute).

### What is a good LCP score?

To provide a good user experience, sites should strive to have Largest
Contentful Paint of **2.5 seconds** or less. To ensure you're hitting this
target for most of your users, a good threshold to measure is the **75th
percentile** of page loads, segmented across mobile and desktop devices.

<figure>
  <picture>
    <source
      srcset="{{ "image/tcFciHGuF3MxnTr1y5ue01OGLBn2/elqsdYqQEefWJbUM2qMO.svg" | imgix }}"
      media="(min-width: 640px)"
      width="800"
      height="200">
    {%
      Img
        src="image/eqprBhZUGfb8WYnumQ9ljAxRrA72/8ZW8LQsagLih1ZZoOmMR.svg",
        alt="Good LCP values are 2.5 seconds or less, poor values are greater than 4.0 seconds, and anything in between needs improvement",
        width="640",
        height="480"
    %}
  </picture>
</figure>

{% Aside %}
  To learn more about the research and methodology behind this recommendation,
  see: [Defining the Core Web Vitals metrics
  thresholds](/defining-core-web-vitals-thresholds/)
{% endAside %}

### What elements are considered?

As currently specified in the [Largest Contentful Paint
API](https://wicg.github.io/largest-contentful-paint/), the types of elements
considered for Largest Contentful Paint are:

- `<img>` elements
- `<image>` elements inside an `<svg>` element
- `<video>` elements with a poster image (the poster image load time is used)
- An element with a background image loaded via the
  [`url()`](https://developer.mozilla.org/docs/Web/CSS/url()) function
  (as opposed to a
  [CSS gradient](https://developer.mozilla.org/docs/Web/CSS/CSS_Images/Using_CSS_gradients))
- [Block-level](https://developer.mozilla.org/docs/Web/HTML/Block-level_elements)
  elements containing text nodes or other inline-level text elements children.
- The first frame painted for autoplaying `<video>` elements (as of [August 2023](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/speed/metrics_changelog/lcp.md))
- The first frame of an animated image format, such as animated GIFs (as of [August 2023](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/speed/metrics_changelog/lcp.md))

As well as only considering some elements, certain heurisitics are applied to exclude certain elements that are likely to be seen as "non-contentful" to users. For Chromium-based browsers, these include:

- Elements with an opacity of 0, that are invisible to the user
- Elements that cover the full viewport, that are likely considered as background rather than content
- Placeholder images or other images with a low entropy, that likely do not reflect the true content of the page

Browsers are likely to continue to improve these heuristics to ensure we match user expectations of what the largest _contentful_ element is.

These "contentful" heuristics may differ from those used by [First Contentful Paint (FCP)](/fcp/), which may consider some of these elements, such as placeholder images or full viewport images, even if they are ineligible to be LCP candidates. Despite both using "contentful" in their name, the aim of these metrics is different. FCP measures when _any content_ is painted to screen and LCP when the _main content_ is painted so LCP is intented to be more selective.

### How is an element's size determined?

The size of the element reported for Largest Contentful Paint is typically the
size that's visible to the user within the viewport. If the element extends
outside of the viewport, or if any of the element is clipped or has non-visible
[overflow](https://developer.mozilla.org/docs/Web/CSS/overflow), those
portions do not count toward the element's size.

For image elements that have been resized from their [intrinsic
size](https://developer.mozilla.org/docs/Glossary/Intrinsic_Size), the
size that gets reported is either the visible size or the intrinsic size,
whichever is smaller. For example, images that are shrunk down to a much
smaller than their intrinsic size will only report the size they're displayed
at, whereas images that are stretched or expanded to a larger size will only
report their intrinsic sizes.

For text elements, only the size of their text nodes is considered (the smallest
rectangle that encompasses all text nodes).

For all elements, any margin, padding, or border applied via CSS is not
considered.

{% Aside %}
  Determining which text nodes belong to which elements can sometimes
  be tricky, especially for elements whose children includes inline elements and
  plain text nodes but also block-level elements. The key point is that every
  text node belongs to (and only to) its closest block-level ancestor element.
  In [spec
  terms](https://wicg.github.io/element-timing/#set-of-owned-text-nodes):
  each text node belongs to the element that generates its [containing
  block](https://developer.mozilla.org/docs/Web/CSS/Containing_block).
{% endAside %}

### When is largest contentful paint reported?

Web pages often load in stages, and as a result, it's possible that the largest
element on the page might change.

To handle this potential for change, the browser dispatches a
[`PerformanceEntry`](https://developer.mozilla.org/docs/Web/API/PerformanceEntry)
of type `largest-contentful-paint` identifying the largest contentful element
as soon as the browser has painted the first frame. But then, after rendering
subsequent frames, it will dispatch another
[`PerformanceEntry`](https://developer.mozilla.org/docs/Web/API/PerformanceEntry)
any time the largest contentful element changes.

For example, on a page with text and a hero image the browser may initially just
render the text&mdash;at which point the browser would dispatch a
`largest-contentful-paint` entry whose `element` property would likely reference
a `<p>` or `<h1>`. Later, once the hero image finishes loading, a second
`largest-contentful-paint` entry would be dispatched and its `element` property
would reference the `<img>`.

It's important to note that an element can only be considered the largest
contentful element once it has rendered and is visible to the user. Images that
have not yet loaded are not considered "rendered". Neither are text nodes using
web fonts during the [font block
period](https://developer.mozilla.org/docs/Web/CSS/@font-face/font-display#The_font_display_timeline).
In such cases a smaller element may be reported as the largest contentful
element, but as soon as the larger element finishes rendering, it'll be
reported via another  `PerformanceEntry` object.

In addition to late-loading images and fonts, a page may add new elements to
the DOM as new content becomes available. If any of these new elements is
larger than the previous largest contentful element, a new `PerformanceEntry`
will also be reported.

If an element that is currently the largest contentful element is removed from
the viewport (or even removed from the DOM), it will remain the largest
contentful element unless a larger element is rendered.

{% Aside %}
  Prior to Chrome 88, removed elements were not considered as largest contentful
  elements, and removing the current candidate would trigger a new
  `largest-contentful-paint` entry to be dispatched. However, due to popular UI
  patterns such as image carousels that often removed DOM elements, the metric
  was updated to more accurately reflect what users experience. See the
  [CHANGELOG](https://chromium.googlesource.com/chromium/src/+/main/docs/speed/metrics_changelog/2020_11_lcp.md)
  for more details.
{% endAside %}

The browser will stop reporting new entries as soon as the user interacts with
the page (via a tap, scroll, or keypress), as user interaction often changes
what's visible to the user (which is especially true with scrolling).

For analysis purposes, you should only report the most recently dispatched
`PerformanceEntry` to your analytics service.

{% Aside 'caution' %}
  Since users can open pages in a background tab, it's possible that
  `largest-contentful-paint` entries will not be dispatched until the user
  focuses the tab, which can be much later than when they first loaded it.

  Google tools that measure LCP do not report this metric if the page was
  loaded in the background, as it does not reflect the user-perceived load time.
{% endAside %}

#### Load time vs. render time

For security reasons, the render timestamp of images is not exposed for
cross-origin images that lack the
[`Timing-Allow-Origin`](https://developer.mozilla.org/docs/Web/HTTP/Headers/Timing-Allow-Origin)
header. Instead, only their load time is exposed (since this is already exposed
via many other web APIs).

The [usage example](#measure-lcp-in-javascript)
below shows how to handle elements whose render time is not available. But,
when possible, it's always recommended to set the
[`Timing-Allow-Origin`](https://developer.mozilla.org/docs/Web/HTTP/Headers/Timing-Allow-Origin)
header, so your metrics will be more accurate.

### How are element layout and size changes handled?

To keep the performance overhead of calculating and dispatching new performance
entries low, changes to an element's size or position do not generate new LCP
candidates. Only the element's initial size and position in the viewport is
considered.

This means images that are initially rendered off-screen and then transition
on-screen may not be reported. It also means elements initially rendered in the
viewport that then get pushed down, out of view will still report their
initial, in-viewport size.

### Examples

Here are some examples of when the Largest Contentful Paint occurs on a few
popular websites:

{% Img src="image/admin/bsBm8poY1uQbq7mNvVJm.png", alt="Largest Contentful Paint timeline from cnn.com", width="800", height="311" %}

{% Img src="image/admin/xAvLL1u2KFRaqoZZiI71.png", alt="Largest Contentful Paint timeline from techcrunch.com", width="800", height="311" %}

In both of the timelines above, the largest element changes as content loads.
In the first example, new content is added to the DOM and that changes what
element is the largest. In the second example, the layout changes and content
that was previously the largest is removed from the viewport.

While it's often the case that late-loading content is larger than content
already on the page, that's not necessarily the case. The next two examples
show the Largest Contentful Paint occurring before the page fully loads.

{% Img src="image/admin/uJAGswhXK3bE6Vs4I5bP.png", alt="Largest Contentful Paint timeline from instagram.com", width="800", height="311" %}

{% Img src="image/admin/e0O2woQjZJ92aYlPOJzT.png", alt="Largest Contentful Paint timeline from google.com", width="800", height="311" %}

In the first example, the Instagram logo is loaded relatively early and it
remains the largest element even as other content is progressively shown. In
the Google search results page example, the largest element is a paragraph of
text that is displayed before any of the images or logo finish loading. Since
all the individual images are smaller than this paragraph, it remains the
largest element throughout the load process.

{% Aside %}
  In the first frame of the Instagram timeline, you may notice the camera logo
  does not have a green box around it. That's because it's an `<svg>` element,
  and `<svg>` elements are not currently considered LCP candidates. The first
  LCP candidate is the text in the second frame.
{% endAside %}

## How to measure LCP

LCP can be measured [in the lab](/user-centric-performance-metrics/#in-the-lab)
or [in the field](/user-centric-performance-metrics/#in-the-field), and it's
available in the following tools:

### Field tools

- [Chrome User Experience
  Report](https://developer.chrome.com/docs/crux/)
- [PageSpeed Insights](https://pagespeed.web.dev/)
- [Search Console (Core Web Vitals
  report)](https://support.google.com/webmasters/answer/9205520)
- [`web-vitals` JavaScript library](https://github.com/GoogleChrome/web-vitals)

### Lab tools

- [Chrome DevTools](https://developer.chrome.com/docs/devtools/)
- [Lighthouse](https://developer.chrome.com/docs/lighthouse/overview/)
- [PageSpeed Insights](https://pagespeed.web.dev/)
- [WebPageTest](https://webpagetest.org/)

### Measure LCP in JavaScript

To measure LCP in JavaScript, you can use the [Largest Contentful Paint
API](https://wicg.github.io/largest-contentful-paint/). The following example
shows how to create a
[`PerformanceObserver`](https://developer.mozilla.org/docs/Web/API/PerformanceObserver)
that listens for `largest-contentful-paint` entries and logs them to the
console.

```js
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('LCP candidate:', entry.startTime, entry);
  }
}).observe({type: 'largest-contentful-paint', buffered: true});
```

{% Aside 'warning' %}

  This code shows how to log `largest-contentful-paint` entries to the console,
  but measuring LCP in JavaScript is more complicated. See below for details:

{% endAside %}

In the above example, each logged `largest-contentful-paint` entry represents
the current LCP candidate. In general, the `startTime` value of the last entry
emitted is the LCP value&mdash;however, that is not always the case. Not all
`largest-contentful-paint` entries are valid for measuring LCP.

The following section lists the differences between what the API reports and how
the metric is calculated.

#### Differences between the metric and the API

- The API will dispatch `largest-contentful-paint` entries for pages loaded in a
  background tab, but those pages should be ignored when calculating LCP.
- The API will continue to dispatch `largest-contentful-paint` entries after a
  page has been backgrounded, but those entries should be ignored when
  calculating LCP (elements may only be considered if the page was in the
  foreground the entire time).
- The API does not report `largest-contentful-paint` entries when the page is
  restored from the [back/forward cache](/bfcache/#impact-on-core-web-vitals),
  but LCP should be measured in these cases since users experience them as
  distinct page visits.
- The API does not consider elements within iframes but the metric does as they
  are part of the user experience of the page. In pages with an LCP within an
  iframe—for example a poster image on an embedded video—this will
  [show as a difference between CrUX and RUM](/crux-and-rum-differences/#iframes).
  To properly measure LCP you should consider them. Sub-frames can use the API
  to report their `largest-contentful-paint` entries to the parent frame for
  aggregation.

Rather than memorizing all these subtle differences, developers can use the
[`web-vitals` JavaScript library](https://github.com/GoogleChrome/web-vitals) to
measure LCP, which handles these differences for you (where possible):

```js
import {onLCP} from 'web-vitals';

// Measure and log LCP as soon as it's available.
onLCP(console.log);
```

You can refer to [the source code for
`onLCP()`](https://github.com/GoogleChrome/web-vitals/blob/main/src/onLCP.ts)
for a complete example of how to measure LCP in JavaScript.

{% Aside %}
  In some cases (such as cross-origin iframes) it's not possible to measure LCP
  in JavaScript. See the
  [limitations](https://github.com/GoogleChrome/web-vitals#limitations) section
  of the `web-vitals` library for details.
{% endAside %}

### What if the largest element isn't the most important?

In some cases the most important element (or elements) on the page is not the
same as the largest element, and developers may be more interested in measuring
the render times of these other elements instead. This is possible using the
[Element Timing API](https://wicg.github.io/element-timing/), as described in
the article on [custom metrics](/custom-metrics/#element-timing-api).

## How to improve LCP

A full guide on [optimizing LCP](/optimize-lcp/) is available to guide you through the process of identifying LCP timings in the field and using lab data to drill down and optimize them.

## Additional resources

- [Lessons learned from performance monitoring in Chrome](https://youtu.be/ctavZT87syI) by [Annie Sullivan](https://anniesullie.com/) at [performance.now()](https://perfnow.nl/) (2019)

{% include 'content/metrics/metrics-changelog.njk' %}

# Total Blocking Time (TBT)

{% Aside %}

  Total Blocking Time (TBT) is an important [lab
  metric](/user-centric-performance-metrics/#in-the-lab) for measuring [load
  responsiveness](/user-centric-performance-metrics/#types-of-metrics) because
  it helps quantify the severity of how non-interactive a page is prior to it
  becoming reliably interactive&mdash;a low TBT helps ensure that the page is
  [usable](/user-centric-performance-metrics/#questions).

{% endAside %}

## What is TBT?

The Total Blocking Time (TBT) metric measures the total amount of time between
[First Contentful Paint (FCP)](/fcp/) and [Time to Interactive (TTI)](/tti/)
where the main thread was blocked for long enough to prevent input
responsiveness.

The main thread is considered "blocked" any time there's a [Long
Task](/custom-metrics/#long-tasks-api)&mdash;a task that runs on the main
thread for more than 50 milliseconds (ms). We say the main thread is "blocked"
because the browser cannot interrupt a task that's in progress. So in the event
that a user _does_ interact with the page in the middle of a long task, the
browser must wait for the task to finish before it can respond.

If the task is long enough (anything above 50 ms), it's likely that the
user will notice the delay and perceive the page as sluggish or janky.

The _blocking time_ of a given long task is its duration in excess of 50 ms. And
the _total blocking time_ for a page is the sum of the _blocking time_ for each
long task that occurs between FCP and TTI.

For example, consider the following diagram of the browser's main thread during
page load:

{% Img src="image/admin/clHG8Yv239lXsGWD6Iu6.svg", alt="A tasks timeline on the main thread", width="800", height="156", linkTo=true %}

The above timeline has five tasks, three of which are Long Tasks because their
duration exceeds 50 ms. The next diagram shows the blocking time for each of the
long tasks:

{% Img src="image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xKxwKagiz8RliuOI2Xtc.svg", alt="A tasks timeline on the main thread showing blocking time", width="800", height="156", linkTo=true %}

So while the total time spent running tasks on the main thread is 560 ms, only
345 ms of that time is considered blocking time.

<table>
  <tr>
    <th></th>
    <th>Task duration</th>
    <th>Task blocking time</th>
  </tr>
  <tr>
    <td>Task one</td>
    <td>250 ms</td>
    <td>200 ms</td>
  </tr>
  <tr>
    <td>Task two</td>
    <td>90 ms</td>
    <td>40 ms</td>
  </tr>
  <tr>
    <td>Task three</td>
    <td>35 ms</td>
    <td>0 ms</td>
  </tr>
  <tr>
    <td>Task four</td>
    <td>30 ms</td>
    <td>0 ms</td>
  </tr>
  <tr>
    <td>Task five</td>
    <td>155 ms</td>
    <td>105 ms</td>
  </tr>
  <tr>
    <td colspan="2" ><strong>Total Blocking Time</strong></td>
    <td><strong>345 ms</strong></td>
  </tr>
</table>

### How does TBT relate to TTI?

TBT is a great companion metric for TTI because it helps quantify the severity
of how non-interactive a page is prior it to becoming reliably interactive.

TTI considers a page "reliably interactive" if the main thread has been free of
long tasks for at least five seconds. This means that three, 51 ms tasks spread
out over 10 seconds can push back TTI just as far as a single 10-second long
task&mdash;but those two scenarios would feel very different to a user trying to
interact with the page.

In the first case, three, 51 ms tasks would have a TBT of **3 ms**. Whereas a
single, 10-second long tasks would have a TBT of **9950 ms**. The larger TBT
value in the second case quantifies the worse experience.

## How to measure TBT

TBT is a metric that should be measured [in the
lab](/user-centric-performance-metrics/#in-the-lab). The best way to measure TBT is to run a
Lighthouse performance audit on your site. See the [Lighthouse documentation on
TBT](https://developer.chrome.com/docs/lighthouse/performance/lighthouse-total-blocking-time/) for usage details.

### Lab tools

* [Chrome DevTools](https://developer.chrome.com/docs/devtools/)
* [Lighthouse](https://developer.chrome.com/docs/lighthouse/overview/)
* [WebPageTest](https://www.webpagetest.org/)

{% Aside %}
  While it is possible to measure TBT in the field, it's not recommended as user
  interaction can affect your page's TBT in ways that lead to lots of variance
  in your reports. To understand a page's interactivity in the field, you should
  measure [First Input Delay (FID)](/fid/) and [Interaction to Next Paint (INP)](/inp/).
{% endAside %}

## What is a good TBT score?

To provide a good user experience, sites should strive to have a Total Blocking
Time of less than **200 milliseconds** when tested on **average mobile
hardware**.

For details on how your page's TBT affects your Lighthouse performance score,
see [How Lighthouse determines your TBT
score](https://developer.chrome.com/docs/lighthouse/performance/lighthouse-total-blocking-time/#how-lighthouse-determines-your-tbt-score)

## How to improve TBT

To learn how to improve TBT for a specific site, you can run a Lighthouse
performance audit and pay attention to any specific
[opportunities](https://developer.chrome.com/docs/lighthouse/performance/#opportunities) the audit suggests.

To learn how to improve TBT in general (for any site), refer to the following
performance guides:

- [Reduce the impact of third-party code](https://developer.chrome.com/docs/lighthouse/performance/third-party-summary/)
- [Reduce JavaScript execution time](https://developer.chrome.com/docs/lighthouse/performance/bootup-time/)
- [Minimize main thread work](https://developer.chrome.com/docs/lighthouse/performance/mainthread-work-breakdown/)
- [Keep request counts low and transfer sizes small](https://developer.chrome.com/docs/lighthouse/performance/resource-summary/)

# Time to First Byte (TTFB)

{% BrowserCompat 'api.PerformanceResourceTiming.responseStart' %}

{% Aside %}
Time to First Byte (TTFB) is a foundational metric for measuring connection setup time and web server responsiveness in both the lab and the field. It helps identify when a web server is too slow to respond to requests. In the case of navigation requests&mdash;that is, requests for an HTML document&mdash;it precedes every other meaningful loading performance metric.
{% endAside %}

## What is TTFB?

TTFB is a metric that measures the time between the request for a resource and when the first byte of a response begins to arrive.

<figure>
  {% Img src="image/jL3OLOhcWUQDnR4XjewLBx4e3PC3/ccT8ltSPrTri3tz7AA3h.png", alt="A diagram of network request timings. The phases from left to right are Redirect (which overlaps with Prompt for Unload), Cache, DNS, TCP, Request, Response, Processing, and Load. The associated timings are redirectStart and redirectEnd (which overlap with the Prompt for Unload's unloadEventStart and unloadEventEnd), fetchStart, domainLookupStart, domainLookupEnd, connectStart, secureConnectionStart, connectEnd, requestStart, responseStart, responseEnd, domInteractive, domContentLoadedEventStart, domContentLoadedEventEnd, domComplete, loadEventStart, and loadEventEnd.", width="800", height="337" %}
  <figcaption>
    A diagram of network request phases and their associated timings. TTFB measures the elapsed time between <code>startTime</code> and <code>responseStart</code>.
  </figcaption>
</figure>

TTFB is the sum of the following request phases:

- Redirect time
- Service worker startup time (if applicable)
- DNS lookup
- Connection and TLS negotiation
- Request, up until the point at which the first byte of the response has arrived

Reducing latency in connection setup time and on the backend will contribute to a lower TTFB.

### What is a good TTFB score?

Because TTFB precedes [user-centric metrics](/user-centric-performance-metrics/) such as [First Contentful Paint (FCP)](/fcp/) and [Largest Contentful Paint (LCP)](/lcp/), it's recommended that your server responds to navigation requests quickly enough so that the **75th percentile** of users experience an [FCP within the "good" threshold](/fcp/#what-is-a-good-fcp-score). As a rough guide, most sites should strive to have Time To First Byte of **0.8 seconds** or less.

<figure>
  <picture>
    <source
      srcset="{{ "image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/ILJ1xKjzVisqOPPyHYVA.svg" | imgix }}"
      media="(min-width: 640px)"
      width="800"
      height="200">
    {%
      Img
        src="image/W3z1f5ZkBJSgL1V1IfloTIctbIF3/EcKicxW5ErYYhf8RvpeO.svg",
        alt="Good TTFB values are 0.8 seconds or less, poor values are greater than 1.8 seconds, and anything in between needs improvement",
        width="640",
        height="480"
    %}
  </picture>
</figure>

{% Aside 'important' %}
<p>TTFB is not a [Core Web Vitals](/vitals/) metric, so it's not absolutely necessary that sites meet the "good" TTFB threshold, provided that it doesn't impede their ability to score well on the metrics that matter.<p>
<p>Websites vary in how they deliver content. A low TTFB is crucial for getting markup out to the client as soon as possible. However, if a website delivers the initial markup quickly, but that markup then requires JavaScript to populate it with meaningful content—as is the the case with Single Page Applications (SPAs)—then achieving the lowest possible TTFB is especially important so that the client-rendering of markup can occur sooner.<p>
<p>Conversely, a server-rendered site that does not require as much client-side work could have a higher TTFB, but better FCP and LCP values than an entirely client-rendered experience. This is why the TTFB thresholds are a “rough guide”, and will need to be weighed against how your site delivers its core content.<p>
{% endAside %}

## How to measure TTFB

TTFB can be measured in [the lab](/user-centric-performance-metrics/#in-the-lab) or in [the field](/user-centric-performance-metrics/#in-the-field) in the following ways.

### Field tools

- [Chrome User Experience Report](https://developer.chrome.com/docs/crux/)
- [`web-vitals` JavaScript library](https://github.com/GoogleChrome/web-vitals)

### Lab tools

- In the [network panel](https://developer.chrome.com/docs/devtools/network/) of Chrome's DevTools
- [WebPageTest](https://www.webpagetest.org/)

### Measure TTFB in JavaScript

You can measure the TTFB of [navigation requests](https://developer.mozilla.org/docs/Web/API/Request/mode) in the browser with the [Navigation Timing API](https://developer.mozilla.org/docs/Web/API/Navigation_timing_API). The following example shows how to create a [`PerformanceObserver`](https://developer.mozilla.org/docs/Web/API/PerformanceObserver) that listens for a `navigation` entry and logs it to the console:

```javascript
new PerformanceObserver((entryList) => {
  const [pageNav] = entryList.getEntriesByType('navigation');

  console.log(`TTFB: ${pageNav.responseStart}`);
}).observe({
  type: 'navigation',
  buffered: true
});
```

{% Aside 'caution' %}
Not all browsers support `PerformanceObserver` or its `buffered` flag. To get the largest possible browser support, consider adopting the `web-vitals` package, which is discussed below.
{% endAside %}

The [`web-vitals` JavaScript library](https://github.com/GoogleChrome/web-vitals) can also measure TTFB in the browser with less complexity:

```javascript
import {onTTFB} from 'web-vitals';

// Measure and log TTFB as soon as it's available.
onTTFB(console.log);
```

### Measuring resource requests

TTFB applies to _all_ requests, not just navigation requests. In particular, resources hosted on cross-origin servers can introduce latency due to the need to set up connections to those servers. To measure TTFB for resources in the field, use the [Resource Timing API](https://developer.mozilla.org/docs/Web/API/Resource_Timing_API/Using_the_Resource_Timing_API) within a `PerformanceObserver`:

```javascript
new PerformanceObserver((entryList) => {
  const entries = entryList.getEntries();

  for (const entry of entries) {
    // Some resources may have a responseStart value of 0, due
    // to the resource being cached, or a cross-origin resource
    // being served without a Timing-Allow-Origin header set.
    if (entry.responseStart > 0) {
      console.log(`TTFB: ${entry.responseStart}`, entry.name);
    }
  }
}).observe({
  type: 'resource',
  buffered: true
});
```

The above code snippet is similar to the one used to measure the TTFB for a navigation request, except instead of querying for `'navigation'` entries, you query for `'resource'` entries instead. It also accounts for the fact that some resources loaded from the primary origin may return a value of `0`, since the connection is already open, or a resource is instantaneously retrieved from a cache.

{% Aside 'gotchas' %}
TTFB for cross-origin requests will not be measurable in the field if cross-origin servers fail to set a [`Timing-Allow-Origin` header](https://developer.mozilla.org/docs/Web/HTTP/Headers/Timing-Allow-Origin).
{% endAside %}

## How to improve TTFB

An in-depth guide on [optimizing TTFB](/optimize-ttfb/) has been published to give you more guidance on improving your website's TTFB.

# Time to Interactive (TTI)

{% Aside %}
  Time to Interactive (TTI) is an important [lab
  metric](/user-centric-performance-metrics/#in-the-lab) for measuring [load
  responsiveness](/user-centric-performance-metrics/#types-of-metrics). It helps
  identify cases where a page _looks_ interactive but actually isn't. A fast TTI
  helps ensure that the page is
  [usable](/user-centric-performance-metrics/#questions).
{% endAside %}

## What is TTI?

The TTI metric measures the time from when the page starts
loading to when its main sub-resources have loaded and it is capable of reliably
responding to user input quickly.

To calculate TTI based on a [performance
trace](https://developer.chrome.com/docs/devtools/evaluate-performance/reference/)
of a web page, follow these steps:

1. Start at [First Contentful Paint (FCP)](/fcp/).
2. Search forward in time for a quiet window of at least five seconds, where
   _quiet window_ is defined as: no [long
   tasks](/custom-metrics/#long-tasks-api) and no more than two in-flight
   network GET requests.
3. Search backwards for the last long task before the quiet window, stopping at
   FCP if no long tasks are found.
4. TTI is the end time of the last long task before the quiet window (or the
   same value as FCP if no long tasks are found).

The following diagram should help visualize the steps above:

{% Img src="image/admin/WZM0n4aXah67lEyZugOT.svg", alt="A page load timeline showing how to compute TTI", width="800", height="473", linkTo=true %}

Historically, developers have optimized pages for fast render times,
sometimes at the expense of TTI.

Techniques like server-side rendering (SSR) can lead to scenarios where a page
_looks_ interactive (that is, links and buttons are visible on the screen), but it's not
_actually_ interactive because the main thread is blocked or
because the JavaScript code controlling those elements hasn't loaded.

When users try to interact with a page that looks interactive but actually
isn't, they'll likely respond in one of two ways:

* In the best-case scenario, they'll be annoyed that the page is slow to respond.
* In the worst-case scenario, they'll assume the page is broken and likely
  leave. They may even lose confidence or trust in the value of your brand.

To avoid this problem, make every effort to minimize the difference
between FCP and TTI. And in cases where a noticeable difference does exist,
make it clear through visual indicators that the components on your page are not yet
interactive.

## How to measure TTI

TTI is a metric that's best measured [in the
lab](/user-centric-performance-metrics/#in-the-lab). The best way to measure TTI is to run a
Lighthouse performance audit on your site. See the [Lighthouse documentation on
TTI](/tti/) for usage details.

### Lab tools

* [Lighthouse](https://developer.chrome.com/docs/lighthouse/overview/)
* [WebPageTest](https://www.webpagetest.org/)

{% Aside %}
  While it's possible to measure TTI in the field, it's not recommended, as user
  interaction can affect your page's TTI in ways that lead to lots of variance
  in your reports. To understand a page's interactivity in the field, you should
  measure [First Input Delay (FID)](/fid/) and [Interaction to Next Paint (INP)](/inp/).
{% endAside %}

## What is a good TTI score?

To provide a good user experience, sites should strive to have a Time to
Interactive of less than **5 seconds** when tested on **average mobile
hardware**.

For details on how your page's TTI affects your Lighthouse performance score,
see [How Lighthouse determines your TTI
score](https://developer.chrome.com/docs/lighthouse/performance/interactive/#how-lighthouse-determines-your-tti-score).

## How to improve TTI

To learn how to improve TTI for a specific site, you can run a Lighthouse
performance audit and pay attention to any specific
[opportunities](https://developer.chrome.com/docs/lighthouse/performance/#opportunities) the audit suggests.

To learn how to improve TTI in general (for any site), refer to the following
performance guides:

* [Minify JavaScript](https://developer.chrome.com/docs/lighthouse/performance/unminified-javascript/e.com/docs/lighthouse/performance/unminified-javascript/)
* [Preconnect to required origins](https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preconnect/)
* [Preload key requests](https://developer.chrome.com/docs/lighthouse/performance/uses-rel-preload/)
* [Reduce the impact of third-party code](https://developer.chrome.com/docs/lighthouse/performance/third-party-summary/)
* [Minimize critical request depth](https://developer.chrome.com/docs/lighthouse/performance/critical-request-chains/)
* [Reduce JavaScript execution time](https://developer.chrome.com/docs/lighthouse/performance/bootup-time/)
* [Minimize main thread work](https://developer.chrome.com/docs/lighthouse/performance/mainthread-work-breakdown/)
* [Keep request counts low and transfer sizes small](https://developer.chrome.com/docs/lighthouse/performance/resource-summary/)

# User-centric performance metrics

We've all heard how important performance is. But when we talk about
performance&mdash;and about making websites "fast"&mdash;what specifically do we
mean?

The truth is performance is relative:

- A site might be fast for one user (on a fast network with a powerful device)
  but slow for another user (on a slow network with a low-end device).
- Two sites may finish loading in the exact same amount of time, yet one may
  _seem_ to load faster (if it loads content progressively rather than waiting
  until the end to display anything).
- A site might _appear_ to load quickly but then respond slowly (or not at all)
  to user interaction.

So when talking about performance, it's important to be precise and to refer to
performance in terms of objective criteria that can be quantitatively measured.
These criteria are known as _metrics_.

But just because a metric is based on objective criteria and can be
quantitatively measured, it doesn't necessarily mean those measurements are
useful.

## Defining metrics

Historically, web performance has been measured with the
<code>[load](https://developer.mozilla.org/docs/Web/API/Window/load_event)</code>
event. However, even though <code>load</code> is a well-defined moment in a
page's lifecycle, that moment doesn't necessarily correspond with anything the
user cares about.

For example, a server could respond with a minimal page that "loads" immediately
but then defers fetching content and displaying anything on the page until
several seconds after the `load` event fires. While such a page might
technically have a fast load time, that time would not correspond to how a user
actually experiences the page loading.

Over the past few years, members of the Chrome team&mdash;in collaboration with
the [W3C Web Performance Working Group](https://www.w3.org/webperf/)&mdash;have
been working to standardize a set of new APIs and metrics that more accurately
measure how users experience the performance of a web page.

To help ensure the metrics are relevant to users, we frame them around a few key
questions:

<table id="questions">
  <tr>
    <td><strong>Is it happening?</strong></td>
    <td>Did the navigation start successfully? Has the server responded?</td>
  </tr>
  <tr>
    <td><strong>Is it useful?</strong></td>
    <td>Has enough content rendered that users can engage with it?</td>
  </tr>
  <tr>
    <td><strong>Is it usable?</strong></td>
    <td>Can users interact with the page, or is it busy?</td>
  </tr>
  <tr>
    <td><strong>Is it delightful?</strong></td>
    <td>Are the interactions smooth and natural, free of lag and jank?</td>
  </tr>
</table>

## How metrics are measured

Performance metrics are generally measured in one of two ways:

- **In the lab:** using tools to simulate a page load in a consistent,
  controlled environment
- **In the field**: on real users actually loading and interacting with the page

Neither of these options is necessarily better or worse than the other&mdash;in
fact you generally want to use both to ensure good performance.

### In the lab

Testing performance in the lab is essential when developing new features. Before
features are released in production, it's impossible to measure their
performance characteristics on real users, so testing them in the lab before the
feature is released is the best way to prevent performance regressions.

### In the field

On the other hand, while testing in the lab is a reasonable proxy for
performance, it isn't necessarily reflective of how all users experience your
site in the wild.

The performance of a site can vary dramatically based on a user's device
capabilities and their network conditions. It can also vary based on whether (or
how) a user is interacting with the page.

Moreover, page loads may not be deterministic. For example, sites that load
personalized content or ads may experience vastly different performance
characteristics from user to user. A lab test will not capture those
differences.

The only way to truly know how your site performs for your users is to actually
measure its performance as those users are loading and interacting with it. This
type of measurement is commonly referred to as [Real User
Monitoring](https://en.wikipedia.org/wiki/Real_user_monitoring)&mdash;or RUM for
short.

## Types of metrics

There are several other types of metrics that are relevant to how users perceive
performance.

- **Perceived load speed:** how quickly a page can load and render all of its
  visual elements to the screen.
- **Load responsiveness:** how quickly a page can load and execute any required
  JavaScript code in order for components to respond quickly to user interaction
- **Runtime responsiveness:** after page load, how quickly can the page respond
  to user interaction.
- **Visual stability:** do elements on the page shift in ways that users don't
  expect and potentially interfere with their interactions?
- **Smoothness:** do transitions and animations render at a consistent frame
  rate and flow fluidly from one state to the next?

Given all the above types of performance metrics, it's hopefully clear that no
single metric is sufficient to capture all the performance characteristics of a
page.

## Important metrics to measure

- **[First Contentful Paint (FCP)](/fcp/):** measures the time from when the
  page starts loading to when any part of the page's content is rendered on the
  screen. _([lab](#in-the-lab), [field](#in-the-field))_
- **[Largest Contentful Paint (LCP)](/lcp/):** measures the time from when the
  page starts loading to when the largest text block or image element is
  rendered on the screen. _([lab](#in-the-lab), [field](#in-the-field))_
- **[First Input Delay (FID)](/fid/):** measures the time from when a user first
  interacts with your site (when they click a link, tap a button, or use a
  custom, JavaScript-powered control) to the time when the browser is actually
  able to respond to that interaction. _([field](#in-the-field))_
- **[Interaction to Next Paint (INP)](/inp/)**: measures the latency of every
  tap, click, or keyboard interaction made with the page, and&mdash;based on the
  number of interactions&mdash;selects the worst interaction latency of the page
  (or close to the highest) as a single, representative value to describe a
  page's overall responsiveness. _([lab](#in-the-lab), [field](#in-the-field))_
- **[Time to Interactive (TTI)](/tti/):** measures the time from when the page
  starts loading to when it's visually rendered, its initial scripts (if any)
  have loaded, and it's capable of reliably responding to user input quickly.
  _([lab](#in-the-lab))_
- **[Total Blocking Time (TBT)](/tbt/):** measures the total amount of time
  between FCP and TTI where the main thread was blocked for long enough to
  prevent input responsiveness. _([lab](#in-the-lab))_
- **[Cumulative Layout Shift (CLS)](/cls/):** measures the cumulative score of
  all unexpected layout shifts that occur between when the page starts loading
  and when its [lifecycle
  state](https://developer.chrome.com/blog/page-lifecycle-api/)
  changes to hidden. _([lab](#in-the-lab), [field](#in-the-field))_
- **[Time to First Byte (TTFB)](/ttfb/)**: measures the time it takes for the
  network to respond to a user request with the first byte of a resource.
  _([lab](#in-the-lab), [field](#in-the-field))_

While this list includes metrics measuring many of the various aspects of
performance relevant to users, it does not include everything. For example, runtime
responsiveness and smoothness are not currently covered.

In some cases, new metrics will be introduced to cover missing areas, but in
other cases the best metrics are ones specifically tailored to your site.

## Custom metrics

The performance metrics listed above are good for getting a general
understanding of the performance characteristics of most sites on the web.
They're also good for having a common set of metrics for sites to compare their
performance against their competitors.

However, there may be times when a specific site is unique in some way that
requires additional metrics to capture the full performance picture. For
example, the LCP metric is intended to measure when a page's main content has
finished loading, but there could be cases where the largest element is not part
of the page's main content and thus LCP may not be relevant.

To address such cases, the Web Performance Working Group has also standardized
lower-level APIs that can be useful for implementing your own custom metrics:

- [User Timing API](https://w3c.github.io/user-timing/)
- [Long Tasks API](https://w3c.github.io/longtasks/)
- [Element Timing API](https://wicg.github.io/element-timing/)
- [Navigation Timing API](https://w3c.github.io/navigation-timing/)
- [Resource Timing API](https://w3c.github.io/resource-timing/)
- [Server timing](https://w3c.github.io/server-timing/)

Refer to the guide on [Custom Metrics](/custom-metrics/) to learn how to use
these APIs to measure performance characteristics specific to your site.