completefrontend
Open lab My progress
THE WEB PERFORMANCE FIELD GUIDE

The Performance panel

Live metrics, recording a trace, throttling, and field data side by side: your starting point for every investigation. The course's store, measured the way the panel measures it, on this computer and on DevTools' calibrated phones.

By the end, you can
  • 01
    Read the live viewRead LCP, CLS and INP before you record anything, and split a slow click into its three phases.
  • 02
    Match your usersUse field data, calibrated CPU throttling and a network preset, so your computer behaves like your users' phones.
  • 03
    Test one file at a timeBlock or slow a single request, and measure what that one file costs the page.
  • 04
    Record the right thingPick Record and reload or Record for your question, know what the tracks show, and save a trace safely.

Your starting point

Asha opens the Leaf and Kettle page on her phone, on the bus. The picture of mint tea takes ages to appear. She taps Add to cart, and for a moment nothing happens. She writes to the shop: "Your page is slow."

You open the same page on your laptop. The picture appears at once. The button answers at once. So who's right? And if Asha is, how do you find out why?

Chrome answers both questions in the Performance panel. It's a tab in DevTools, the tools built into Chrome for looking inside a page. Open it, and it starts measuring straight away.

The course loaded its store, Leaf and Kettle from Your toolkit and the course project, and measured it the way the panel does. It did that twice. Once on this computer as it is. Once with DevTools making the same computer behave like a cheap phone. Here is when the store's main picture appeared:

The main picture, on two set-upsPop out in the code editor (opens in a new tab)JavaScript
// The store's Largest Contentful Paint (LCP), measured the way the live view does.// Times are in milliseconds (ms) after the page started loading.const lcp = { thisComputer: 112, lowTierPhone: 6200 }; console.log(`On this computer, the main picture appeared at ${lcp.thisComputer} ms.`);console.log(`On a low-tier phone, it appeared at ${lcp.lowTierPhone} ms.`);console.log(`That's ${Math.round(lcp.lowTierPhone / lcp.thisComputer)} times as long.`);
  • Lines 1 and 2 are comments. Largest Contentful Paint, or LCP, is the moment the biggest picture or block of text on the screen appeared. On the store, that's the main picture. Times are in milliseconds (ms), thousandths of a second.
  • Line 3 holds the two times. lowTierPhone is this same computer, slowed down by DevTools to behave like a cheap phone.
  • Lines 5 to 7 print On this computer, the main picture appeared at 112 ms., On a low-tier phone, it appeared at 6200 ms. and That's 55 times as long.

So you're both right. On your laptop, the page is fast. On a cheap phone, the same page took more than six seconds to show its main picture. That's why you start in this panel: it can show you the page the way your users get it, not only the way your laptop gets it.

Those numbers tell you that the page is slow. To learn why, you make a recording. The panel's reference suggests an order: watch the live numbers first, and record once a problem keeps showing up. Here is a whole investigation, in five steps:

A first investigation in the Performance panel: read the live metrics, compare them with field data, set the environment to match your users, record a trace, and save it.live viewfield dataenvironmentrecordsaveno recording neededa trace
The first three steps need no recording: the live view measures while you use the page. Record only once you know what you're looking for.
  1. Live view. Read the numbers the panel shows while you use the page.
  2. Field data. Put your real users' numbers beside yours.
  3. Environment. Slow your computer down until it behaves like your users' phones.
  4. Record. Capture everything the page does while the problem happens.
  5. Save. Keep the recording, and send it to whoever needs it.
Real-life analogyThe shop's report card on the counter

Leaf and Kettle keeps a report card on the counter. Each time a customer is served, the person at the till writes down how long it took, and marks it: good, needs work, or poor. Nobody plans anything special. The card fills up as the day goes on. One glance tells you how today is going. It doesn't tell you why a customer waited.

In real life: The report card on the counter
In DevTools: The live metrics view, the first thing the panel shows
In real life: A new line each time a customer is served
In DevTools: LCP, CLS and INP, updated as you load and use the page
In real life: The marks: good, needs work, poor
In DevTools: The ratings: good, needs improvement, poor
In real life: A glance at the card
In DevTools: Reading the numbers, with no recording
The main idea

The Performance panel measures your page the moment you open it. Read those live numbers first. Make your computer behave like your users' devices. Then record, with one question in mind. A recording of the load answers questions about loading. A recording of a click answers questions about that click.

The JavaScript lesson Measuring performance covers the browser APIs behind these numbers. This lesson tours the panel that shows them, one step of the picture at a time.

The live metrics view

Open the Performance panel, and the first thing you see is the live metrics view: the shop's report card. The panel's docs say that when you open it, "it immediately captures and shows you your local Largest Contentful Paint (LCP) and Cumulative Layout Shift (CLS)". Local means measured here, in your own browser.

