completefrontend
Open lab My progress
THE WEB PERFORMANCE FIELD GUIDE

Throttling and simulation

How Lighthouse makes a fast laptop stand in for a mid-range phone: simulated, applied and no throttling, the CPU benchmark and slowdown, and why the same page scores differently on different machines. Recorded with Lighthouse 13.6.0.

By the end, you can
  • 01
    Tell the three methods apartSay what simulated, applied and provided throttling do to the page load, and where each one's slow numbers come from.
  • 02
    Read a simulated reportExplain why the trace and the report disagree, and how Lantern works out a slow phone's numbers from a fast load.
  • 03
    Compare results fairlyRead the benchmark, pick a CPU multiplier for your machine, and explain why PageSpeed Insights and your laptop disagree.

Three ways to slow a page down

In How Lighthouse works, Lighthouse tested the Leaf and Kettle store on a fast laptop. Yet its numbers were for a slow phone. The course ran it on the store again, checking Performance only. The report said the store's LCP, Largest Contentful Paint, was 2,694.7 ms. LCP is the moment the biggest image or block of text appeared.

Then the course opened the trace from that same run. A trace is the full recording of a page load, the kind you read in Reading a performance trace. In the trace, LCP happened at 785 ms.

One run, two LCPsPop out in the code editor (opens in a new tab)JavaScript
// One Lighthouse run on the store, Performance only.// Both numbers are the store's LCP from that run, in milliseconds (ms).const lcp = { inTheTrace: 785, inTheReport: 2694.7 }; console.log(`The trace shows LCP at ${lcp.inTheTrace} ms.`);console.log(`The report says LCP was ${lcp.inTheReport} ms.`);
  • Lines 1 and 2 are comments. Both numbers come from one Lighthouse run, in milliseconds (ms).
  • Line 3 holds them: LCP as the trace saw it, and LCP as the report gave it.
  • Lines 5 and 6 print The trace shows LCP at 785 ms. and The report says LCP was 2694.7 ms.

Same run, same page, and the report's LCP is more than three times later. Which one is right? Both are. They answer two different questions:

  • The trace says what really happened, on this fast laptop.
  • The report says how long the same load would take on a mid-range phone, on a slow network.

Lighthouse never loaded the store slowly. It loaded it at full speed, then worked out the phone's numbers. Making a fast computer behave like a slower device is called throttling: slowing something down on purpose. You did it yourself in The Performance panel, with DevTools' CPU and network menus.

Lighthouse has three ways to deal with the slow phone. It calls them throttling methods:

  • simulate is the default. Lighthouse loads the page at full speed, then calculates the phone's numbers. That's where 785 and 2,694.7 came from.
  • devtools really slows the network and the CPU down while the page loads. This lesson calls it applied throttling.
  • provided slows nothing down. The report calls it "Provided by environment": any slowness comes from your own setup, like a real phone.

The course ran every method on the store. Here is the store's LCP four ways: the real load and the estimate from one simulated run, then the other two methods.

The store's LCP four ways: the real load, in the trace, 785 ms; simulate: Lantern's estimate, 2,694.7 ms; devtools: really slowed down, 2,759.3 ms; provided: not slowed down, 748.8 ms.the real load, in the trace785 mssimulate: Lantern's estimate2,694.7 msdevtools: really slowed down2,759.3 msprovided: not slowed down748.8 ms
The top two bars are one simulated run: what Chrome saw, and what Lantern worked out from it. The shaded bars are the slow phone's numbers. The bottom two are the middle of five runs each.

Look at the second and third bars. One was worked out, and one was timed on a load that was really slowed down. Yet they landed close together, at 2,694.7 and 2,759.3 ms. The default method works like a satnav:

Real-life analogyA satnav times the rush-hour drive

On Sunday morning, you drive to the tea shop on empty roads, and your satnav saves every turn. Then you ask it how long the same drive takes in Monday's rush hour. It doesn't send you out again. It works out each stretch at rush-hour speed, keeps them in order, and adds them up.

In real life: The Sunday drive on empty roads
In Lighthouse: The real load, at the laptop's full speed
In real life: Every turn and stretch the satnav saved
In Lighthouse: The trace, and the log of every request
In real life: Rush-hour speeds
In Lighthouse: A slow phone on a slow network
In real life: The rush-hour time the satnav works out
In Lighthouse: The metrics in the report, like LCP
The main idea

