completefrontend
Open lab My progress
THE WEB PERFORMANCE FIELD GUIDE

How Lighthouse works

What Lighthouse does when it tests a page, step by step, and how its three modes, navigation, timespan, and snapshot, measure different things. Recorded with Lighthouse 13.6.0 on the course's store.

By the end, you can
  • 01
    Run it four waysStart Lighthouse in DevTools, from the command line, from Node, or on PageSpeed Insights, and say whose computer did the work.
  • 02
    Follow one runFollow a run from the blank page to the report, and say what it did to your page and to your server.
  • 03
    Tell gathering from auditingExplain how gatherers collect artifacts and audits judge them, and why the same artifacts always get the same score.
  • 04
    Pick the right modeChoose navigation, timespan, or snapshot for the question you're asking, and put them together in a user flow.

What Lighthouse does

Asha told Leaf and Kettle that its page is slow on her phone. Over the last few lessons, you recorded the store in the Performance panel and found what was likely slowing it down. Before you change anything, you want one number. Something you can check again after each fix, to see if it helped.

Chrome has a tool that gives you one: Lighthouse. The course ran it on the store, with its default settings. Seven seconds later, it gave the store a Performance score of 90 out of 100.

But Asha's phone is still slow. So what exactly scored 90? Which phone was the page tested on? Which network? Did anyone tap Add to cart? This lesson follows that one run from start to finish, to find out.

Lighthouse is an open-source tool that tests web pages. It's built into Chrome's DevTools, and you can also run it on its own. You give it an address. It loads the page in Chrome, runs a long list of checks on what it saw, and writes a report. The report covers more than speed: the store also got scores for Accessibility, Best Practices and SEO.

Here is the whole run at once, drawn to scale:

One Lighthouse run on the store, drawn to scale: getting ready, 1,102.5 ms; loading the page, 3,150.5 ms; collecting the rest, 2,722.8 ms; auditing, 266.6 ms; 7,242.4 ms in all. The first three parts happen in Chrome. Auditing needs only what they collected.in Chromegetting ready1,102.5 msloading the page3,150.5 mscollecting the rest2,722.8 msauditing266.6 msthe whole run: 7,242.4 ms
Each bar is one part of the recorded run, drawn to scale. The page itself is only loading during the shaded bar. Auditing, the thin bar on the right, is the only part that doesn't need Chrome.

The same four parts, as numbers you can add up:

One run, in four partsPop out in the code editor (opens in a new tab)JavaScript
// One Lighthouse run on the store, in four parts.// Each time is in milliseconds (ms), from Lighthouse's own notes.const run = { gettingReady: 1102.5, loading: 3150.5, collecting: 2722.8, auditing: 266.6 }; const total = Object.values(run).reduce((sum, ms) => sum + ms, 0);console.log(`The whole run took ${(total / 1000).toFixed(1)} seconds.`);console.log(`Loading the page took ${Math.round((run.loading / total) * 100)}% of it.`);console.log(`Auditing took ${Math.round((run.auditing / total) * 100)}% of it.`);
  • Lines 1 and 2 are comments. Every time is in milliseconds (ms), thousandths of a second, from Lighthouse's own notes.
  • Line 3 holds the four parts. gettingReady is everything before the page starts loading. collecting is the work Lighthouse does on the page after it has loaded.
  • Line 5 adds the four parts up.
  • Lines 6 to 8 print The whole run took 7.2 seconds., Loading the page took 44% of it. and Auditing took 4% of it.

So the page itself was loading for less than half of the run. The rest went on getting ready, and on studying the page once it had loaded. Auditing is the part that checks what Lighthouse saw and works out the scores. It took 4%. The first three parts need Chrome. The last part doesn't need a browser at all.

Real-life analogyLighthouse is a home inspector's visit

Before you buy a house, you can pay an inspector to look at it. They come once, on a day you agree. They test every house the same way: run every tap, switch on every light. They bring the same checklist every time. Then they write a report. It tells you how the house did on that day. It can't tell you what living there is like all year.

In real life: The house
In Lighthouse: Your page
In real life: One visit, on a day you agree
In Lighthouse: One load of the page, at one time
In real life: Testing every house the same way
In Lighthouse: Fixed settings: a phone-sized screen, an empty cache, a set phone and network speed
In real life: The same checklist for every house
In Lighthouse: The audits: the same list of checks for every page
In real life: The written report
In Lighthouse: The Lighthouse report, with its scores
The main idea

Lighthouse is a lab test. It loads your page once, under settings it chooses, writes down what it saw, and then checks those notes against a list. Its numbers describe that one load, on that one machine. They aren't a measurement of Asha's phone.

That's what Lab data vs field data called a lab test: a test under conditions someone chose, not a measurement of real visits. The sections below take this one run apart.

Four ways to run Lighthouse