The view has three cards, one for each Core Web Vital. These are three measures that web.dev says every site owner should track. Each card is rated good, needs improvement, or poor:

  • LCP measures loading: when the biggest picture or block of text appeared. It's good up to 2.5 seconds, and poor above 4.
  • Cumulative Layout Shift, or CLS, measures stability: how much the content jumped around by surprise. 0 means nothing moved. It's good up to 0.1, and poor above 0.25.
  • Interaction to Next Paint, or INP, measures how quickly the page answers you. It times each tap, click or key press until the screen shows the result. Over a whole visit, INP keeps the slowest one, ignoring outliers. It's good up to 200 ms, and poor above 500.

The INP card only fills in once you tap, click or type. The other two fill in as the page loads.

You don't record anything to get these numbers. Rick Viscomi, who introduced the view, goes further: "you don't even need to have DevTools open; the metrics are gathered in the background." So when a page feels slow while you browse, its numbers are already waiting for you.

The numbers come from a small script DevTools adds to the page. It's built on Google's web-vitals library, which sites also use to measure their own users.

Each interaction, phase by phase

Under the cards are two tabs. Interactions lists every tap, click and key press, with how long each took. Layout shifts lists every time content moved, with its score. Hover over an interaction's element, and DevTools highlights it on the page.

The Interactions tab also splits each interaction into three phases. Think of ordering tea at the counter: you wait for someone to take your order, they make it, and then they hand it over.

  • Input delay runs from your click until the page's event handlers start. An event handler is a function the page runs when something happens, like a click.
  • Processing is the handlers running.
  • Presentation delay runs from the handlers finishing until the next frame is on the screen. A frame is one picture the screen shows.

Here is the store's click on Add to cart, split the same way. The course clicked it on the mid-tier phone:

The click on Add to cart, phase by phasePop out in the code editor (opens in a new tab)JavaScript
// The click on Add to cart, on the mid-tier phone, in ms.const click = { inputDelay: 1, processing: 104.9, presentation: 110.1 };const total = click.inputDelay + click.processing + click.presentation;const share = (ms) => `${Math.round((ms / total) * 100)}%`; console.log(`The click took ${Math.round(total)} ms.`);console.log(`Waiting for the handlers to start: ${share(click.inputDelay)}`);console.log(`Running the handlers: ${share(click.processing)}`);console.log(`Drawing the next frame: ${share(click.presentation)}`);
  • Line 1 is a comment. The mid-tier phone is this computer, slowed down to behave like a mid-range phone. The throttling section shows how.
  • Line 2 holds the three phases the course measured, in ms.
  • Line 3 adds them up. Line 4 is a small function that turns one phase into its share of the total.
  • Lines 6 to 9 print The click took 216 ms., Waiting for the handlers to start: 0%, Running the handlers: 49% and Drawing the next frame: 51%.

The click hardly waited to start: its input delay was 1 ms. The rest split almost evenly between running the store's code and drawing the result. That fits what the store does. Every click rebuilds a list of prices and draws all 1,800 teas again. So a fix has two places to look: the handler's work, and the drawing it causes.

Build an Interactions table yourself. Each button below is slow in a different way. Try Quick click while the page is busy, and watch the input delay grow:

Playground: an Interactions table
What this table runs, shortenedJavaScript
new PerformanceObserver((list) => {  for (const entry of list.getEntries()) {    if (!entry.interactionId) continue;    const inputDelay = entry.processingStart - entry.startTime;    const processing = entry.processingEnd - entry.processingStart;    const presentation = entry.startTime + entry.duration - entry.processingEnd;    show(entry.interactionId, entry.target, { inputDelay, processing, presentation });  }}).observe({ type: "event", durationThreshold: 16 });
Interactionslive

None yet.

Try it yourself
Your interactions

Checking what your browser can report…

This runs in your browser. Slow handler keeps the page busy for 150 ms. Big update adds or removes 6,000 elements. Busy page runs 250 ms tasks for 3 seconds: click Quick click while it's busy.

Interactions shorter than 16 ms don't show up. 16 ms is the shortest time Event Timing, the browser API behind this table, can be asked to report.

Comparing with your real users' data

The live view measured your visit: one visit, on one computer, on one network. Is it like your users' visits? You can't tell from one visit.

Real-life analogyYour own cup, and the comment box

Each morning, the owner of Leaf and Kettle makes herself a cup of tea. It's always quick: the shop is empty. The customers' comment box tells another story. It holds four weeks of cards, each saying how long that customer waited. She sorts them from the shortest wait to the longest, and reads the card three-quarters of the way through.