Throttling makes a fast computer stand in for a slow phone. Lighthouse's default doesn't slow the page down: it loads it fast, then calculates. So the trace shows the fast load, and the report shows the estimate. The other two methods really slow the load down, or leave the slowing to you.

The course ran each method five times, taking turns. Pick a method to see every run:

Playground: three throttling methods
Simulatedrecorded
90LCP 2,694.7 msobserved 785 msTBT 45 msCLS 0.136run 6.7 s
90LCP 2,688.9 msobserved 750 msTBT 42.5 msCLS 0.136run 6.2 s
89LCP 2,836.9 msobserved 769 msTBT 84 msCLS 0.136run 6.2 s
90LCP 2,688.4 msobserved 761 msTBT 42 msCLS 0.136run 6.2 s
90LCP 2,685.8 msobserved 740 msTBT 41.5 msCLS 0.136run 6.2 s
Try it yourself
Simulated

Chrome loaded the store at full speed, and LCP came at 761 ms. Lantern then worked out a slow phone's LCP from that load: 2,688.9 ms. The median score was 90. Each run took 6.2 s, and the load with its waits took 3.1 s of it.

Recorded with Lighthouse 13.6.0 from Node, Performance only, on the store from Performance insights. Five runs of each method, taking turns. Each row is one run, with its score on the left.

Before you trust a number from Lighthouse, check which method made it. The next two sections look at the methods one at a time.

Simulated throttling and the Lantern model

Start with the default, since it made the two LCPs. The docs say simulated throttling "uses a simulation of a page load, based on the data observed in the initial unthrottled load." Here is what that means, in four steps:

  1. Load the page at full speed. Lighthouse even turns off any network throttling first. Its source does that for every method except devtools.
  2. Keep the notes. It saves the trace, and a log of every request: when it started, what started it, and how big it was.
  3. Draw a map of the load. A model called Lantern turns the notes into a map of the load's steps. The map shows which step waited for which.
  4. Time it again, slowly. Lantern replays the map on an imaginary slow phone, and reads the metrics off the new timeline.

Lighthouse's docs name the model in one line: "Lantern is how Lighthouse simulates network and cpu throttling." Another of its docs says simulated throttling works "by replaying network activity on its own."

A map of steps and waits

Lantern's map is a dependency graph. Each step of the load is a node, and there are two kinds:

  • A network node is one request, like downloading app.js.
  • A CPU node is one task on the main thread, like running app.js.

An arrow between two nodes means the second had to wait for the first. Here are the steps the store's first paint waited for, drawn that way:

What the store's first paint waited for. The page, index.html, led to three requests: styles.css, fonts.css and app.js. Then app.js had to run, a task on the main thread. The first paint waited for both stylesheets and for app.js to run.index.htmlthe pagerequestsstyles.cssfonts.cssapp.jsa CPU taskrun app.jsfirst paint
An arrow goes from a step to the step that had to wait for it. Lantern keeps this order from the real load, then times each step again for the slow phone.

The page has to arrive before the browser can ask for its stylesheets and its script. app.js has to arrive before it can run. The first paint needs both stylesheets and the finished script. Lantern finds this order in the real load. Then it times every step again:

Real-life analogyTiming a recipe from your notes

Last night you cooked dal on a big gas stove, and noted how long each step took. A friend with a camping stove, four times weaker, asks how long it'll take them. You don't cook it again. You keep the order of the steps, make each cooking step four times longer, and add them up.

In real life: Each step in your notes, with its time
In Lighthouse: A node: one request, or one task on the main thread
In real life: No frying the onions before they're chopped
In Lighthouse: An arrow: app.js has to arrive before it can run
In real life: A stove four times weaker
In Lighthouse: The CPU slowdown: each task takes 4 times as long
In real life: Your total, worked out at the table
In Lighthouse: Lantern's estimate, with no second load

Lantern's source times the two kinds of node in different ways:

  • A CPU node takes as long as it did in the real load, times the CPU slowdown: 4 by default. Tasks that lay out the page get half that slowdown.
  • A network node is timed on the slow network. Every round trip, a message to the server and back, takes 150 ms, including the ones that open a connection. The download runs at 1.6 megabits a second.
  • To each request, it also adds how long the server took to answer, which it estimates from the real load.