There are four ways to start Lighthouse. They all run the same tests. They differ in where you start it, and whose computer does the work:

  • In Chrome DevTools. Open DevTools, pick the Lighthouse panel, and click Analyze page load. It tests the page in the browser you're using. So it works on pages you have to sign in to see.
  • From the command line. Type npx lighthouse and the page's address. This suits scripts that you run again and again.
  • As a Node module. Call Lighthouse from your own JavaScript. Chrome's docs suggest this for continuous integration: the checks a team runs automatically each time the code changes.
  • On PageSpeed Insights. It's a web page from Google. Paste an address and click Analyze. There's nothing to install.

The command line and Node need Google Chrome installed on your computer. Lighthouse doesn't load pages by itself. It drives Chrome. From Node, a run takes a few lines:

Lighthouse from NodeJavaScript
import lighthouse from "lighthouse";import * as chromeLauncher from "chrome-launcher"; const chrome = await chromeLauncher.launch({ chromeFlags: ["--headless=new"] });const { lhr } = await lighthouse("http://127.0.0.1:8080/", { port: chrome.port }); console.log(lhr.lighthouseVersion, lhr.categories.performance.score);await chrome.kill();
  • Line 1 loads Lighthouse. Line 2 loads chrome-launcher, a small package that starts Chrome for you.
  • Line 4 starts Chrome without a window. That's what --headless=new asks for.
  • Line 5 runs Lighthouse on the store. It passes Chrome's debugging port: a numbered door that lets another program drive Chrome, open pages, and read what happened.
  • Line 7 prints two things from lhr, the Lighthouse Result: one big object holding everything the run found. Here, the version, and the Performance score from 0 to 1.
  • Line 8 closes Chrome.

The course's recorder makes these same calls. Its run printed version 13.6.0 and a score of 0.9. The report shows that as 90.

PageSpeed Insights runs it for you

PageSpeed Insights is different in one big way. It runs Lighthouse on Google's computers, not yours. Its docs say it runs "in a Google datacenter", in North America, Europe or Asia. So its lab numbers describe Google's machine and network, not your laptop.

It also shows a second kind of number: field data. These are real visits by Chrome users who agreed to share them, collected in the Chrome User Experience Report, or CrUX. PageSpeed Insights shows what those visits got over the previous 28 days.

Real-life analogyA forecast, and yesterday's weather

The weather page on your phone shows two different things. One is tomorrow's forecast, worked out by a computer model. The other is what yesterday was really like: how warm it got, and whether it rained. A sunny forecast doesn't mean yesterday was dry. You read both, and you know which is which.

In real life: Tomorrow's forecast, worked out by a computer model
In PageSpeed Insights: The Lighthouse result: one load, on a set phone and network
In real life: What the weather really was yesterday
In PageSpeed Insights: Field data: what real Chrome users got over 28 days
In real life: One weather page that shows both
In PageSpeed Insights: PageSpeed Insights, with field data and a Lighthouse run

Two things follow. If PageSpeed Insights and your own DevTools run give different lab scores, nothing is broken: they ran on different machines. Throttling and simulation compares results across machines. And if the lab score is green but the field data isn't, the field data is what your visitors really got. Judge the page by it, as Lab data vs field data explained.

What Lighthouse does to your page

Back to the store's run. Lighthouse keeps notes as it works: every step, when it started, and how long it took. These timing entries are saved in the Lighthouse Result. The course also logged every request the store's servers received. Side by side, the two show exactly what Lighthouse did to the store.

Pick a step in the playground to see how long it took, and which requests reached the servers during it:

Playground: one Lighthouse run
The run0 to 7,242.4 ms
about:blank
Benchmark
Prepare
Load the page
Artifacts
Screenshot
Back/forward cache
Audits
Requests
Requests in this steprecorded
  • /200, the phone
  • /styles.css200, the phone
  • /fonts.css200, the phone
  • /app.js200, the phone
  • /hero.jpg200, the phone
  • /widget.js200, the phone, the widget's server
  • /reviews.js200, the phone
  • /geist.woff2200, the phone
Try it yourself
Load the page

It loads the page. Then it waits until 1,000 ms have passed since the load event, and the network and the CPU have each been quiet for 1,000 ms. This took 3,150.5 ms. All 8 of the page's files arrived in this step, each with the phone's user agent.

Recorded with Lighthouse 13.6.0 from Node, with its default settings, on the store from Performance insights. The bars come from Lighthouse's own timing entries. The ticks on the last row are the requests the store's servers received. This is the middle of 3 runs.

Getting ready: about a second

Before it loads the store, Lighthouse does four things, in this order:

  1. It opens a blank page, about:blank. It turns its network log off while it does, so nothing from before the run gets into its notes.
  2. It rates the computer. It runs a benchmark: a small test program that counts how much work the CPU gets done. You met a version of it in The Performance panel, where DevTools used it to calibrate its slowdown.
  3. It makes Chrome behave like a phone. The screen becomes 412 by 823 CSS pixels. The user agent, the line of text a browser sends with each request to say what it is, now names an Android phone, a moto g power.
  4. It clears out saved data for the store, so that its files have to come over the network again.