In real life: Her own cup each morning
In DevTools: Your local metrics, in the live view
In real life: The comment box
In DevTools: Field data from the Chrome UX Report (CrUX)
In real life: Four weeks of cards
In DevTools: 28 days of real Chrome users' visits
In real life: The card three-quarters of the way through
In DevTools: The 75th percentile

Field data is measured on real users' devices, during real visits. The panel gets it from the Chrome UX Report, or CrUX: Google's public dataset of how real Chrome users experience websites. Rick Viscomi describes its API as "a 28-day aggregation of real-user experiences on a given web page and origin." An origin is a whole website, like https://completefrontend.com.

The panel shows each field number at the 75th percentile. Put every visit's time in order, from fastest to slowest, and take the one three-quarters of the way along. Three in four visits were that fast or faster. So the number describes most of your users, not only the lucky ones.

To turn field data on:

  1. Under Field data, click Set up.
  2. Read the privacy disclosure, and click Ok.

DevTools then fetches your users' numbers and shows them beside your own.

The store has no field data. CrUX only includes public pages with enough visitors, and the store runs on your own computer. A site you're building has the same problem at localhost. The setup dialog's advanced section lets you map your development address to your real one. The docs map http://localhost:8080 to https://example.com, so localhost:8080/page1 shows the field data for example.com/page1.

Why bother? In Viscomi's words, "if your local experience is unrealistically fast, you're probably overlooking some real-world issues." Field data shows you how far your computer is from your users. The Chrome User Experience Report covers CrUX in depth.

Which network should you test on?

Field data does one more job. It tells the panel how slow your users' network is, so the panel can suggest a setting that matches it.

It reads one number: the round trip time at the 75th percentile. A round trip is a message going from the device to the server, and the answer coming back. The panel compares it with DevTools' three network presets. Each preset imitates one round trip time:

How far each preset is from your usersPop out in the code editor (opens in a new tab)JavaScript
// The round trip each DevTools preset imitates, in ms.const presets = { "Fast 4G": 60, "Slow 4G": 150, "3G": 400 };const yourUsers = 120; for (const [name, roundTrip] of Object.entries(presets)) {  console.log(`${name} is ${Math.abs(roundTrip - yourUsers)} ms away.`);}
  • Line 2 holds the round trip each preset imitates, from DevTools' source.
  • Line 3 is your users' round trip. 120 ms is an example, not a measurement.
  • Lines 5 to 7 print Fast 4G is 60 ms away., Slow 4G is 30 ms away. and 3G is 280 ms away.

Slow 4G is the closest, so the panel suggests Slow 4G. DevTools' real rule adds two limits. Here it is, written the same way as in DevTools' source code:

DevTools' network recommendationPop out in the code editor (opens in a new tab)JavaScript
const presets = { "Fast 4G": 60, "Slow 4G": 150, "3G": 400 }; function recommend(roundTrip) {  if (roundTrip < 60) return "too fast to simulate with throttling";  let best = null;  for (const [name, target] of Object.entries(presets)) {    const distance = Math.abs(target - roundTrip);    if (distance <= 200 && (best === null || distance <= best.distance)) best = { name, distance };  }  return best ? `similar to ${best.name} throttling` : "no recommendation";} console.log(`Round trip 120 ms: ${recommend(120)}`);
  • Line 4 is the first limit. Below 60 ms, your users' network is faster than every preset. The panel then says "75th percentile is too fast to simulate with throttling".
  • Lines 6 to 9 look at each preset in turn. Line 7 works out how far it is from your users.
  • Line 8 is the second limit. It keeps the closest preset, but only if it's 200 ms away or less.
  • Line 10 gives the advice. If no preset was close enough, the panel gives none.
  • Line 13 asks about 120 ms. It prints Round trip 120 ms: similar to Slow 4G throttling.

The panel shows this advice in the network menu of its Environment settings. The next section turns those settings on.

Calibrated CPU throttling and network throttling

Your laptop is faster than a cheap phone in two ways. Its network is quicker. And its processor, the CPU, gets more work done each second. Throttling means slowing one of them down on purpose, so your computer behaves more like the phone.

Slowing the network

Network throttling is the simpler one. You pick a preset from the network menu, and DevTools slows every request. Read this table across: each row is one preset, the round trip it imitates, the delay DevTools adds to each request, and the download speed.

DevTools' network presets
PresetRound trip it imitatesDelay it adds to each requestDownload speed
Fast 4G60 ms.165 ms.8.1 Mbps.
Slow 4G150 ms.562.5 ms.1.44 Mbps.
3G400 ms.2000 ms.0.4 Mbps.

The added delay is bigger than the round trip. DevTools delays each whole request, while a real phone network delays each small packet of data. The extra delay makes up for that difference. Choosing test conditions compares the two kinds of slowing.