So the estimate keeps what really made the store slow. Its server held the page back for 700 ms in every load, fast or slow. A slow server stays slow in the estimate, and a big file gets slower.

Each metric stretches differently

The same run gave three metrics, both ways. FCP, First Contentful Paint, is when the first text or image appeared. SI, Speed Index, is how quickly the page's content showed up on the screen.

Three metrics, observed and reportedPop out in the code editor (opens in a new tab)JavaScript
// The same run: three metrics, observed in the real load and reported, in ms.const observed = { FCP: 785, LCP: 785, SI: 1125 };const reported = { FCP: 1944.7, LCP: 2694.7, SI: 2353.4 }; for (const metric of Object.keys(observed)) {  const times = (reported[metric] / observed[metric]).toFixed(1);  console.log(`${metric}: observed ${observed[metric]} ms, reported ${reported[metric]} ms, ${times} times later`);}
  • Line 2 holds what Lighthouse observed: its word for what Chrome saw in the real load.
  • Line 3 holds what the report gave: Lantern's estimates.
  • Line 6 divides one by the other, to see how many times later the estimate is. Line 7 prints one line per metric.
  • It prints FCP: observed 785 ms, reported 1944.7 ms, 2.5 times later, then LCP: observed 785 ms, reported 2694.7 ms, 3.4 times later. The last line is SI: observed 1125 ms, reported 2353.4 ms, 2.1 times later.

So Lantern doesn't multiply the whole page by one number. Look at FCP and LCP. In the real load, they happened together, at 785 ms. In the estimate, LCP comes 750 ms after FCP.

That's because each metric replays its own part of the graph. For FCP, Lantern keeps only what the first paint needed, like the picture above. For LCP, it keeps every request that finished before LCP, except low-priority images. That's a bigger part of the graph, so LCP can stretch further.

One metric isn't estimated at all: CLS, Cumulative Layout Shift, which measures how much things moved around on the screen. Lantern has no model for it, so Lighthouse reads it from the real load. The simulated runs gave a CLS of 0.1364, the same as the provided runs, which slowed nothing down.

Now the two LCPs from the start make sense. The trace is the Sunday drive. The report is the rush-hour sum. The docs warn about exactly this: the trace values "will not match up with Lighthouse's metric results, as the original trace is prior to the simulation." In DevTools, the report's menu even names it: View Unthrottled Trace.

Why it's the default

Simulation is quick and steady. The docs call it "both very fast and deterministic": the same notes always give the same estimate. On accuracy, they say it's "roughly as accurate or better than DevTools throttling for most sites", but it "suffers from edge cases".

Why it matters: when a simulated report looks odd, open the trace to see what happened, and in what order. Then read the report for how slow that order would be on a phone.

Applied throttling

Sometimes you want the slow load itself, not a sum. Maybe you want to watch the long tasks happen in the trace. That's what devtools throttling gives you. The Lighthouse panel calls it DevTools throttling. This lesson calls it applied throttling, because the slowdown is really applied while the page loads.

Real-life analogyThe driving instructor's second brake

A learner drives to the tea shop with an instructor beside her. The instructor's side of the car has a second brake pedal, and the instructor keeps pressing it. The car really goes slower, the trip really takes longer, and the dashcam films the slow trip.

In real life: The instructor's brake pedal
In Lighthouse: The throttling Lighthouse turns on in Chrome
In real life: The car really going slower
In Lighthouse: The page really loading slowly
In real life: The dashcam film of the slow trip
In Lighthouse: The trace, which now matches the report

Lighthouse presses two brakes, the same two kinds of throttling you used in The Performance panel:

  • The network. Chrome adds a delay to every request, and caps the download speed.
  • The CPU. Chrome slows the page's work down 4 times. The docs say this "actually interrupts execution of CPU work at periodic intervals to emulate a slower processor."

The network delay needs a bigger number than the phone's 150 ms round trip. As The Performance panel explained, DevTools delays each whole request. A real phone network delays each small packet of data. The docs put it plainly: "this throttling isn't highly accurate."

To make up for it, Lighthouse multiplies the round trip by 3.75, so every request waits 562.5 ms. It also multiplies the download speed by 0.9. Choosing test conditions compares the two kinds of network slowing.

Because Chrome measures the slow load itself, the report says what Chrome saw. Compare one run of each kind:

Seen and reported, two waysPop out in the code editor (opens in a new tab)JavaScript
// LCP in ms from two runs: what Chrome saw in the load, and what the report said.const simulated = { saw: 785, reported: 2694.7 };const applied = { saw: 2760, reported: 2760.4 }; console.log(`Simulated: Chrome saw ${simulated.saw} ms, and the report said ${simulated.reported} ms.`);console.log(`Applied: Chrome saw ${applied.saw} ms, and the report said ${applied.reported} ms.`);
  • Line 1 is a comment. Both runs loaded the store, and both numbers are LCP in ms.
  • Line 2 is the first simulated run, the one from the start. Line 3 is the first run with applied throttling.
  • Lines 5 and 6 print Simulated: Chrome saw 785 ms, and the report said 2694.7 ms. and Applied: Chrome saw 2760 ms, and the report said 2760.4 ms.

With applied throttling, the two numbers agree. The extra 0.4 is only rounding: Lighthouse rounds what it observed to whole milliseconds. The docs promise the same for the trace: with DevTools throttling, its numbers "will match" the report's. Why it matters: when you need a trace that lines up with the report, use applied throttling.

Slower runs

Applied throttling costs time. Here is how long each whole run took, with each method:

How long a run takesPop out in the code editor (opens in a new tab)JavaScript
// How long each whole run took, in ms. Five runs of each method, taking turns.const runs = {  simulate: [6667.3, 6237.8, 6175.5, 6224.1, 6177.1],  devtools: [17666.7, 17630.9, 17668.5, 17695.2, 17729.6],  provided: [14807.7, 14751.4, 14764, 15395.1, 14739.8],};const median = (list) => [...list].sort((a, b) => a - b)[Math.floor(list.length / 2)]; for (const [method, times] of Object.entries(runs)) {  console.log(`${method}: ${(median(times) / 1000).toFixed(1)} seconds a run`);}
  • Lines 3 to 5 are recorded: five runs of each method, taking turns.
  • Line 7 sorts a list and takes the middle value, the median.
  • Line 10 prints simulate: 6.2 seconds a run, devtools: 17.7 seconds a run and provided: 14.8 seconds a run.

The DevTools runs took almost three times as long as the simulated ones. Some of that is the slow load. But look at provided. It slowed nothing down, and still took 14.8 seconds. Most of the extra time is waiting.

Remember the photographer in How Lighthouse works, who waits until everyone keeps still? With simulate, Lighthouse waits for 1,000 ms of quiet. With the other two methods, every wait grows to 5,250 ms. That's after the first paint, after the load event, for a quiet network, and for a quiet CPU.

Its source does this when the method "requires observation": when the numbers come from watching the load itself. So it watches for longer before it stops.

One more number changed. CLS was 0.1623 with applied throttling, and 0.1364 with the other two. CLS always comes from the real load. In a slower real load, things can arrive at different moments, so the page can shift differently.

Provided: no slowing at all

The third method, provided, adds no throttling. On this laptop, the store loaded at full speed. Those are the laptop's numbers, not a phone's.

So what is it for? For when the slowness comes from somewhere else. It's like driving the route as it is today, with no sums and no second brake. The main case is a real phone. Lighthouse can test a phone plugged in over USB. Its docs suggest these flags "to disable any additional emulation":

--screenEmulation.disabled --throttling.cpuSlowdownMultiplier=1 --throttling-method=provided

The phone is already slow, so Lighthouse shouldn't slow it down again. For the most realistic network, the docs point to packet-level tools, which slow every packet your computer sends. Exercise 6 asks you to find the one they name.

Here are all three methods side by side, the median of five runs each:

Same page, three scoresPop out in the code editor (opens in a new tab)JavaScript
// The same five runs of each method: the score, and LCP in ms.const runs = {  simulate: { score: [90, 90, 89, 90, 90], lcp: [2694.7, 2688.9, 2836.9, 2688.4, 2685.8] },  devtools: { score: [83, 81, 81, 81, 81], lcp: [2760.4, 2745.8, 2759.3, 2758.9, 2787.2] },  provided: { score: [95, 95, 95, 95, 95], lcp: [753.7, 745.4, 746.7, 798.9, 748.8] },};const median = (list) => [...list].sort((a, b) => a - b)[Math.floor(list.length / 2)]; for (const [method, { score, lcp }] of Object.entries(runs)) {  console.log(`${method}: score ${median(score)}, LCP ${median(lcp)} ms`);}
  • Lines 3 to 5 are recorded: each run's score, and its LCP.
  • Line 10 prints the medians: simulate: score 90, LCP 2688.9 ms, devtools: score 81, LCP 2759.3 ms and provided: score 95, LCP 748.8 ms.

