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.
- 01Read the live viewRead LCP, CLS and INP before you record anything, and split a slow click into its three phases.
- 02Match your usersUse field data, calibrated CPU throttling and a network preset, so your computer behaves like your users' phones.
- 03Test one file at a timeBlock or slow a single request, and measure what that one file costs the page.
- 04Record 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 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.
lowTierPhoneis 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.andThat'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:
- Live view. Read the numbers the panel shows while you use the page.
- Field data. Put your real users' numbers beside yours.
- Environment. Slow your computer down until it behaves like your users' phones.
- Record. Capture everything the page does while the problem happens.
- Save. Keep the recording, and send it to whoever needs it.
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 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, 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%andDrawing 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:
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 });None yet.
Checking what your browser can report…
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.
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:
- Under Field data, click Set up.
- 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:
// 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.and3G 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:
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.
| Preset | Round trip it imitates | Delay it adds to each request | Download speed |
|---|---|---|---|
| Fast 4G | 60 ms. | 165 ms. | 8.1 Mbps. |
| Slow 4G | 150 ms. | 562.5 ms. | 1.44 Mbps. |
| 3G | 400 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:
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.
// 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:
// 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., then13.33x slower scored 218.5: too slow.and11.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:
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:
// 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.andOn 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:
1,312 ms0.1369216 ms700 msimg#hero1 ms105 ms110 msMid-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.
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?
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:
- In the Network panel, right-click a request.
- Choose Block request or Throttle request.
- 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. - Reload the page, and measure again.
The course did both on the mid-tier phone. First, it blocked the main picture, hero.svg:
// 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.andWith 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:
// 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.andWith 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.
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.
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:
| Recording | What it captures | Events in the trace | Main thread |
|---|---|---|---|
| Record and reload | A blank page, then the reload, until a couple of seconds after the load. | 31,984. | 454.7 ms, 3 long tasks. |
| Record, then click | Whatever 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.
- Click Download, then Save trace.
- Choose what goes in the file. The table below lists the options.
- 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.
| Option | Starts on? | What it adds |
|---|---|---|
| Include annotations | On, when there are any. | Your labels and arrows, for whoever opens the file. |
| Include resource content | Off. | The page's HTML, CSS and JavaScript, so the Sources panel can show them. |
| Include script source maps | Off. | The names from the code you wrote, before it was built and minified. It needs resource content. |
| Compress with gzip | On, 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:
// 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.andGzip 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?
- 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?
Sort each question by the part of the panel that answers it.
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.
Your users' round trip is now 300 ms. What does the recommendation print?
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)}`);Round trip 300 ms: similar to 3G throttling. 3G's 400 ms is 100 ms away: the closest of the three, and within 200 ms.
Here is the same click, on the low-tier phone. What does the last line print?
// 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)}`);Drawing the next frame: 45%. On the low-tier phone, running the handlers took the bigger share, 54%.
In the playground with three set-ups, when did the store's first paint come on the low-tier phone, in ms?
3,056 ms. That's more than four times the mid-tier phone's 700 ms.
You block a request in the Request conditions drawer and reload. What does the Network panel's Status column show for it?
(blocked:devtools), in red. That's how you tell a request DevTools blocked from one that failed by itself.
This is the recorded search for a mid-tier phone, which scores 1000. What does the first line print?
// 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}.`);
}2.98x slower scored 895: too slow. A score below the target means the slowdown was too much, so the next try goes faster: 2.61x.
A request matches two patterns in the Request conditions drawer. One blocks it, and one throttles it. Which rule does DevTools apply?
The first one in the list. The docs: "If a request matches multiple patterns, DevTools applies the first rule found." Move a rule up to make it win.
Check your understanding
For each question, ask: does the live view answer it, do you need field data, or do you need a recording?
Question 1 of 7What does the recommendation print for a 700 ms round trip?
Read the code, then predictconst 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.
Question 2 of 7Where does the panel's field data come from?
Choose an answer to see the explanation.
Question 3 of 7What does DevTools' CPU calibration measure?
Choose an answer to see the explanation.
Question 4 of 7What does the blocking example print on its second line?
Read the code, then predict// 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.
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.
Question 6 of 7Typing in a search box feels slow. What do you record?
Choose an answer to see the explanation.
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:
- Performance panel: Analyze your website's performance, Dale St. Marthe and Sofia Emelianova, Chrome DevTools.
- Performance features reference, Kayce Basques and Sofia Emelianova, Chrome DevTools.
- Web Vitals, web.dev.
- Defining the Core Web Vitals metrics thresholds, web.dev.
- Interaction to Next Paint (INP), web.dev.
- Monitor your local and real-user Core Web Vitals performance in DevTools, Rick Viscomi, Chrome for Developers.
- LiveMetricsView.ts, devtools-frontend source.
- web-vitals-injected.ts, devtools-frontend source.
- PerformanceObserver: observe() method, MDN.
- CrUX methodology, Chrome UX Report docs, Chrome for Developers.
- NetworkManager.ts, devtools-frontend source.
- Throttling, Lighthouse docs.
- Throttling, Sofia Emelianova, Chrome DevTools.
- CalibrationController.ts, devtools-frontend source.
- Request conditions, Ewa Gasperowicz, Chrome DevTools.
- Save performance traces, Connor Clark, Chrome DevTools.
- Annotate and share your performance findings, Sofia Emelianova and Barry Pollard, Chrome DevTools.
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.