Slowing the CPU

CPU throttling uses a rate. The panel's reference explains: "the 2x slowdown option makes your CPU operate 2 times slower than its usual ability." To do it, DevTools keeps pausing the page's work for short moments, over and over.

So the rate is relative to your computer. 4x on a fast laptop and 4x on an old one give two different results. Which rate makes your computer behave like a phone? Here's how you'd find out on foot:

Real-life analogyWalking at a friend's pace

Ravi walks from his home to the tea shop in 10 minutes. You walk the same road in 2. You want to feel the walk the way Ravi does. "Five times slower" sounds right, but nobody can judge their own pace that well. So you time it. You try a slow pace: 8 minutes, still too fast. Slower: 12 minutes, too slow. Halfway between: 10 minutes. That's your pace.

In real life: Ravi's 10 minutes
In DevTools: A phone's benchmark score: 264 for a low-tier phone
In real life: Trying one pace, and timing the walk
In DevTools: Running the benchmark at one slowdown rate
In real life: Too fast, too slow, then halfway between
In DevTools: Trying the rate in the middle of the ones left
In real life: The pace that takes 10 minutes
In DevTools: The calibrated rate

DevTools does the same. Its Throttling settings can "calibrate CPU throttling presets to approximate the typical performance of low- and mid-tier mobile devices." To time each walk, it uses a benchmark: a small test program that counts how much work the CPU gets done. This one comes from Lighthouse. For 250 ms, a quarter of a second, it builds long strings and copies big arrays, over and over. The more it gets done, the higher its score.

DevTools' source names two targets. A mid-tier phone, "like a Pixel 5", scores 1000. A low-tier phone scores 264. The course ran the same benchmark on this computer, an Apple M1 Pro. At full speed, it scored 3,068.

Two scores, side by sidePop out in the code editor (opens in a new tab)JavaScript
// Benchmark scores: a higher score means a faster CPU.const scores = { thisComputer: 3068, lowTierPhone: 264 }; console.log(`This computer is about ${(scores.thisComputer / scores.lowTierPhone).toFixed(1)} times as fast.`);
  • Line 1 is a comment: a higher score means a faster CPU.
  • Line 2 holds this computer's score and a low-tier phone's.
  • Line 4 divides one by the other. It prints This computer is about 11.6 times as fast.

So should you just pick 11.6x? DevTools doesn't. Its source says the benchmark's results "aren't exactly linear with clock speed": slowing the CPU 11.6 times doesn't simply divide the score by 11.6. So DevTools measures instead, like timing the walk. It runs the benchmark at one rate, reads the score, and tries the rate halfway between the ones left. The course ran those same steps:

Calibrating for a low-tier phonePop out in the code editor (opens in a new tab)JavaScript
// The recorded search for a low-tier phone's score.const target = 264;const tries = [  { slowdown: 9.22, score: 322.5 },  { slowdown: 13.33, score: 218.5 },  { slowdown: 11.28, score: 260.5 },]; for (const { slowdown, score } of tries) {  const verdict = Math.abs(target - score) < 10 ? "close enough" : score > target ? "still too fast" : "too slow";  console.log(`${slowdown}x slower scored ${score}: ${verdict}.`);}
  • Line 2 is the target: a low-tier phone's score.
  • Lines 4 to 6 are the three slowdowns the search tried, in order, with the score at each.
  • Line 10 gives each try a verdict. Within 10 points of the target is close enough. A higher score means the computer is still faster than the phone: still too fast.
  • Line 11 prints 9.22x slower scored 322.5: still too fast., then 13.33x slower scored 218.5: too slow. and 11.28x slower scored 260.5: close enough.

Each try landed in the middle of the slowdowns still left. This picture shows the search closing in:

DevTools' calibration for a low-tier phone, aiming for a score of 264. Try 1: slowdowns from 1x to 17.43x were left; it tried 9.22x, which scored 322.5: still too fast. Try 2: slowdowns from 9.22x to 17.43x were left; it tried 13.33x, which scored 218.5: too slow. Try 3: slowdowns from 9.22x to 13.33x were left; it tried 11.28x, which scored 260.5: close enough.try 19.22xscored 322.5: still too fasttry 213.33xscored 218.5: too slowtry 311.28xscored 260.5: close enough1x17.43x
Each try lands in the middle of the slowdowns that are left. Still too fast: the next try goes slower, to the right. Too slow: it goes back to the left. The third try scored within 10 of 264.

For a mid-tier phone, the search settled on 2.96x after 6 tries. Three whole calibrations gave low-tier rates from 11.14x to 11.36x, and mid-tier rates from 2.96x to 3.13x: the benchmark varies a little from run to run. Your computer will find rates of its own.