Same page, three scores. The two ways of imitating the phone gave LCPs about 70 ms apart. Yet applied throttling scored 81, not 90. The score weighs five metrics, as The Lighthouse score showed, and the other four moved more than LCP did. Exercise 3 asks you to find one of them in the playground.

Read this table across. Each row is one method: what it does to the load, where its slow numbers come from, and whether its trace matches its report.

The three throttling methods
MethodWhat happens to the load?Where do the slow numbers come from?Does the trace match the report?
Simulated (simulate)Chrome loads the page at full speed.Lantern works them out from the fast load.No. The trace is the fast load.
Applied (devtools)Chrome really slows the network and the CPU.Chrome measures them in the slowed load.Yes. The trace is the slowed load.
Provided (provided)Lighthouse slows nothing down.From your own setup, like a real phone, or nowhere.Yes. The trace is the load as it happened.

CPU benchmarking and slowdown

Both methods that imitate the phone use the same number for the CPU: 4. The phone's CPU is 4 times slower. But 4 times slower than what? The docs answer: CPU throttling "is expressed relative to the performance of the host device." The host is the computer running Lighthouse.

You met this problem in The Performance panel, walking at Ravi's pace. "Five times slower" means something different for every walker. DevTools solves it by calibrating: it tries slowdowns until a benchmark scores like a phone. Lighthouse doesn't calibrate. By default, its docs say, it uses "a constant 4x CPU multiplier".

It does measure the host, though. In every run, before the page loads, Lighthouse runs a benchmark: a small test program that counts how much work the CPU gets done. Lighthouse saves the result as benchmarkIndex. The report shows it near the bottom, as "Unthrottled CPU/Memory Power". This laptop's median was 3,009.5.

The docs give rough ranges for a few kinds of device. Two of them matter here:

  • A high-end desktop, like a 16-inch MacBook Pro, scores 1,500 to 2,000.
  • A mid-tier phone, like a Moto G4, scores 125 to 800.

The docs call each range a bracket. The default 4x is meant for the first one. In the docs' words, it "moves a typical run in the high-end desktop bracket somewhere into the mid-tier mobile bracket."

What the multiplier changes

The course ran the simulation on this laptop with four multipliers, three runs each:

Four multipliersPop out in the code editor (opens in a new tab)JavaScript
// The simulation with each CPU slowdown multiplier, three runs each: TBT in ms, and the score.const runs = {  1: { tbt: [0, 0, 0], score: [91, 91, 90] },  2: { tbt: [16, 8.5, 8], score: [90, 90, 90] },  4: { tbt: [83, 83, 41.5], score: [89, 89, 90] },  8: { tbt: [225, 216, 215], score: [84, 84, 84] },};const median = (list) => [...list].sort((a, b) => a - b)[Math.floor(list.length / 2)]; for (const [multiplier, { tbt, score }] of Object.entries(runs)) {  console.log(`${multiplier}x: TBT ${median(tbt)} ms, score ${median(score)}`);}
  • Lines 3 to 6 are recorded: the TBT and the score of each run, for each multiplier.
  • Line 8 takes the median of three.
  • Line 11 prints 1x: TBT 0 ms, score 91, 2x: TBT 8.5 ms, score 90, 4x: TBT 83 ms, score 89 and 8x: TBT 216 ms, score 84.

TBT, Total Blocking Time, adds up how far each long task ran past 50 ms, after the first paint. At 1x, no task was long enough to block, so TBT was 0. Each step up stretched the tasks in the graph further past 50 ms. At 8x, TBT reached 216 ms, and the score fell to 84.

Why it matters: the multiplier decides how bad a busy main thread looks. Pick it to match the device you care about. Try each multiplier here, and the slower machine from the next section:

Playground: CPU slowdown
4xrecorded
89benchmark 3,023TBT 83 msLCP 2,837 ms
89benchmark 3,029.5TBT 83 msLCP 2,836.2 ms
90benchmark 3,035TBT 41.5 msLCP 2,685.7 ms
Try it yourself
4x

The simulation on this laptop, benchmark 3,029.5, with the CPU 4 times slower. Median TBT 83 ms, LCP 2,836.2 ms, and score 89.