The benchmark is most of this part: 1,004.6 ms of it. It runs two half-second loops of JavaScript that build strings and copy arrays. The more loops the CPU gets through, the higher the score. This computer scored 3,001, and Lighthouse's source calls 1,000 and up "a desktop-class device". The report lists the score as "Unthrottled CPU/Memory Power", so readers know how fast the testing computer was.

The reset works on the store's origin: its scheme, host and port together, like http://127.0.0.1:8080. Here is what it clears by default:

  • file_systems
  • shader_cache
  • service_workers
  • cache_storage
  • and, separately, the browser's cache.

Clearing them matters for speed. A service worker, a script a site installs to answer its own requests, could answer the page's requests from files saved in Cache Storage. So could the browser's cache. With those cleared, every file has to come over the network, as on a first visit.

Cookies, localStorage and IndexedDB aren't on the list, so they survive the reset. That's how the DevTools panel can test a page you're signed in to: your cookies are still there. You can also turn the whole reset off with a setting. Exercise 5 asks you to find it.

One thing is missing from the list: slowing down. In The Performance panel, DevTools slowed the computer down, so it behaved like a phone while the page loaded. Lighthouse's default method, simulate, doesn't. It loads the page at this computer's full speed. Afterwards, it calculates how long the same load would have taken on a slower phone, on a slower network.

That phone's CPU is 4 times slower than this computer's. Its network takes 150 ms for a round trip, a message to the server and back, and carries about 1.6 megabits a second. Throttling and simulation shows how the calculation works. For now, remember the forecast: Lighthouse's speed numbers are worked out, not timed on a real phone.

Loading the page: about three seconds

Now Lighthouse loads the store, and the store's servers see the page arrive. Here are the requests they received during this step:

The page's own requestsPop out in the code editor (opens in a new tab)JavaScript
// The requests the store's server got while Lighthouse loaded the page.// Each is [file, when it arrived], in ms from the start of the run.const loadStartMs = 1102.5;const requests = [  ["/", 1107.7], ["/styles.css", 1844], ["/fonts.css", 1844.1], ["/app.js", 1844.6],  ["/hero.jpg", 1844.7], ["/widget.js", 1847.7], ["/reviews.js", 1848.2], ["/geist.woff2", 1860.3],]; const lastMs = requests.at(-1)[1] - loadStartMs;console.log(`The page asked for ${requests.length} files.`);console.log(`The last one arrived ${lastMs.toFixed(1)} ms into the load.`);
  • Lines 1 and 2 are comments. Each request is a file and the moment it reached the server, counted from the start of the run.
  • Line 3 is when the load step began: 1,102.5 ms into the run.
  • Lines 5 and 6 are the 8 requests, as recorded. Every one of them carried the phone's user agent.
  • Line 9 works out how far into the step the last request came.
  • It prints The page asked for 8 files. and The last one arrived 757.8 ms into the load.

Look at the first two times on line 5. The request for the page itself arrived at 1,107.7 ms. The next one came at 1,844 ms. In between, the store's server held the HTML back for 700 ms: the slow server from Performance insights. The browser couldn't ask for the other files until the HTML told it what they were.

Now compare the 757.8 ms with the whole step, which lasted 3,150.5 ms. For its last 2.4 seconds, the store's servers heard nothing new. Lighthouse was letting the page finish, and then waiting for it to go quiet. Why wait? A page that has stopped asking for files can still be running scripts or drawing. Lighthouse needs to know when the page is really done.

Real-life analogyA photographer waits for everyone to keep still

At a family photo, the photographer doesn't press the button the moment the last cousin arrives. They wait until nobody is walking in or out of the picture. Then they wait until everyone stops fidgeting. If a small child just won't keep still, they take the photo anyway, after a while.

In real life: Everyone has arrived
In Lighthouse: The page's load event has fired
In real life: Nobody is walking in or out of the picture
In Lighthouse: The network is quiet: two requests or fewer at a time
In real life: Nobody is fidgeting
In Lighthouse: The CPU is quiet: no long tasks on the main thread
In real life: Taking the photo anyway, after a long wait
In Lighthouse: Giving up after 45 seconds

Lighthouse's source spells out what "keeping still" means. With the default settings, it waits until all of these are true:

  • 1,000 ms have passed since the page's load event, the moment the browser says the page and its files have loaded.
  • The network has been quiet for 1,000 ms. Quiet means two requests or fewer still going.
  • After that, the CPU has been quiet for 1,000 ms. Quiet means no long task: no single piece of work that kept the main thread busy for 50 ms or more.

If that never happens, it stops waiting after 45 seconds and carries on with what it has. Why it matters: the load step includes the work your page does right after it loads. A script that keeps the main thread busy after the load event makes Lighthouse wait longer, and that work ends up in the recording.