To run it, open DevTools' Settings, go to Throttling, and click Calibrate, then Continue. It takes about 5 seconds. The rates it finds then show up in the Performance panel's CPU menu.

The same click, three set-ups

The course loaded the store on a phone-sized screen in three set-ups. Mid-tier phone means the 2.96x CPU rate with Fast 4G. Low-tier phone means 11.28x with Slow 4G. Each time, it clicked Add to cart once, and read INP the way the live view does:

One click, three ratingsPop out in the code editor (opens in a new tab)JavaScript
// The same click on Add to cart, on three set-ups, in ms.const clicks = { "this computer": 88, "the mid-tier phone": 216, "the low-tier phone": 792 };const rating = (ms) => (ms <= 200 ? "good" : ms <= 500 ? "needs improvement" : "poor"); for (const [where, ms] of Object.entries(clicks)) {  console.log(`On ${where}, the click took ${ms} ms: ${rating(ms)}.`);}
  • Line 2 holds the click's time on each set-up, in ms.
  • Line 3 rates a time: good up to 200 ms, poor above 500.
  • Lines 5 to 7 print On this computer, the click took 88 ms: good., On the mid-tier phone, the click took 216 ms: needs improvement. and On the low-tier phone, the click took 792 ms: poor.

Same page, same click, three different marks on the report card. That's why you set up the environment before you judge a number. Pick a set-up below to see all three cards, as the live view would show them:

Playground: the same store, three environments
Local metricsMid-tier phone
LCP1,312 msgood
CLS0.1369needs improvement
INP216 msneeds improvement
Detailsrecorded
First paint700 ms
LCP elementimg#hero
Click: input delay1 ms
Click: processing105 ms
Click: presentation delay110 ms
Try it yourself
The live view would show

Mid-tier phone: this computer with Fast 4G and the CPU 2.96 times slower. The main picture appeared at 1,312 ms: LCP is rated good. The promotion's layout shift scored 0.1369: CLS is rated needs improvement. The click on Add to cart took 216 ms: INP is rated needs improvement.

Recorded in Chrome on a phone-sized screen. The phones use DevTools' network presets and the CPU slowdowns its calibration found on this computer. Each set-up was loaded five times and clicked once each time; you see the load with the middle LCP.

Throttling or blocking single requests

Throttling the whole network slows every file at once. Sometimes your question is about one file. How much does the big picture cost the store? What happens if the script arrives late?

Real-life analogyHolding back one delivery

Leaf and Kettle gets deliveries from several suppliers each morning: milk, cups, cakes. To learn how much the shop depends on the cakes, you don't slow down every van. You cancel the cake order for one day, and watch the counter. Another day, you let only the milk van get stuck in traffic.

In real life: Each supplier's van
In DevTools: Each request the page makes
In real life: Cancelling the cake order
In DevTools: Block request: the file never arrives
In real life: Only the milk van stuck in traffic
In DevTools: Throttle request: one file arrives slowly
In real life: Watching the counter
In DevTools: Reading LCP and the first paint

The Request conditions drawer does this. Its docs say it tests "how your page behaves if certain resources, such as images or stylesheets, are prevented from loading or if the resources load slower." A drawer is a panel that opens along the bottom of DevTools. To use it:

  1. In the Network panel, right-click a request.
  2. Choose Block request or Throttle request.
  3. The Request conditions drawer opens, with a pattern for that request. A pattern is an address that can hold *, meaning "anything here", so one rule can match many files.
  4. Reload the page, and measure again.

The course did both on the mid-tier phone. First, it blocked the main picture, hero.svg:

Blocking the main picturePop out in the code editor (opens in a new tab)JavaScript
// The store on the mid-tier phone: its largest element, and when it was painted (LCP).const normal = { largest: "img#hero", lcp: 1312 };const heroBlocked = { largest: "p", lcp: 1668 }; console.log(`Normally, the largest element was ${normal.largest}, painted at ${normal.lcp} ms.`);console.log(`With hero.svg blocked, it was a ${heroBlocked.largest} element, painted at ${heroBlocked.lcp} ms.`);
  • Line 1 is a comment. LCP is the moment the largest element was painted.
  • Line 2 is the normal load. The largest element was the picture, img#hero.
  • Line 3 is the load with hero.svg blocked.
  • Lines 5 and 6 print Normally, the largest element was img#hero, painted at 1312 ms. and With hero.svg blocked, it was a p element, painted at 1668 ms.

That's a surprise. Without the picture, LCP came later, not sooner. With the picture gone, the largest thing on the screen was a paragraph, a p element. On the store, that's the promotion, which the store adds a second after its HTML has been read. The first paint did come sooner: 548 ms instead of 700. But LCP waits for the largest element, and that one came late. So don't assume removing a file helps. Measure it.