Recorded with Lighthouse 13.6.0, simulated throttling, three runs each. The slower machine is this laptop with its tab slowed down 4 times by DevTools' CPU throttling before each run.

To set the multiplier yourself, use the flag --throttling.cpuSlowdownMultiplier. Lighthouse's help says it "Controls simulated + emulated CPU throttling". So it works for both simulate and devtools.

Comparing results across machines

Here's a puzzle teams run into. Two people test the same page with Lighthouse, on two laptops, and get two different scores. Nobody did anything wrong.

The reason is in the last section: the 4x goes on top of whatever the laptop can do. On a slower laptop, every task in the real load already takes longer. Lantern then stretches it 4 times more.

Real-life analogyOne pace rule for the whole team

A running coach tells the whole team: run at a quarter of your top speed, to feel what a beginner feels. For the fastest runner, that works. A runner who's already slow ends up barely moving, far slower than any beginner.

In real life: "Run at a quarter of your top speed"
In Lighthouse: The default 4x CPU slowdown
In real life: The fastest runner
In Lighthouse: A fast laptop, like this one
In real life: A slow runner, barely moving
In Lighthouse: A slower laptop, slowed down 4 more times
In real life: "You, just run at your usual pace"
In Lighthouse: Calibrating: a multiplier of 1

The course didn't have a slow laptop, so it made one. Before Lighthouse started, it slowed the tab down 4 times with DevTools' CPU throttling. So Lighthouse saw what looked like a slower computer: even its benchmark ran slower. Then the course ran Lighthouse with the default 4x, and again with 1x:

Two machines, two multipliersPop out in the code editor (opens in a new tab)JavaScript
// This machine, and a slower one: the tab slowed down 4 times. Benchmark indexes, and each run's score.const machines = {  "this machine, 4x": { benchmark: [2992.5, 3013, 3009.5, 3027, 3006.5], score: [90, 90, 89, 90, 90] },  "slower machine, 4x": { benchmark: [680.5, 683, 682], score: [74, 72, 72] },  "slower machine, 1x": { benchmark: [683.5, 682.5, 687.5], score: [89, 89, 89] },};const median = (list) => [...list].sort((a, b) => a - b)[Math.floor(list.length / 2)]; for (const [machine, { benchmark, score }] of Object.entries(machines)) {  console.log(`${machine}: benchmark ${median(benchmark)}, score ${median(score)}`);}
  • Line 3 is this laptop: its five default runs.
  • Line 4 is the slower machine with the default 4x. Line 5 is the same machine with the multiplier set to 1.
  • Line 10 prints this machine, 4x: benchmark 3009.5, score 90, then slower machine, 4x: benchmark 682, score 72. The last line is slower machine, 1x: benchmark 683.5, score 89.

Same page. With the default 4x, the slower machine scored 72 instead of 90. Set to 1x, it scored 89, one point from this laptop. The benchmark barely moved between lines 4 and 5: the multiplier doesn't change the machine.

Benchmark index, from 0 to 3,200. The docs put a mid-tier phone at 125 to 800 and a high-end desktop at 1,500 to 2,000; the command line warns at 1,000 or less. This machine measured 3009.5; the slower stand-in, 682.mid-tier phone, 125 to 800high-end desktop, 1,500 to 2,000warning at 1,000 or lessslower stand-in682this machine3,009.5
The default 4x is meant for a machine in the high-end desktop range. The slower stand-in is already in the mid-tier phone range, so it needs a smaller multiplier.

Where did 1x come from? The docs have a second table, for picking a multiplier. Find the kind of machine you test on, then the kind of device you want to imitate. A machine in the mid-tier phone range, testing for a mid-tier phone, gets 1x. It's already as slow as the target. The stand-in's 682 is in that range.

The docs add a rule for the ends of each range: "If your device's BenchmarkIndex falls on the higher end of its bracket, use a higher multiplier." Near the lower end, use a lower one.

Lighthouse can warn you, too. Run it from the command line with the default multiplier, on a benchmark of 1,000 or less. The report then gets a warning: "The tested device appears to have a slower CPU than Lighthouse expects. This can negatively affect your performance score." The course ran Lighthouse from Node, which doesn't add the warning. So its reports had none, even on the stand-in.

Why it matters: before you compare two scores, compare the two benchmarks. If they're far apart, the gap in scores may come from the machines, not the page.