Collecting the rest: under three seconds

With the page settled, Lighthouse asks it questions and saves each answer: its links, its images, its scripts and more. It also saves the trace: the full recording of the load, the same kind you read in Reading a performance trace.

Two of these steps do things you might not expect:

  • It takes a screenshot of the whole page, top to bottom, for the report. After the load itself, it's the longest step in the run. Exercise 3 asks you to find out how long it took.
  • It tests the back/forward cache. That's a browser feature that keeps a whole page in memory when you leave it, so the Back button can show it at once. Lighthouse goes to chrome://terms, a page built into Chrome, and then back. The store came straight back from the cache, so that check passed.

The servers noticed this part too. Here is what they received after the load:

What the servers got after the loadPop out in the code editor (opens in a new tab)JavaScript
// What the store's server got after the load, in order: [file, who asked, status].// "phone" is Chrome still dressed as the phone. "chrome" is Chrome as itself.const afterLoad = [  ["/llms.txt", "chrome", 404], ["/styles.css", "phone", 200], ["/fonts.css", "phone", 200],  ["/robots.txt", "chrome", 404], ["/", "chrome", 200],  ["/.well-known/ard.json", "chrome", 404], ["/.well-known/ai-catalog.json", "chrome", 404],]; const again = afterLoad.filter(([, by]) => by === "phone").map(([file]) => file);const own = afterLoad.filter(([, by]) => by === "chrome").map(([file, , status]) => `${file} ${status}`);console.log(`Asked for again: ${again.join(", ")}`);console.log(`Chrome itself: ${own.join(", ")}`);
  • Lines 1 and 2 are comments. "phone" means the request carried the phone's user agent. "chrome" means Chrome asked as itself, with its own user agent.
  • Lines 4 to 6 are the 7 requests, in the order they arrived, with the status the server sent back. A 404 means "not found".
  • Line 9 picks the files the phone asked for. Line 10 picks the ones Chrome asked for itself.
  • Lines 11 and 12 print Asked for again: /styles.css, /fonts.css and Chrome itself: /llms.txt 404, /robots.txt 404, / 200, /.well-known/ard.json 404, /.well-known/ai-catalog.json 404.

So two kinds of request came in after the load:

  • The page's two stylesheets, again. They came during the step that collects the page's CSS, called CSSUsage.
  • Five requests from Chrome itself. They feed Lighthouse's checks, not the page. robots.txt is for an SEO check. llms.txt, the page, and the two files under /.well-known/ are for the checks in a category called Agentic Browsing.

None of these requests are part of the page loading, so none of them count in its metrics. But they all show up in your server's logs. If your logs show requests for robots.txt and llms.txt right after a test, that's likely Lighthouse, not a visitor.

Gatherers, audits, and the report

You've now seen Lighthouse do two kinds of work. While the page was open, it watched and wrote things down. At the very end, in 266.6 ms, it judged. An inspector's clipboard makes them easy to picture:

Real-life analogyThe clipboard, and the checks back at the office

During the visit, the inspector only measures and writes things down. The damp in the bathroom, the age of the boiler, a photo of the roof: it all goes on the clipboard. Back at the office, they go through their list of checks, using only what's on the clipboard. Anyone at the office could do the checks from that clipboard and get the same answers.

In real life: Taking one reading, like the damp in the bathroom
In Lighthouse: A gatherer collecting one kind of information
In real life: The clipboard, full of readings and photos
In Lighthouse: The artifacts
In real life: One check on the office's list: is the damp too high?
In Lighthouse: One audit
In real life: A colleague doing the checks from the same clipboard
In Lighthouse: Audit mode, -A, run on saved artifacts
In real life: The finished report
In Lighthouse: The report, drawn from the Lighthouse Result

Here are the parts, in Lighthouse's words:

  • A gatherer collects one kind of information from Chrome. The docs say it does "Minimal post-processing." It writes things down. It doesn't judge.
  • What a gatherer collects is an artifact: the trace, the list of images, the robots.txt file, and so on.
  • An audit is one check, such as "Do the images have a width and height?" In the docs' words: "Using the Artifacts as input, an audit evaluates a test and resolves to a numeric score."
  • The Lighthouse Result holds every audit's result. The report you read is drawn from it.
Lighthouse's two halves. Gathering: gatherers collect 40 artifacts from Chrome, in 6,975.8 ms. Auditing: 161 audits judge the artifacts, in 266.6 ms, and the report shows them. Saved to disk, the artifacts took 4.0 MB.gather (-G)audit (-A)Chromegatherers40 artifacts161 auditsreport6,975.8 ms4.0 MB on disk266.6 ms
Read it left to right. Gathering needs the browser and takes most of the time. Auditing needs only the artifacts.

The store's run had 40 artifacts. Lighthouse filled in 12 of them itself, such as its settings and the benchmark score. Gatherers collected the other 28.

Fewer checks, less to collect