Next, the course slowed only app.js to 3G, and left every other file alone:

Slowing only the scriptPop out in the code editor (opens in a new tab)JavaScript
// The store on the mid-tier phone: when the first paint came, in ms.const firstPaint = { normal: 700, appSlowedTo3G: 5376 }; console.log(`Normally, the first paint came at ${firstPaint.normal} ms.`);console.log(`With app.js slowed to 3G, it came at ${firstPaint.appSlowedTo3G} ms.`);
  • Line 2 holds the first paint, normally and with app.js slowed.
  • Lines 4 and 5 print Normally, the first paint came at 700 ms. and With app.js slowed to 3G, it came at 5376 ms.

One slow script held back the whole page by more than 4.6 seconds. In the store's HTML, app.js is a plain <script> in the <head>. The browser draws nothing until that script has downloaded and run. Render-blocking and parser-blocking resources explains why.

One condition told you what no global setting could: which single file holds up the rest. The conditions only last while DevTools is open, but your patterns stay saved for next time.

Recording a page load vs an interaction

So far you've read numbers. They tell you that the click on the mid-tier phone took 216 ms, and which phase took longest. They don't tell you which function ran, or what it was waiting for. For that, you record.

Real-life analogyA security camera in the shop

Leaf and Kettle has a camera above the counter. To see how opening went, you start it before the shutter goes up, and it stops by itself a little after the shop is ready. To see why one customer waited, you start it when they walk in, and stop it when they leave with their tea. Afterwards, you can rewind the footage and step through it frame by frame.

In real life: Recording opening time, from the shutter going up
In DevTools: Record and reload: a recording of the page load
In real life: Recording one customer, from the door to the till
In DevTools: Record, click, stop: a recording of one interaction
In real life: The footage
In DevTools: The trace: everything the page did, with its times
In real life: Stepping through it frame by frame
In DevTools: The screenshots DevTools can keep of every frame

A recording is called a trace. It's a long list of events: tasks, requests, frames and more, each with its time. The panel has two record buttons, one for each kind of question:

  • Record and reload is for loading. The docs: "DevTools first navigates to about:blank to clear any remaining screenshots and traces." about:blank is an empty page. Then DevTools reloads your page, and "automatically stops the recording a couple seconds after the load finishes."
  • Record is for everything else. It records whatever you do until you press stop: a click, typing in a box, scrolling.
Two ways to record. Record and reload goes to about:blank, reloads the page, and stops a couple of seconds after the load. Record captures whatever happens between start and stop, such as an interaction.Record and reloadabout:blankreloadloadthen stopsRecordstartinteractyou stop
Record and reload starts from an empty page and stops by itself. Record keeps going until you press stop, so it holds only what you did.

The course made one of each on the mid-tier phone: the store's load, then one click on Add to cart. The table counts the work of the main thread, the head cook from Processes and threads who runs almost all of a page's work. A long task keeps it busy for more than 50 ms. Read each row across:

Two recordings
RecordingWhat it capturesEvents in the traceMain thread
Record and reloadA blank page, then the reload, until a couple of seconds after the load.31,984.454.7 ms, 3 long tasks.
Record, then clickWhatever you do until you stop it: here, one click on Add to cart.4,062.One task of 188.2 ms; the click handler, 114.5 ms of it.

The click recording held about an eighth as many events as the load recording. Record and reload holds the whole load; Record holds only the click. So when your question is about a click, use Record, and the load stays out of your way.

To keep the camera's frames too, tick Screenshots before you record. DevTools then captures "a screenshot of every frame while recording."

One row for each worker

When the recording stops, the panel draws the trace like a timetable, with one row for each worker. Each row is called a track. A track shows what one part of the browser did, and when. Time runs from left to right on every track, so you can read straight down to see what happened at the same moment. Some of the tracks:

  • Network: every request, as a bar from start to finish.
  • Frames: each frame, and how long it took.
  • Main: the main thread's tasks.
  • Interactions: each tap, click or key press, with its three phases.
  • Layout shifts: each time the content moved.

Zooming in on the click

The Main track holds the most detail. Zoomed out, it's a row of bars, one for each task. Zoom in, and a bar opens up into the functions it called, stacked underneath it. It's like zooming into a map, where a stretch of road turns into separate houses. This view is called a flame chart. The docs: "The x-axis represents the recording over time. The y-axis represents the call stack. The events on top cause the events below."

In the click recording, the click ran inside one task of 188.2 ms. Zoom into that task, and the store's click handler takes 114.5 ms of it. Inside that handler, the store calls two functions: buildIndex(), which sorts 200,000 prices, and showTeas(), which draws every tea. The flame chart stacks calls like those under the handler, so you can see which one took longest.