A lab test imitates one device, but your real visitors bring their own. Two browser APIs give a page a rough idea of the device it's on:

  • navigator.hardwareConcurrency is the number of logical processors: roughly, how many things the CPU can work on at once.
  • navigator.connection.effectiveType is a rough label for the connection, like "4g". Not every browser has it.

Why PageSpeed Insights disagrees with your laptop

PageSpeed Insights runs Lighthouse with the same method. The docs say simulated throttling "matches the setup of PageSpeed Insights and the Lighthouse CLI default." Same Lantern, same slow phone. So why can its score differ from your laptop's?

Go back to the satnav. Two satnavs can do the same rush-hour sum and still disagree, if each one saved a different Sunday drive. PageSpeed Insights starts from a different real load, in three ways:

  • A different computer. It runs on Google's machines, so its benchmark and its real load aren't yours.
  • A different road to your server. Its docs say "PageSpeed also runs in a Google datacenter that can vary based on network conditions." It reports running in North America, Europe or Asia. Lantern keeps some numbers from the real load, like how long each server took to answer. From a different place, those come out different.
  • A different page. It loads the public address. It can't see a build on your laptop, or a change you haven't put online yet.

So before you compare two reports, compare where they ran. A Lighthouse report lists it near the bottom, under these labels. The PageSpeed Insights docs point to the same place to see where its test ran.

  • Unthrottled CPU/Memory Power: the benchmark.
  • CPU throttling: the multiplier and the method, like "4x slowdown (Simulated)".
  • Network throttling: the round trip and the download speed it imitated.

Why it matters: if the benchmarks or the places are far apart, a different score says more about the machines than about your page. And remember the forecast from How Lighthouse works: PageSpeed Insights' field data is another thing again, measured on real visits.

Which method?

Each job below needs one of the three methods. For each one, ask: do you want a quick estimate, a load that's really slow, or no slowing from Lighthouse at all?

Which method?
  • Quick, repeatable checks on every pull request
  • Matching PageSpeed Insights' setup
  • A trace whose numbers match the report
  • Watching a really slowed-down load's long tasks
  • A real phone over USB
  • This laptop's own full speed, with nothing slowed
Try it yourself
0 of 6 correct

Sort each job by the throttling method that suits it.

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

Common mix-ups

  • "Lighthouse loads my page on a slow network." No. By default, it loads it at full speed. Then Lantern calculates the slow phone's numbers.
  • "The trace shows what the report measured." No. With the default method, the trace is the fast load. The report's metrics are Lantern's estimate.
  • "4x slower means the same thing on every machine." No. It's 4 times slower than the machine running Lighthouse. A slower machine scored 72 instead of 90.
  • "DevTools throttling is the accurate one." No. It delays whole requests, not packets, and the docs call it not highly accurate. They say simulation is roughly as accurate or better for most sites.
  • "Provided imitates a phone in some other way." No. It slows nothing down. On a laptop, you get the laptop's own numbers: the store scored 95.
  • "PageSpeed Insights should match my laptop." No. It runs the same simulation, but from a real load on Google's machine, in a Google datacenter.

Practice exercises

Try each one before you open the hints.

Exercise 1 · Warm-upA later LCP

This is the three-metrics example, with the numbers from the third simulated run. What does the second line print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
// The same run: three metrics, observed in the real load and reported, in ms.
const observed = { FCP: 769, LCP: 769, SI: 1096 };
const reported = { FCP: 1936.9, LCP: 2836.9, SI: 2309.1 };