A category is one section of the report, such as Performance or SEO. When you ask for fewer categories, Lighthouse collects fewer artifacts. The course ran both ways three times, taking turns:

Every category, and only PerformancePop out in the code editor (opens in a new tab)JavaScript
// Every category, and only Performance: three runs each, taking turns.const runs = {  "every category": { artifacts: 40, audits: 161, gatherMs: [7250.5, 6975.8, 6960.1] },  "Performance only": { artifacts: 25, audits: 49, gatherMs: [6149, 6041.5, 6038] },}; for (const [name, { artifacts, audits, gatherMs }] of Object.entries(runs)) {  const median = [...gatherMs].sort((a, b) => a - b)[1];  console.log(`${name}: ${artifacts} artifacts, ${audits} audits, gathering ${median} ms`);}
  • Lines 3 and 4 are recorded: how many artifacts and audits each kind of run had, and how long gathering took in each of its three runs.
  • Line 8 sorts the three times and takes the middle one, the median.
  • It prints every category: 40 artifacts, 161 audits, gathering 6975.8 ms and Performance only: 25 artifacts, 49 audits, gathering 6041.5 ms.

Asking for Performance alone skipped 15 artifacts and made gathering about 900 ms shorter. If speed is all you're testing, that's almost a second saved on every run.

Gathering and auditing can run apart

Audits read only the artifacts. So Lighthouse can stop halfway, and finish later. Gather mode, -G, collects the artifacts and saves them to disk. Audit mode, -A, reads saved artifacts and runs the audits, with no browser at all. The course gathered once. Here is what it saved:

What gather mode savedPop out in the code editor (opens in a new tab)JavaScript
// The files Lighthouse's gather mode saved for one run, in bytes.const saved = { "artifacts.json": 805308, "devtoolslog.json": 45836, "trace.json": 3142201 }; const total = Object.values(saved).reduce((sum, bytes) => sum + bytes, 0);console.log(`saved: ${(total / 1e6).toFixed(1)} MB`);console.log(`the trace alone: ${Math.round((saved["trace.json"] / total) * 100)}%`);
  • Line 2 holds the three files gather mode saved, with their sizes in bytes, as recorded.
  • Line 4 adds the sizes up.
  • Lines 5 and 6 print saved: 4.0 MB and the trace alone: 79%.

Lighthouse's docs describe each file:

  • artifacts.json holds "The output from all gatherers."
  • trace.json is the trace. "Most performance characteristics come from here," and you can open it in the Performance panel.
  • devtoolslog.json is a log of the messages Chrome sent Lighthouse during the run. The docs call it the "Primary signal about network requests and page state."

So the trace, the security camera footage from The Performance panel, is most of the clipboard. Then the course audited those same files twice:

Audit the same files twicePop out in the code editor (opens in a new tab)JavaScript
// Audit mode, run twice on the same saved files, with no browser.const audits = [{ ms: 498.9, score: 0.9, lcpMs: 2686.4 }, { ms: 451.2, score: 0.9, lcpMs: 2686.4 }]; for (const [index, { ms, score, lcpMs }] of audits.entries()) {  console.log(`audit ${index + 1}: score ${Math.round(score * 100)}, LCP ${lcpMs} ms, in ${ms} ms`);}
  • Line 2 holds the two audit runs, as recorded: how long each took, the Performance score, and the LCP. LCP, Largest Contentful Paint, is the moment the biggest image or block of text appeared.
  • Line 5 prints one line per run: audit 1: score 90, LCP 2686.4 ms, in 498.9 ms and audit 2: score 90, LCP 2686.4 ms, in 451.2 ms.

Same artifacts, same score, same LCP, to the decimal. Only the time each audit run took changed. Now compare the three full runs from earlier: they scored 88, 90 and 90. So when the score moves from run to run, the change comes from gathering. Each run loads the page again, and sees something a little different. The next lesson looks at why.

From the command line, the same runs look like this:

The same runs from the command lineText
npx lighthouse http://127.0.0.1:8080/npx lighthouse http://127.0.0.1:8080/ --only-categories=performancenpx lighthouse http://127.0.0.1:8080/ -Gnpx lighthouse http://127.0.0.1:8080/ -A
  • Line 1 runs every category. Line 2 runs only Performance.
  • Line 3 gathers and saves, to a folder called latest-run unless you name another.
  • Line 4 audits what line 3 saved. It needs no browser.

Navigation, timespan, and snapshot

Every run so far has started the same way: with a fresh page load. But Asha had two complaints. The picture of mint tea was slow to appear. And when she tapped Add to cart, for a moment nothing happened. A page-load test can't see that tap. It's over before anyone taps anything.

So Lighthouse has three modes, for three kinds of question. To picture them, go back to the security camera above Leaf and Kettle's counter, from The Performance panel:

Real-life analogyFilm the opening, film one customer, or take a photo