The live view told you where the click's time went. The recording shows you what ran in that time. Reading a performance trace, the next lesson, reads each track and the flame chart in detail.

Saving and loading traces

A good recording is worth keeping. Connor Clark, who wrote DevTools' page on saving them, says a saved trace is useful "to share with other developers or even your future self". It's like copying the camera's footage for a colleague who wasn't in the shop.

  1. Click Download, then Save trace.
  2. Choose what goes in the file. The table below lists the options.
  3. To open a trace, yours or someone else's, click Upload.

Before you save, you can add notes to the trace. You can label one item, connect two items with an arrow, or label a stretch of time. Whoever opens the file sees your notes. Custom tracks, annotations, and sharing covers them in detail.

Read this table across: each row is one save option, whether it starts on, and what it adds to the file.

What a saved trace can hold
OptionStarts on?What it adds
Include annotationsOn, when there are any.Your labels and arrows, for whoever opens the file.
Include resource contentOff.The page's HTML, CSS and JavaScript, so the Sources panel can show them.
Include script source mapsOff.The names from the code you wrote, before it was built and minified. It needs resource content.
Compress with gzipOn, since Chrome 142.A much smaller file to keep and send.

Traces are saved as JSON, a plain text format. Gzip squeezes text into a smaller file. Here is the store's load recording, saved both ways:

One trace, two file sizesPop out in the code editor (opens in a new tab)JavaScript
// The store's load recording, saved two ways, in bytes.const saved = { plain: 6992594, gzipped: 502039 }; console.log(`As plain JSON, the file is ${(saved.plain / 1000000).toFixed(1)} MB.`);console.log(`With gzip, it is ${(saved.gzipped / 1000000).toFixed(1)} MB.`);console.log(`Gzip kept ${Math.round((saved.gzipped / saved.plain) * 100)}% of the bytes.`);
  • Line 2 holds the file's size in bytes, as plain JSON and with gzip.
  • Lines 4 to 6 print As plain JSON, the file is 7.0 MB., With gzip, it is 0.5 MB. and Gzip kept 7% of the bytes.

Seven megabytes, for a few seconds of one small page. Gzip makes it about 14 times smaller, so it's much easier to keep and to send.

Be careful who you send it to. The store's load trace named every address the page loaded, all 5 of them. With resource content, a trace holds your code too, like footage that shows the recipe book on the counter. The docs: "If you consider resource contents to be sensitive information, then you should treat the trace file as sensitive as well."

Live view or recording?

Each question below is one you might bring to the panel. Ask yourself: does a number answer it, or do you need to see what ran? If you need to see what ran, was it during the load, or during something you did?

Live view or recording?
  • Is the store's LCP good on a mid-tier phone?
  • Which element moved when the promotion arrived?
  • What's my INP after clicking around for a minute?
  • Which request holds up the first paint?
  • Which function runs longest while the page starts?
  • Why does Add to cart take 216 ms?
  • Which code runs on every key press in the search box?
Try it yourself
0 of 7 correct

Sort each question by the part of the panel that answers it.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "You have to record to see Core Web Vitals." No. The live view shows LCP and CLS as soon as you open the panel, and INP once you interact.
  • "If it's fast on my computer, it's fast for my users." No. The store's LCP was 112 ms on this computer and 6,200 ms on a calibrated low-tier phone.
  • "4x is the right CPU slowdown for everyone." No. The rate is relative to your computer. This machine needed 11.28x for a low-tier phone and 2.96x for a mid-tier one. Calibrate on yours.
  • "Blocking a slow file always makes LCP sooner." No. Blocking hero.svg moved LCP from 1,312 ms to 1,668 ms, because another element became the largest.
  • "A trace is small and harmless to share." No. The store's was 7.0 MB as JSON, and it named every file the page loaded. With resource content, it holds your code too.
  • "My numbers should match the field data." No. Field data is 28 days of real users, at the 75th percentile. Match your environment first, then compare.

Practice exercises

Answer first, then use the hints.

Exercise 1 · Warm-upAnother round trip

Your users' round trip is now 300 ms. What does the recommendation print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
const presets = { "Fast 4G": 60, "Slow 4G": 150, "3G": 400 };

function recommend(roundTrip) {
  if (roundTrip < 60) return "too fast to simulate with throttling";
  let best = null;
  for (const [name, target] of Object.entries(presets)) {
    const distance = Math.abs(target - roundTrip);
    if (distance <= 200 && (best === null || distance <= best.distance)) best = { name, distance };
  }
  return best ? `similar to ${best.name} throttling` : "no recommendation";
}