for (const metric of Object.keys(observed)) {
  const times = (reported[metric] / observed[metric]).toFixed(1);
  console.log(`${metric}: observed ${observed[metric]} ms, reported ${reported[metric]} ms, ${times} times later`);
}

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

    Exercise 2 · Warm-upThis machine at 1x

    This is the machines example with one more row: this laptop, with the multiplier set to 1. What does the last line print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    // This machine, and a slower one: the tab slowed down 4 times. Benchmark indexes, and each run's score.
    const machines = {
      "this machine, 4x": { benchmark: [2992.5, 3013, 3009.5, 3027, 3006.5], score: [90, 90, 89, 90, 90] },
      "slower machine, 4x": { benchmark: [680.5, 683, 682], score: [74, 72, 72] },
      "slower machine, 1x": { benchmark: [683.5, 682.5, 687.5], score: [89, 89, 89] },
      "this machine, 1x": { benchmark: [3010, 3039.5, 3031.5], score: [91, 91, 90] },
    };
    const median = (list) => [...list].sort((a, b) => a - b)[Math.floor(list.length / 2)];
    
    for (const [machine, { benchmark, score }] of Object.entries(machines)) {
      console.log(`${machine}: benchmark ${median(benchmark)}, score ${median(score)}`);
    }

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

      Exercise 3 · PracticeA DevTools run's TBT

      In the first playground, what was the median TBT of the runs with applied throttling?

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

        Exercise 4 · PracticeThe slower machine's TBT

        In the second playground, what was the median TBT on the slower machine with the default 4x?

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

          Exercise 5 · ChallengeA low-end phone

          Your machine's benchmark is in the middle of the high-end desktop range. You want to imitate a low-end phone. Which multiplier does the docs' table suggest?

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

            Exercise 6 · ChallengeA packet-level tool

            The docs name a command line tool for packet-level throttling on Mac and Linux. What is its npm package called?

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

              Check your understanding

              For each question, ask three things: what really loaded, what was worked out, and on which machine.

              Throttling quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What does the first line of the three-metrics example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // The same run: three metrics, observed in the real load and reported, in ms.
                const observed = { FCP: 785, LCP: 785, SI: 1125 };
                const reported = { FCP: 1944.7, LCP: 2694.7, SI: 2353.4 };
                
                for (const metric of Object.keys(observed)) {
                  const times = (reported[metric] / observed[metric]).toFixed(1);
                  console.log(`${metric}: observed ${observed[metric]} ms, reported ${reported[metric]} ms, ${times} times later`);
                }

                Choose an answer to see the explanation.

              2. Question 2 of 7What does the last line of the machines example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // This machine, and a slower one: the tab slowed down 4 times. Benchmark indexes, and each run's score.
                const machines = {
                  "this machine, 4x": { benchmark: [2992.5, 3013, 3009.5, 3027, 3006.5], score: [90, 90, 89, 90, 90] },
                  "slower machine, 4x": { benchmark: [680.5, 683, 682], score: [74, 72, 72] },
                  "slower machine, 1x": { benchmark: [683.5, 682.5, 687.5], score: [89, 89, 89] },
                };
                const median = (list) => [...list].sort((a, b) => a - b)[Math.floor(list.length / 2)];
                
                for (const [machine, { benchmark, score }] of Object.entries(machines)) {
                  console.log(`${machine}: benchmark ${median(benchmark)}, score ${median(score)}`);
                }

                Choose an answer to see the explanation.

              3. Question 3 of 7You open the trace of a simulated Lighthouse run. Its LCP is three times earlier than the report's. Why?

                Choose an answer to see the explanation.

              4. Question 4 of 7In a simulated run, which of these metrics does Lantern not estimate?

                Choose an answer to see the explanation.

              5. Question 5 of 7Why did each DevTools-throttled run take almost three times as long as a simulated one?

                Choose an answer to see the explanation.

              6. Question 6 of 7A teammate's laptop benchmarks 680 and scores 72. Yours benchmarks 3,000 and scores 90 on the same page. What's going on?

                Choose an answer to see the explanation.

              7. Question 7 of 7Why can PageSpeed Insights give a different score from Lighthouse on your laptop?

                Choose an answer to see the explanation.

              Key takeaways

              • Throttling makes a fast computer stand in for a slow phone. Lighthouse has three methods: simulate, devtools and provided.
              • Simulate, the default, loads the page at full speed. Lantern then replays its graph of requests and tasks on a slow phone. It's quick and steady, but the trace shows the fast load.
              • Applied throttling really slows the network and the CPU. The trace matches the report, and each run takes longer.
              • Provided slows nothing down. Use it when the device is already slow, like a real phone.
              • The CPU slowdown is relative to the machine. Check the benchmark, and change the multiplier on a much slower or faster machine.
              • PageSpeed Insights runs the same simulation from a different real load. Compare environments before you compare scores.

              Remember the one-liner.
              Throttling starts from your machine: know which method ran, and on what.

              Sources

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

              Coming next: Reading audits and insights, also in Measuring in the lab. Turn the report into a short, ordered list of fixes, and learn which savings are estimates.

              CompleteFrontend Clear concepts. Working examples.