You can use the shop's camera three ways. Film opening time, starting as the shutter goes up. Film one customer, from the moment they walk in until they leave with their tea. Or take one photo of the counter, to see what's on it right now. A photo can't tell you how long anything took. But it shows exactly what's there.

In real life: Filming opening time, from the shutter going up
In Lighthouse: Navigation mode: one page load
In real life: Filming one customer, from the door to the till
In Lighthouse: Timespan mode: the stretch of time you choose, usually with a click or a tap
In real life: One photo of the counter
In Lighthouse: Snapshot mode: the page as it is at one moment
In real life: A photo has no running time
In Lighthouse: A snapshot has no metrics and no score

Lighthouse's docs define the three modes in one line each:

  • "Navigation mode analyzes a single page load." It's the default, and every run in this lesson so far used it.
  • "Timespan mode analyzes an arbitrary period of time, typically containing user interactions." You start it, use the page, and stop it.
  • "Snapshot mode analyzes the page in a particular state", such as with a menu open.

DevTools' Lighthouse panel has all three: you pick the mode, then click its button. Read each row of this table across: one mode, what it tests, and how its report shows Performance.

The three modes
ModeWhat does it test?How does it show Performance?When would you use it?
NavigationIt tests one page load, until the page goes quiet.It shows a score out of 100.Use it to test how a page loads.
TimespanIt tests whatever happens between its start and its end.It shows the audits that passed, like 17/21.Use it to test a click, a tap, or typing.
SnapshotIt tests the page as it is at one moment.It shows the audits that passed, like 0/1.Use it to test an open menu or a filled-in form.

The docs also list each mode's limits. A timespan "Cannot analyze moment-based performance metrics (e.g. Largest Contentful Paint)", and has no Accessibility category. A snapshot doesn't look at the network or the main thread, so it has no metrics at all.

The course ran all three on the store, one after the other: load it, tap Add to cart, then look at the page. Here is what each step reported:

What each mode measuredPop out in the code editor (opens in a new tab)JavaScript
// The store's user flow: how many audits each step ran, and which metrics it reported.const flow = [  { mode: "navigation", audits: 161, metrics: ["FCP", "LCP", "TBT", "CLS", "SI"] },  { mode: "timespan", audits: 49, metrics: ["TBT", "CLS", "INP"] },  { mode: "snapshot", audits: 90, metrics: [] },]; for (const { mode, audits, metrics } of flow) {  console.log(`${mode}: ${audits} audits, ${metrics.length > 0 ? metrics.join(", ") : "no metrics"}`);}
  • Lines 3 to 5 are recorded, from each step's report: how many audits ran, and which metrics it showed.
  • Line 9 prints one line per step: navigation: 161 audits, FCP, LCP, TBT, CLS, SI, timespan: 49 audits, TBT, CLS, INP and snapshot: 90 audits, no metrics.

Here is each of those metrics in one line:

  • FCP, First Contentful Paint: when the first text or image appeared.
  • LCP, Largest Contentful Paint: when the biggest image or block of text appeared.
  • TBT, Total Blocking Time: how long the main thread was blocked for long enough that it couldn't answer input.
  • CLS, Cumulative Layout Shift: how much things moved around on the screen.
  • SI, Speed Index: how quickly the page's content showed up on the screen.
  • INP, Interaction to Next Paint: how quickly the page answered a click, a tap or a key press.

Only the timespan measured the click. It reported INP, the click's metric, and no LCP. Why it matters: to measure a tap like Asha's, you need a timespan. A plain page load doesn't include one.

Pick a mode to see what the store's flow report showed for it:

Playground: one flow, three modes
Add to cart49 audits
17/21Performance
8/8Best Practices
TBT 210 msCLS 0INP 270 ms
Try it yourself
Timespan: Add to cart

A timespan tests whatever happens between its start and its end: here, the click on Add to cart. 49 audits ran. There's no LCP: the docs say a timespan can't measure moment-based metrics like it. There is an INP: 270 ms. Performance shows 17/21: the audits that passed, out of those that count. These didn't pass: total-blocking-time, interaction-to-next-paint, inp-breakdown-insight, unsized-images.

Recorded with Lighthouse 13.6.0: one user flow on the store, run from Node with Puppeteer. Each mode shows what the flow report showed for that step, worked out with the report's own rules.

Look at Performance in the timespan: 17/21, not a score. For timespans and snapshots, the report shows how many audits passed, out of those that count. So don't compare a timespan's 17/21 with a navigation's score. They're different kinds of number.

INP and CLS in a timespan are lab readings. Real browsers report the same two things to a page's JavaScript, as these two kinds of performance entries:

User flows

A user flow puts several steps, in any of the three modes, into one report. It's like keeping the opening film, the customer clip and the photo of the counter in one folder, in the order they happened.

In DevTools, each run makes its own report. The docs say: "Analyzing a multi-step user flow is currently only available using the Lighthouse Node API along with Puppeteer." Puppeteer is a library that drives Chrome from code: it opens pages, clicks buttons, and types. Here is the store's flow, as the course ran it:

The store's user flowJavaScript
import * as chromeLauncher from "chrome-launcher";import puppeteer from "puppeteer-core";import { startFlow } from "lighthouse"; const chrome = await chromeLauncher.launch({ chromeFlags: ["--headless=new"] });const browser = await puppeteer.connect({ browserURL: `http://127.0.0.1:${chrome.port}` });const page = await browser.newPage();const flow = await startFlow(page); await flow.navigate("http://127.0.0.1:8080/", { name: "Load the store" });await flow.startTimespan({ name: "Add to cart" });await page.click("#add");await new Promise((resolve) => setTimeout(resolve, 2000));await flow.endTimespan();await flow.snapshot({ name: "After adding to cart" }); const report = await flow.generateReport();
  • Lines 1 to 3 load chrome-launcher, Puppeteer, and Lighthouse's startFlow.
  • Lines 5 to 7 start Chrome, connect Puppeteer to it, and open a tab.
  • Line 8 starts the flow in that tab.
  • Line 10 is the navigation: it loads the store.
  • Lines 11 to 14 are the timespan. Line 11 starts it. Line 12 clicks Add to cart. Line 13 waits two seconds. Line 14 ends it.
  • Line 15 is the snapshot: the page as the click left it.
  • Line 17 builds one report with all three steps.

Line 13 matters. The docs advise: "Always wait for transitions and interactions to finish before ending a timespan." The two seconds leave time for the work the click starts to happen inside the timespan.

The recorded user flow: a navigation that loads the store (161 audits), a timespan around the click on Add to cart (49 audits), and a snapshot of the page after it (90 audits).Load the storeAdd to cartnavigationtimespansnapshot161 audits49 audits90 auditsafter adding to cart
One flow, three steps, one report. The navigation covers the load, the timespan covers the click, and the snapshot is one moment, drawn as a thin line.

One more tip from the docs: "strive for each report to have a narrow focus." One load, one interaction, one state. Then each report answers one question. To see the real thing, open the store's flow report (1.4 MB), the file Lighthouse wrote for this flow.

Which mode?

Each item below is something you might want to test. For each one, ask: is it about a page load, about something that happens over time, or about the page at one moment?

Which mode?
  • The LCP of your landing page
  • A Performance score out of 100
  • The INP of clicking Add to cart
  • Layout shifts while a filter updates the list
  • Accessibility of a menu once it's open
  • Images without sizes inside an open dialog
Try it yourself
0 of 6 correct

Sort each item by the Lighthouse mode that tests 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 phone." No. By default, it loads the page at this computer's full speed, dressed up as a phone. Then it calculates how long a slower phone would have taken.
  • "It only loads my page." No. It also opens a blank page, goes to chrome://terms and back, and asks your server for robots.txt, llms.txt and more.
  • "It starts from a clean browser." No. It clears the cache and some kinds of storage. Cookies and localStorage stay, so you stay signed in.
  • "A timespan's 17/21 is a score." No. It's the number of audits that passed, out of those that count.
  • "Auditing is the slow part." No. On the store, gathering took about seven seconds. All 161 audits took 266.6 ms.
  • "PageSpeed Insights tested my page on my computer." No. It runs Lighthouse on Google's computers, in a datacenter.

Practice exercises

Try each one before you open the hints.

Exercise 1 · Warm-upA slower load

This is the four-parts example, with loading 3,000 ms longer. What does line 7 print now?

Starter codePop out in the code editor (opens in a new tab)JavaScript
// One Lighthouse run on the store, in four parts.
// Each time is in milliseconds (ms), from Lighthouse's own notes.
const run = { gettingReady: 1102.5, loading: 6150.5, collecting: 2722.8, auditing: 266.6 };