console.log(`Round trip 300 ms: ${recommend(300)}`);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upThe click on a low-tier phone

    Here is the same click, on the low-tier phone. What does the last line print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    // The click on Add to cart, on the low-tier phone, in ms.
    const click = { inputDelay: 5.4, processing: 429.7, presentation: 356.9 };
    const total = click.inputDelay + click.processing + click.presentation;
    const share = (ms) => `${Math.round((ms / total) * 100)}%`;
    
    console.log(`The click took ${Math.round(total)} ms.`);
    console.log(`Waiting for the handlers to start: ${share(click.inputDelay)}`);
    console.log(`Running the handlers: ${share(click.processing)}`);
    console.log(`Drawing the next frame: ${share(click.presentation)}`);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeThe first paint on a low-tier phone

      In the playground with three set-ups, when did the store's first paint come on the low-tier phone, in ms?

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeA blocked request

        You block a request in the Request conditions drawer and reload. What does the Network panel's Status column show for it?

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeCalibrating for a mid-tier phone

          This is the recorded search for a mid-tier phone, which scores 1000. What does the first line print?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          // The recorded search for a mid-tier phone's score.
          const target = 1000;
          const tries = [
            { slowdown: 2.98, score: 895 },
            { slowdown: 2.61, score: 1159 },
            { slowdown: 2.79, score: 1078 },
            { slowdown: 2.88, score: 1038.5 },
            { slowdown: 2.93, score: 1034.5 },
            { slowdown: 2.96, score: 1008.5 },
          ];
          
          for (const { slowdown, score } of tries) {
            const verdict = Math.abs(target - score) < 10 ? "close enough" : score > target ? "still too fast" : "too slow";
            console.log(`${slowdown}x slower scored ${score}: ${verdict}.`);
          }

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeTwo rules, one request

            A request matches two patterns in the Request conditions drawer. One blocks it, and one throttles it. Which rule does DevTools apply?

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              For each question, ask: does the live view answer it, do you need field data, or do you need a recording?

              The Performance panel quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What does the recommendation print for a 700 ms round trip?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                const presets = { "Fast 4G": 60, "Slow 4G": 150, "3G": 400 };
                
                function recommend(roundTrip) {
                  if (roundTrip < 60) return "too fast to simulate with throttling";
                  let best = null;
                  for (const [name, target] of Object.entries(presets)) {
                    const distance = Math.abs(target - roundTrip);
                    if (distance <= 200 && (best === null || distance <= best.distance)) best = { name, distance };
                  }
                  return best ? `similar to ${best.name} throttling` : "no recommendation";
                }
                
                console.log(`Round trip 700 ms: ${recommend(700)}`);

                Choose an answer to see the explanation.

              2. Question 2 of 7Where does the panel's field data come from?

                Choose an answer to see the explanation.

              3. Question 3 of 7What does DevTools' CPU calibration measure?

                Choose an answer to see the explanation.

              4. Question 4 of 7What does the blocking example print on its second line?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // The store on the mid-tier phone: its largest element, and when it was painted (LCP).
                const normal = { largest: "img#hero", lcp: 1312 };
                const heroBlocked = { largest: "p", lcp: 1668 };
                
                console.log(`Normally, the largest element was ${normal.largest}, painted at ${normal.lcp} ms.`);
                console.log(`With hero.svg blocked, it was a ${heroBlocked.largest} element, painted at ${heroBlocked.lcp} ms.`);

                Choose an answer to see the explanation.

              5. Question 5 of 7In the recordings, why did slowing only app.js hold back the store's first paint?

                Choose an answer to see the explanation.

              6. Question 6 of 7Typing in a search box feels slow. What do you record?

                Choose an answer to see the explanation.

              7. Question 7 of 7What does the Main track's flame chart show?

                Choose an answer to see the explanation.

              Key takeaways

              • The Performance panel measures as soon as you open it. Its live view shows LCP, CLS and INP, with no recording.
              • The Interactions tab splits each interaction into input delay, processing and presentation delay.
              • Field data from CrUX puts real users beside you: 28 days of visits, at the 75th percentile.
              • CPU throttling is relative to your computer. Calibrate it, and pick the network preset your users' round trips suggest.
              • The Request conditions drawer blocks or slows one file, so you can see what that file costs.
              • Record and reload for loading questions. Record, then interact, for anything you do on the page.
              • A trace has one track for each worker. Zoom into a task to see the functions it ran.
              • Saved traces are big and can hold your code, so share them with care.

              Remember the one-liner.
              Read the report card first, match your users, then press record with one question in mind.

              Sources

              The facts on this page come from these docs, articles and source files:

              Coming next: Reading a performance trace. You'll read a recording track by track, and follow a slow task down the flame chart to the function that caused it.

              CompleteFrontend Clear concepts. Working examples.