const total = Object.values(run).reduce((sum, ms) => sum + ms, 0);
console.log(`The whole run took ${(total / 1000).toFixed(1)} seconds.`);
console.log(`Loading the page took ${Math.round((run.loading / total) * 100)}% of it.`);
console.log(`Auditing took ${Math.round((run.auditing / total) * 100)}% of it.`);

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

    Exercise 2 · Warm-upA store with a robots.txt

    This is the after-the-load example, for a store that has a robots.txt file. What does line 12 print?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    // What the store's server got after the load, in order: [file, who asked, status].
    // "phone" is Chrome still dressed as the phone. "chrome" is Chrome as itself.
    const afterLoad = [
      ["/llms.txt", "chrome", 404], ["/styles.css", "phone", 200], ["/fonts.css", "phone", 200],
      ["/robots.txt", "chrome", 200], ["/", "chrome", 200],
      ["/.well-known/ard.json", "chrome", 404], ["/.well-known/ai-catalog.json", "chrome", 404],
    ];
    
    const again = afterLoad.filter(([, by]) => by === "phone").map(([file]) => file);
    const own = afterLoad.filter(([, by]) => by === "chrome").map(([file, , status]) => `${file} ${status}`);
    console.log(`Asked for again: ${again.join(", ")}`);
    console.log(`Chrome itself: ${own.join(", ")}`);

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

      Exercise 3 · PracticeThe screenshot

      In the first playground, how long did the full-page screenshot take?

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

        Exercise 4 · PracticeThe click's INP

        In the second playground, what INP did the timespan measure for the click on Add to cart?

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

          Exercise 5 · ChallengeKeep the storage

          You want Lighthouse to leave the cache and storage alone. Which command line flag turns the reset off?

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

            Exercise 6 · ChallengeA timespan that ends too soon

            This is the store's flow with one line missing. Now it breaks the docs' advice about timespans. Which line ends the timespan too soon?

            Starter codeJavaScript
            import * as chromeLauncher from "chrome-launcher";
            import puppeteer from "puppeteer-core";
            import { startFlow } from "lighthouse";
            
            const chrome = await chromeLauncher.launch({ chromeFlags: ["--headless=new"] });
            const browser = await puppeteer.connect({ browserURL: `http://127.0.0.1:${chrome.port}` });
            const page = await browser.newPage();
            const flow = await startFlow(page);
            
            await flow.navigate("http://127.0.0.1:8080/", { name: "Load the store" });
            await flow.startTimespan({ name: "Add to cart" });
            await page.click("#add");
            await flow.endTimespan();
            await flow.snapshot({ name: "After adding to cart" });
            
            const report = await flow.generateReport();

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

              Check your understanding

              For each question, ask which part of the run did the work, and which mode tests what.

              How Lighthouse works quiz · 7 questionsScore: first tries count
              1. Question 1 of 7What does line 6 of the four-parts example print?

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // One Lighthouse run on the store, in four parts.
                // Each time is in milliseconds (ms), from Lighthouse's own notes.
                const run = { gettingReady: 1102.5, loading: 3150.5, collecting: 2722.8, auditing: 266.6 };
                
                const total = Object.values(run).reduce((sum, ms) => sum + ms, 0);
                console.log(`The whole run took ${(total / 1000).toFixed(1)} seconds.`);
                console.log(`Loading the page took ${Math.round((run.loading / total) * 100)}% of it.`);
                console.log(`Auditing took ${Math.round((run.auditing / total) * 100)}% of it.`);

                Choose an answer to see the explanation.

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

                Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
                // The store's user flow: how many audits each step ran, and which metrics it reported.
                const flow = [
                  { mode: "navigation", audits: 161, metrics: ["FCP", "LCP", "TBT", "CLS", "SI"] },
                  { mode: "timespan", audits: 49, metrics: ["TBT", "CLS", "INP"] },
                  { mode: "snapshot", audits: 90, metrics: [] },
                ];
                
                for (const { mode, audits, metrics } of flow) {
                  console.log(`${mode}: ${audits} audits, ${metrics.length > 0 ? metrics.join(", ") : "no metrics"}`);
                }

                Choose an answer to see the explanation.

              3. Question 3 of 7You want the INP of opening a menu on a page that has already loaded. Which mode do you use?

                Choose an answer to see the explanation.

              4. Question 4 of 7Why does a timespan's report show Performance as something like 17/21 instead of a score?

                Choose an answer to see the explanation.

              5. Question 5 of 7With the default settings, what does Lighthouse clear before it loads your page?

                Choose an answer to see the explanation.

              6. Question 6 of 7Lighthouse audits the same saved artifacts twice. What happens to the score?

                Choose an answer to see the explanation.

              7. Question 7 of 7PageSpeed Insights gives your page a different lab score from the one DevTools gave on your laptop. What's the likely reason?

                Choose an answer to see the explanation.

              Key takeaways

              • Lighthouse is a lab test: one load of your page, under settings it chooses, on one machine.
              • You can run it in DevTools, from the command line, from Node, or on PageSpeed Insights. PageSpeed Insights runs it on Google's computers, and shows real users' field data beside it.
              • A run gets ready, loads the page, waits for it to go quiet, then collects the rest. Your server also sees requests from Chrome itself.
              • By default, Lighthouse loads the page at full speed and calculates a slower phone's numbers afterwards.
              • Gatherers collect artifacts. Audits judge only the artifacts, so the same artifacts always get the same score.
              • Navigation tests a load, timespan a stretch of time, snapshot one moment. Only a navigation gets a score.
              • A user flow puts the modes together in one report, from Node with Puppeteer.

              Remember the one-liner.
              Lighthouse visits once, writes down what it sees, then checks its notes. Pick the mode that matches your question.

              Sources

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

              Coming next: The Lighthouse score, also in Measuring in the lab. How the metric values become one score from 0 to 100, why that score moves between runs, and what it can't tell you.

              CompleteFrontend Clear concepts. Working examples.