Why performance matters
See what speed changes: sales, reading and searches, Google Search, who can use a site, what its bytes cost and the carbon they emit, with sources you can trust.
- 01Work out what speed changesTurn visits and orders into a conversion rate, a bounce rate and a relative change. Every speed claim is made of these numbers.
- 02Tell proof from coincidenceExplain why a fair test, which splits visitors by chance, can show that speed caused a change, and a report on fast and slow visits can't.
- 03Check a famous numberTrace a claim to its source. Then quote it, quote it with its limits, or leave it out.
- 04Measure what a page costsAdd up the bytes a page brought, and turn them into a share of a data plan and a carbon estimate.
Speed changes what people do
Remember Asha, on the bus with her cheap phone? Today she opens Leaf and Kettle again to buy mint tea. The screen stays blank. One second goes by, then another. She closes the tab and gets off the bus. She'll buy her tea somewhere else.
Here is her screen, from a real load of the shop's page on a phone like hers:
The shop's name and the teas do come, but only after she has gone. Nobody at Leaf and Kettle sees her go. She doesn't complain or fill in a form. Her visit just ends.
Ravi looks after the shop's website, and he wants to make it faster. The owner asks: "Is it worth the time? What does speed actually change?" This lesson helps Ravi answer. A slow page changes:
- what people do: how many stay, read and buy;
- how Google Search treats the page;
- who can use the site at all;
- what each visit costs, in money and in energy.
Start with the first:
You join the queue at a busy bakery. It moves slowly. A man ahead of you checks his watch and leaves. Then a woman behind you leaves too. The baker never meets either of them, so she never learns what they would have bought. The longer the queue, the more people walk out.
- In real life: People joining the queue
- In web performance: Visits that start loading the page
- In real life: People who walk out before the till
- In web performance: Visits that end before the page shows, or before an order
- In real life: People who reach the till and buy
- In web performance: Visits that end with an order
- In real life: The baker never meets the ones who left
- In web performance: Nobody tells the shop: only counting visits shows it
A slow page is that queue. But a story isn't proof. How could Ravi show that speed, and nothing else, made people leave?
A fair test: two versions of one page
The clearest way is a fair test. Make two versions of the page that are the same in every way, except that one is faster. Send each visitor to one of them by chance, like a coin flip. Then count what each group does. This is called an A/B test.
Vodafone, a telecoms company, ran one on a landing page in 2021. Each version got about 34,000 visits a day. Here is a test in that shape, with made-up orders:
const fast = { visits: 34000, orders: 1020 };const slow = { visits: 34000, orders: 944 }; const rate = (group) => (group.orders / group.visits) * 100; console.log("fast page:", rate(fast).toFixed(1) + "% of visits ordered");console.log("slow page:", rate(slow).toFixed(1) + "% of visits ordered");- Lines 1 and 2 are the two versions. Each got 34,000 visits. The fast one got 1,020 orders, and the slow one 944.
- Line 4 works out a conversion rate: the share of visits that did what the shop wanted, here ordering. It divides orders by visits, then multiplies by 100 to make a percentage.
- Lines 6 and 7 print
fast page: 3.0% of visits orderedandslow page: 2.8% of visits ordered.toFixed(1)keeps one digit after the point.
3.0% and 2.8% look almost the same. Is the gap worth anything? Count the extra orders instead:
const fastOrders = 1020;const slowOrders = 944; // from the same 34,000 visits const extra = fastOrders - slowOrders;const change = extra / slowOrders; console.log("extra orders:", extra);console.log("the fast page sold", Math.round(change * 100) + "% more");- Line 4 subtracts. The fast page got 76 more orders, and line 7 prints
extra orders: 76. - Line 5 divides those 76 by the slow page's 944 orders. That gives about 0.08, so the fast page got 8% more. A change measured against where it started is called a relative change.
- Line 8 prints
the fast page sold 8% more.Math.roundrounds to a whole number.
So one gap can be told two ways. Subtract the rates, and 3.0% minus 2.8% is 0.2 percentage points. Compare the orders, and the fast page sold 8% more. Both are true, so check which one a claim uses.
Speed changes what people do. When only the speed of a page changed, people searched, read and bought more on the faster version. Speed also decides who can use a site, and what each visit costs. The best proof comes from fair tests, not from comparing fast visits with slow ones.
Conversions, bounce and engagement
Leaf and Kettle runs an analytics tool: a small script on every page that records each visit. Google Analytics is one. Each row is one of its numbers, what it counts, and how a slow page can change it:
| Number | What it counts | How a slow page can change it |
|---|---|---|
| Conversion rate | The share of visits that end with the thing the shop wants, such as an order or a sign-up. | Every slow step is a place where some people give up before the end. |
| Bounce rate | In Google Analytics 4, the share of visits that weren't engaged: 10 seconds or less, one page, and no key event such as an order. | Someone staring at a blank screen may leave before anything shows. |
| Pages per visit | How many pages each visit opened. It's also called session depth. | Each new page is one more wait, so long visits feel it most. |
You met the conversion rate in the first example. The bounce rate needs a rule for what counts as a real visit. Google Analytics 4 calls a visit engaged if it does at least one of these:
- it lasts longer than 10 seconds;
- it opens 2 or more pages;
- it has a key event, something the shop marked as important, such as an order.
A visit that does none of them is a bounce. Here are five visits, checked by that rule:
const visits = [ { pages: 1, seconds: 4, ordered: false }, { pages: 1, seconds: 35, ordered: false }, { pages: 3, seconds: 80, ordered: true }, { pages: 2, seconds: 50, ordered: false }, { pages: 1, seconds: 6, ordered: false },]; const engaged = (visit) => visit.seconds > 10 || visit.pages >= 2 || visit.ordered;const bounces = visits.filter((visit) => !engaged(visit)); console.log("bounce rate:", (bounces.length / visits.length) * 100 + "%");- Lines 1 to 7 hold the five visits. Each has the pages it opened, the seconds it lasted, and whether it ordered.
- Line 9 is the rule for an engaged visit.
||means "or", so any one of the three is enough. - Line 10 keeps the visits that are not engaged.
!means "not". - Line 12 prints
bounce rate: 40%. The visits on lines 2 and 6 bounced: one page, for a few seconds. The visit on line 3 also opened one page, but it stayed 35 seconds, so it's engaged.
Pages per visit is simpler. These five visits opened 1 + 1 + 3 + 2 + 1 = 8 pages. 8 divided by 5 visits is 1.6 pages per visit.
Tests that changed only the speed
Do these numbers really move when a page gets slower? Four companies tested it, changing only the speed for one group. Each row is one test, what changed, and what people did:
| Who and when | What they changed | What people did |
|---|---|---|
| Google, 2009 | Added 100 to 400 ms to its search results page for one group of users, for 4 to 6 weeks. A second group, the control, got no delay. | Daily searches per user fell 0.20% with 100 ms, and 0.59% with 400 ms. With 400 ms, the loss grew to 0.74% in weeks 4 to 6. |
| Financial Times, 2016 | Added 1, 2 or 3 seconds to every page for some groups of subscribers, for about 4 weeks. A control group saw the normal speed. | With 1 second added, subscribers read 4.9% fewer articles in 7 days. With 3 seconds, they read 7.2% fewer in 7 days, and 7.9% fewer in 28 days. |
| Zalando, 2018 | Ran an A/B test of loading time on its shop. | Loading 100 ms faster brought 0.7% more revenue per visit. |
| Vodafone, 2021 | Sent half the visitors to a landing page with a 31% better LCP (5.7 s instead of 8.3 s). Nothing else differed. | The faster page brought 8% more sales. It also had 15% more leads per visit, and 11% more visits that reached the cart. |
Google's smallest delay, 100 ms, is a tenth of a second. Yet people with it searched less. The paper found two more things. People searched less the longer they had the delay. And after Google took the 400 ms delay away, that group still searched 0.21% less over the next 5 weeks. The FT saw a loss grow too. With 3 seconds added, 7.2% fewer articles in the first week became 7.9% fewer over 28 days.
The café near your office is slow one week. Every morning you wait too long for your tea. By Friday, you've started going to the café across the road. The next week, the first café is quick again, but you don't know that. You keep crossing the road.
- In real life: A slow morning at the café
- In web performance: A slow visit to the page
- In real life: A whole slow week hurts more than one morning
- In web performance: The losses grew the longer people had the delay
- In real life: Trying the café across the road
- In web performance: Searching less, or reading fewer articles
- In real life: Not going back, even once it's quick again
- In web performance: Searching less for weeks after the delay was removed
So a slow page doesn't only lose one visit. It can change a habit for weeks. Now feel the delays yourself:
searchButton.addEventListener("click", () => { results.textContent = "Searching…"; setTimeout(() => { results.textContent = "Mint tea, Green tea, Chai"; }, addedDelay);});No search yet.
Pick a delay, then press Search teas. The delay now is 400 ms.
In the panel's code, line 2 shows Searching… at once. Lines 3 to 5 wait for the delay you picked, then show the teas.
Most people barely notice 100 ms or 400 ms. That's why it matters: nobody complains about a delay they can't feel, yet over thousands of visits it still costs searches, reads and sales.
Cause or coincidence?
Ravi has an easier idea than building two versions. His analytics tool already holds last month's visits. He could split them into fast and slow ones, and compare. Here it is with made-up visits:
const visits = [ { who: "returning", loadMs: 900, count: 200, orders: 12 }, { who: "new", loadMs: 3100, count: 800, orders: 16 },]; for (const group of visits) { const rate = (group.orders / group.count) * 100; console.log(`${group.loadMs} ms visits: ${rate}% ordered (${group.who} visitors)`);}- Line 2 is 200 visits from returning visitors, people who had been to the shop before. Their browser had saved the site's files, so their pages loaded in 900 ms. 12 of them ordered.
- Line 3 is 800 visits from new visitors. Nothing was saved, so their pages took 3,100 ms. 16 of them ordered.
- Lines 6 to 9 print each group's rate:
900 ms visits: 6% ordered (returning visitors)and3100 ms visits: 2% ordered (new visitors).
Fast visits ordered three times as often. Should Ravi tell the owner that speed triples orders? First, look at who made the fast visits. Every one came from a returning visitor. In these made-up visits, returning visitors order more often anyway. So the report can't tell how much of the 6% came from speed, and how much from who the visitor was.
The report shows that speed and orders go together. That's called a correlation. It doesn't show that speed caused the orders. The hidden difference, here the kind of visitor, changes both at once. A hidden difference like that is called a confounder.
Split the same made-up visits another way, and watch the difference disappear:
const visits = [ { who: "returning", fast: true, count: 200, orders: 12 }, { who: "returning", fast: false, count: 50, orders: 3 }, { who: "new", fast: true, count: 100, orders: 2 }, { who: "new", fast: false, count: 800, orders: 16 },]; function percentOrdered(groups) { const count = groups.reduce((sum, group) => sum + group.count, 0); const orders = groups.reduce((sum, group) => sum + group.orders, 0); return ((orders / count) * 100).toFixed(1) + "%";} console.log("fast visits:", percentOrdered(visits.filter((group) => group.fast)));console.log("slow visits:", percentOrdered(visits.filter((group) => !group.fast)));4.7% ordered14 orders from 300 visits
2.2% ordered19 orders from 850 visits
Split by speed only, fast visits ordered 4.7% of the time and slow visits 2.2%. It looks as if speed doubles orders. Now split by the kind of visitor too.
In the panel's code, lines 1 to 6 are the visits, in four groups. Lines 8 to 12 add up some groups and work out the share that ordered. Lines 14 and 15 print fast visits: 4.7% and slow visits: 2.2%.
These visits were made up so that speed changes nothing. Real data can hide the same thing, or the opposite, and the report alone can't tell you which. A fair test can:
A shop wants to know if a slow till loses customers. It opens two tills that are the same in every way, except that one cashier works slowly on purpose. At the door, a coin flip sends each shopper to one till. The coin doesn't care who you are, so each till gets the same mix of shoppers. If more baskets are left behind at the slow till, the wait caused it.
- In real life: The coin flip at the door
- In web performance: Sending each visitor to a version by chance
- In real life: Two tills, the same except for one slow cashier
- In web performance: Two versions of a page that differ only in speed
- In real life: The same mix of shoppers at each till
- In web performance: New and returning visitors spread evenly over both versions
- In real life: More baskets left behind at the slow till
- In web performance: Fewer orders on the slow version, caused by the speed
Choosing by chance like this is called a random split. It gives both groups about the same mix of new and returning visitors, phones and countries. So if one group does better, speed is the only difference left to explain it. That's why fair tests are the strongest evidence that speed itself changes what people do. The four tests in the last section each kept a group at the normal speed to compare against.
Famous numbers, checked
Ravi searches the web for more evidence. The same few numbers turn up on slide after slide: "100 ms costs Amazon 1% of sales", and "53% of mobile visits are abandoned after 3 seconds". Can he quote them? Before you repeat any number, ask three questions:
- Is there a named source you can read?
- Was it an experiment, a fair test?
- Did they publish how they did it?
This function asks the same three questions:
function judge(claim) { if (!claim.source) return "don't repeat it"; if (claim.experiment && claim.method) return "quote it, with its source and year"; return "quote it only with its limits";} const claims = [ { text: "400 ms slower, 0.59% fewer searches", source: "Google, 2009", experiment: true, method: true }, { text: "100 ms slower, 1% fewer sales", source: "one slide, 2006", experiment: true, method: false }, { text: "a faster page always doubles sales", source: null },]; for (const claim of claims) console.log(`${claim.text}: ${judge(claim)}`);- Line 2: a claim with no source isn't evidence, so don't repeat it.
- Line 3: an experiment with a published method can be quoted, with its source and year.
- Line 4: anything else can be quoted only with its limits.
- Lines 7 to 11 are three claims. Line 13 prints each one with its verdict:
quote it, with its source and year,quote it only with its limitsanddon't repeat it.
Here is where the most famous numbers really come from. Each row is one claim, its real source, and what you can honestly say:
| The claim | Where it really comes from | What you can say |
|---|---|---|
| "100 ms of delay costs Amazon 1% of sales." | One slide in a 2006 talk by Greg Linden, who worked at Amazon until 2002. His blog post on the same test says only "substantial and costly drops". Amazon never published the data. | Quote it only as history: an old test whose data nobody can check. |
| "53% of mobile visits are abandoned after 3 seconds." | A 2016 Google blog post. It says visits are "likely to be abandoned", from analytics data of about 3,700 mobile sites in one month. It doesn't say how a visit counted as abandoned. | Quote it only as a 2016 estimate, with those limits. |
| "0.1 seconds faster brings 8.4% more retail conversions." | Deloitte's 2020 study for Google: 37 brands, over 30 million visits, 30 days. It watched natural changes in speed. Nobody was slowed down on purpose. | Quote it as a correlation from one study, not as proof that speed caused it. |
| "400 ms slower means 0.59% fewer searches." | Jake Brutlag's 2009 paper at Google. One group of users got the delay and a control group didn't. It gives the method and a table of results. | Quote it, with its source and its year. |
Notice the years. The two best-known numbers come from 2006 and 2016, and phones, networks and pages have changed since then. These numbers show that speed mattered then. They can't tell you what speed is worth on your site today. For that, run your own fair test. Lesson 13.2, Proving a change worked, shows how to compare two versions honestly.
Search and Core Web Vitals
The owner has another question: "Won't a faster page put us at the top of Google?" It's worth knowing exactly what Google says.
In the last lesson you met the Core Web Vitals: LCP for loading, INP for responding, and CLS for stability. Where do they fit in search? Think of choosing a shop:
You walk down a street, looking for mint tea. You glance in each window as you pass. If only one shop sells mint tea, you go in, even if its window is dusty. But if three shops sell the same tea, a clean, bright window might be what makes you pick one.
- In real life: Walking down the street, looking for mint tea
- In web performance: Someone searching on Google
- In real life: Does this shop sell mint tea?
- In web performance: Is this page relevant to the search? That comes first.
- In real life: A clean, bright window
- In web performance: A good page experience, including good Core Web Vitals
- In real life: Three shops that sell the same tea
- In web performance: Many helpful pages that answer the same search
Google's page experience documentation says the same thing in its own words. Ranking is the order Google shows its results in:
- "Core Web Vitals are used by our ranking systems."
- Good results in Search Console or other tools don't guarantee that your pages rank at the top.
- "Google Search always seeks to show the most relevant content, even if the page experience is sub-par."
- When lots of helpful pages answer the same search, a great page experience "can contribute to success in Search".
- Chasing a perfect score just for search "may not be the best use of your time".
Why it matters: speed won't lift a page that doesn't answer the search. It helps when many pages answer it equally well. In 2018, when Google first used page speed for searches on phones, it said the change would "only affect pages that deliver the slowest experience to users".
How Search Console rates your pages
Google's Search Console shows site owners how Google sees their site. Its Core Web Vitals report works like this:
- It uses real visits, not tests. The data comes from Chrome users, through the Chrome UX Report (CrUX), over the last 28 days.
- For each metric, it takes the 75th percentile. Line up all the page loads from best to worst, and go three quarters of the way along. 75% of loads were that good or better.
- It puts similar pages together into a URL group.
- A group's status is its worst metric.
Each row of this table is one Core Web Vital, and the limits it's rated by:
| Metric | Good | Needs improvement | Poor |
|---|---|---|---|
| Largest Contentful Paint (LCP) | 2.5 s or less | Over 2.5 s, up to 4 s | Over 4 s |
| Interaction to Next Paint (INP) | 200 ms or less | Over 200 ms, up to 500 ms | Over 500 ms |
| Cumulative Layout Shift (CLS) | 0.1 or less | Over 0.1, up to 0.25 | Over 0.25 |
First, rate each of one group's values against its limits:
const limits = { lcp: [2500, 4000], inp: [200, 500], cls: [0.1, 0.25] }; function rate(metric, value) { const [good, poor] = limits[metric]; if (value <= good) return "good"; if (value <= poor) return "needs improvement"; return "poor";} console.log("LCP 2100 ms:", rate("lcp", 2100));console.log("INP 180 ms:", rate("inp", 180));console.log("CLS 0.3:", rate("cls", 0.3));- Line 1 holds two limits for each metric. At or under the first is good. At or under the second needs improvement. Anything above is poor.
- Lines 3 to 8 rate one value. Line 4 picks that metric's two limits.
- Lines 10 to 12 rate the group's three values. They print
LCP 2100 ms: good,INP 180 ms: goodandCLS 0.3: poor.
Then give the group its status:
const ratings = ["good", "good", "poor"]; // LCP, INP and CLS let status = "good";if (ratings.includes("needs improvement")) status = "needs improvement";if (ratings.includes("poor")) status = "poor"; console.log("the group's status:", status);- Line 1 holds the three ratings.
- Line 3 starts at good. Line 4 lowers it if any rating needs improvement. Line 5 lowers it to poor if any rating is poor.
- Line 7 prints
the group's status: poor.
One poor metric makes the whole group poor. So when a group is poor, fix its worst metric first.
How many sites pass? The Web Almanac 2025, HTTP Archive's yearly study of the web, found good Core Web Vitals on 48% of mobile sites and 56% of desktop sites. So passing still puts a site ahead of about half the web.
One warning. Rumours like "Google lowered the good LCP limit to 2 seconds" get passed around. No Google page says so: web.dev and Search Console still call 2.5 seconds good. Lesson 3.1, Core Web Vitals, covers each metric. Lesson 10.1, The Chrome User Experience Report, covers CrUX.
Accessibility and inclusion
A slow page isn't equally slow for everyone. It's slowest on older phones and slower networks, like Asha's. And some people already find the web harder to reach.
In 2021, Pew Research Center asked adults in the United States which devices they own. Each row is one question, with the share of adults with and without a disability who said yes:
| Question | Adults with a disability | Adults without one |
|---|---|---|
| Own a desktop or laptop computer | 62% | 81% |
| Own a smartphone | 72% | 88% |
| Have high-speed internet at home | 72% | 78% |
| Never go online | 15% | 5% |
Adults with a disability were less likely to own a computer or a smartphone. They were three times as likely never to go online. For home internet, Pew says 72% and 78% don't differ statistically.
A slow page can shut people out in three ways:
- Slower devices. An older or cheaper phone has a slower CPU, so every long task lasts longer on it.
- Screen readers wait too. A screen reader reads the page aloud for people who can't see it. It reads from the browser's accessibility tree, a copy of the page made for tools like it. Chrome updates that tree as part of the steps it runs to draw a new frame. So a long task that holds up the next frame also holds up what the screen reader can say.
- Moving targets. A layout shift can make anyone tap the wrong thing, like Asha and the Buy button. Someone who needs longer to aim, for example with a head pointer or a shaking hand, gives things more time to move first.
Some choices help people and speed at once. Motion on screen can make people with some balance disorders feel sick. They can ask their phone or computer to reduce motion. A page that honours that setting also skips the work of the animation:
const cup = document.querySelector("#cup"); document.querySelector("#add").addEventListener("click", () => { const calm = matchMedia("(prefers-reduced-motion: reduce)").matches; if (calm) { console.log("reduced motion: the cup stays still"); return; } cup.animate([{ transform: "translateX(0)" }, { transform: "translateX(120px)" }], 600); console.log("the cup slides across");});- Line 1 finds the cup. Line 3 runs the arrow function each time someone clicks Add tea.
- Line 4 asks
matchMediawhetherprefers-reduced-motion: reducematches. It does when the person turned on reduced motion in their settings. - If so, lines 6 and 7 print
reduced motion: the cup stays stilland stop. - Otherwise, line 9 slides the cup 120 pixels over 600 ms with
animate, and line 10 printsthe cup slides across.
Speed and accessibility aren't the same thing, but both aim for a page that works for everyone, on the devices and settings they really have. The next lesson, The performance inequality gap, looks at those devices. A later lesson, Performance and accessibility, goes deeper into screen readers and motion.
Who pays for the bytes
Every byte of a page is paid for twice: once by the site that sends it, and once by the visitor who receives it. Start with the visitor.
You take a taxi home, and the meter ticks up with every kilometre. One driver takes the long way round. You reach the same door, but the fare is higher. And you pay it, not the driver.
- In real life: The meter ticking up with every kilometre
- In web performance: Each byte counting against the visitor's data plan
- In real life: The long way round to the same door
- In web performance: A heavy file that looks the same as a light one
- In real life: You pay the fare, not the driver
- In web performance: The visitor pays for the bytes the site chose to send
A phone's data plan is that meter. The International Telecommunication Union (ITU), a United Nations agency, compares one kind of plan in every country: the cheapest mobile data plan with at least 5 GB a month. A gigabyte (GB) is a billion bytes. The ITU found:
- In 2025, that plan's median price was 1.4% of a person's average income. The median is the middle value: in half the countries, the plan costs a bigger share of income, and in half a smaller one.
- People in low-income countries spend about 22 times as large a share of their income on it as people in high-income countries.
So the same page costs some visitors far more than others. Here is how far a 5 GB plan goes:
const pageBytes = 2000000; // a 2 MB pageconst planBytes = 5 * 1000 ** 3; // a 5 GB monthly data plan const loads = Math.floor(planBytes / pageBytes);console.log("loads that use up the plan:", loads);- Line 1 is a 2 MB page: 2 million bytes. That's close to the median mobile page in the Web Almanac 2025, 2,164 KB.
- Line 2 is a 5 GB plan.
1000 ** 3means 1,000 × 1,000 × 1,000, a billion bytes. - Line 4 works out how many loads of the page fit in the plan.
Math.floordrops the part after the point. - Line 5 prints
loads that use up the plan: 2500.
Now the site's side. A site can send its files through a CDN, a content delivery network: servers around the world that send files from close to each visitor. Some CDNs charge for every gigabyte they send. Google's Cloud CDN is one.
const pageBytes = 2000000; // a 2 MB pageconst viewsPerMonth = 100000; const sentGB = (pageBytes * viewsPerMonth) / 1000 ** 3;console.log("GB the site sends each month:", sentGB);- Lines 1 and 2 are the same 2 MB page, viewed 100,000 times a month.
- Line 4 multiplies the bytes by the views, then divides by a billion to get gigabytes.
- Line 5 prints
GB the site sends each month: 200. Twice the bytes would mean twice the gigabytes, and twice the bill.
A heavy picture that looks the same
Ravi wants to see this on a real page. So the course loaded the tea shop from the last lesson twice, without its script:
- The light page uses the small picture of the mint tea.
- The heavy page uses the same picture, exported from a design tool with a "paper texture". The texture is 14,000 tiny dots, each one its own circle.
On screen, the two look almost the same. Both pages share this HTML:
<!doctype html><html lang="en"><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Leaf and Kettle</title> <link rel="stylesheet" href="styles.css"></head><body> <header>Leaf and Kettle</header> <main> <img id="hero" src="hero.svg" alt="A glass of mint tea" width="360" height="270"> <h1>Mint tea</h1> <p>Fresh mint leaves, picked this week. 120 grams.</p> <button id="buy">Buy</button> </main></body></html>Line 7 loads the styles. Line 12 is the picture, with its width and height. Only hero.svg differs between the two pages. Here is the heavy one, shortened:
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="270" viewBox="0 0 360 270"> <rect width="360" height="270" fill="#eef5ea"/> …<g fill="#3f6f31" opacity=".08"> <circle cx="0.1" cy="160.7" r="0.9"/> <circle cx="75.4" cy="65.1" r="0.9"/> <circle cx="279.2" cy="88.5" r="0.9"/> … 13,997 more dots like these …</g></svg>Line 4 starts a group of faint dots, and line 5 is the first of 14,000 circles. Each circle is only a few dozen bytes. Together, they make the file 130 times as heavy as the whole light page.
The course loaded each page 5 times in Chrome 156, slowed down like a phone, as in the last lesson. The numbers come from the middle load. You can open the light page and the heavy page yourself.
- Nothing drawn yet
0 ms to 1,228 msA blank screen

At 1,228 msThe text

At 1,241 msThe picture, a moment later
- Nothing drawn yet
0 ms to 1,219 msA blank screen

At 1,219 msThe text

At 4,718 msThe picture, seconds later
Each row is one thing the two loads measured:
| What was measured | Light page | Heavy page |
|---|---|---|
| Bytes over the network | 4 KB | 582 KB |
| hero.svg finished downloading | 1,203 ms | 4,406 ms |
| The text showed (FCP) | 1,228 ms | 1,220 ms |
| The picture showed (LCP) | 1,240 ms, good | 4,716 ms, poor |
| Loads that use up a 5 GB plan | 1,115,075 | 8,586 |
| Carbon per view, by the model | 0.0007 g CO2e | 0.086 g CO2e |
- Both pages showed their text at about the same time: 1,228 ms and 1,220 ms. The text needs only the HTML and the styles, and those are the same on both.
- The heavy picture took until 4,406 ms to download. Slow 4G sends only about 180 KB a second.
- Then the main thread spent 213 ms reading the 14,000 circles: a long task. So heavy bytes cost twice: the time to download them, then the work to read them.
- The main content showed at 4,716 ms instead of 1,240 ms. LCP went from good to poor, for a picture that looks almost the same.
Back to the taxi. Every visit to the heavy page runs Asha's meter up by 578 KB more. And she waits 3,476 ms longer for the tea she came for.
The recorder sent each file as it is. A server can compress text files such as SVG, which would shrink the heavy picture. It would still be far heavier than the light one. Lesson 16.1, gzip, Brotli, and Zstandard, covers compression.
Now measure what this lesson page cost you to load:
const [page] = performance.getEntriesByType("navigation");const files = performance.getEntriesByType("resource");const bytes = files.reduce((sum, file) => sum + file.transferSize, page.transferSize); showBytes(bytes);showPlanShare(bytes / (5 * 1000 ** 3));showCarbon(gramsPerView(bytes));Reading…
Reading what this tab downloaded…
In the panel's code:
- Line 1 reads this page's navigation entry, from the last lesson. Line 2 reads a resource entry for every other file the tab downloaded.
- Line 3 adds up their
transferSize: the bytes each one brought over the network. A file the browser already had saved reports 0. - Lines 5 to 7 show the total, its share of a 5 GB plan, and the carbon estimate from the next section.
Energy use and carbon
Bytes cost energy as well as money. Sending, carrying and showing them uses electricity: in data centres, in the network, and in the visitor's phone. Making all that equipment used energy too.
Making electricity from coal or gas gives off greenhouse gases. These are counted as CO2e, short for carbon dioxide equivalent. It turns every gas into the amount of carbon dioxide that would warm the planet as much.
Nobody can measure the carbon of one page view directly. So people estimate it with a model: a formula built from big averages. The Sustainable Web Design Model, version 4, is one made for websites.
The model takes all the energy the internet uses in a year, and divides it by all the data the internet carries. That gives energy per gigabyte. Energy is counted in kilowatt-hours (kWh). One kWh is what a 1,000-watt heater uses in an hour. Each row is one part of the system, in kWh for every GB:
| Part of the system | Using it | Making it |
|---|---|---|
| Data centers | 0.055 kWh | 0.012 kWh |
| Networks | 0.059 kWh | 0.013 kWh |
| User devices | 0.080 kWh | 0.081 kWh |
| All three | 0.194 kWh | 0.106 kWh |
const kWhPerGB = 0.194 + 0.106;const gramsPerKWh = 494; function gramsPerView(bytes) { return (bytes / 1000 ** 3) * kWhPerGB * gramsPerKWh;} console.log("a 2 MB page:", gramsPerView(2000000).toFixed(2), "g CO2e per view");console.log("100,000 views:", Math.round((gramsPerView(2000000) * 100000) / 1000), "kg CO2e");- Line 1 adds the energy for using the equipment, 0.194 kWh per GB, and for making it, 0.106 kWh per GB. That's 0.3 kWh per GB.
- Line 2 is how much CO2e one kWh of electricity gives off, as a world average: 494 grams. The model lets you use your own country's number instead.
- Line 5 turns bytes into gigabytes, then into kWh, then into grams.
- Line 8 prints
a 2 MB page: 0.30 g CO2e per view. Line 9 prints100,000 views: 30 kg CO2e.
For the tea shop, the model gives 0.0007 g for each view of the light page, and 0.086 g for the heavy one. One view is tiny. But a popular page is viewed millions of times, so every byte you remove is removed millions of times.
Treat these numbers as estimates. The model counts only bytes, not how hard a phone's CPU works. Its authors also say researchers still debate using data as the input. So use it to compare two versions of your own page, not to state an exact footprint. A later lesson, Performance and sustainability, goes deeper.
Check a claim before you quote it
Ravi is ready to answer the owner. The strongest case uses his own measurements, plus evidence he can defend. First, sort each claim: can you quote it as it is, only with its limits, or not at all?
- "Adding 400 ms to Google's results cut searches per user by 0.59%" (Brutlag, 2009).
- "A 1-second delay cut articles read by 4.9% in a week" (FT, 2016).
- "A 31% better LCP brought 8% more sales" (Vodafone's A/B test, 2021).
- "100 ms faster brought 0.7% more revenue per visit" (Zalando's A/B test, 2018).
- "Every 100 ms costs Amazon 1% of sales."
- "53% of mobile visits are abandoned after 3 seconds."
- "0.1 s faster means 8.4% more retail conversions" (Deloitte, 2020).
- "Passing Core Web Vitals puts a page at the top of Google."
- "Google lowered the good LCP limit to 2 seconds."
Sort each claim by what you can honestly say about it.
Here is Ravi's note to the owner, built only from what he measured and what he can quote:
"On a phone with Slow 4G, the textured picture moves our main content from 1,240 ms to 4,716 ms. That takes LCP from good to poor. It also makes every visit download 578 KB more. In Vodafone's 2021 A/B test, a 31% better LCP brought 8% more sales. Let's remove the texture, then run our own A/B test to see what it does for our sales."
A later lesson, The business case for performance, goes further.
Common mix-ups
- "Speed only matters for shops." No. Google measured fewer searches, and the FT fewer articles read. Speed also decides who can use a site.
- "A faster page always ranks higher on Google." No. The most relevant page comes first, and Google says good scores don't guarantee a top ranking.
- "A delay too small to notice can't matter." No. 100 ms cut Google searches, and the loss grew the longer people had a delay.
- "Fast visits convert more, so speed caused it." No. That's a correlation. Returning visitors, phones or countries can explain part of it. Only a fair test, with a random split, shows cause.
- "8% more sales means 8 percentage points more." No. In the first example, the rate went from 2.8% to 3.0%. That's 0.2 percentage points, yet the fast page got 8% more orders.
- "Bytes are free now." No. Visitors pay for them through their data plans, sites can pay to send them, and every byte uses energy.
Two kinds of evidence are easy to mix up. A correlation study compares the visits you already have. An experiment decides who gets which speed, before the visits happen. Each row asks both the same question:
| Question | Correlation study | Experiment |
|---|---|---|
| Who decides which visits are fast and which are slow? | Nobody. Each visit just happened to be fast or slow. | The company, before the visit. A fair test decides by chance, like a coin flip. |
| What else can differ between the groups? | Anything: new or returning visitors, phones, countries, reasons for coming. | Nothing on purpose. Groups picked by chance get about the same mix of people. |
| What can it show? | That speed and results go together. | That speed changed the results. |
| Examples in this lesson | Deloitte's 2020 study, the 53% figure, and the split playground. | Google in 2009, the FT in 2016, and Zalando's and Vodafone's A/B tests. |
Practice exercises
Answer first, then use the hints.
A shop had 1,250 visits and 50 orders. What does this code print?
const visits = 1250;
const orders = 50;
console.log((orders / visits) * 100 + "%");It prints 4%. 50 orders from 1,250 visits is 0.04, and 0.04 times 100 is 4.
A faster page lifts the conversion rate from 2% to 3%. By what percent did the rate grow, as a relative change?
const before = 2; // percent of visits that ordered
const after = 3;
console.log("change in points:", after - before);
console.log("change in percent:", ((after - before) / before) * 100);50%. The rate went up 1 percentage point, from 2% to 3%. That's half as much again as before: a relative change of 50%. A headline would say "50% more orders".
This joins the rating and status examples, for another URL group on line 10. What status does the group get?
const limits = { lcp: [2500, 4000], inp: [200, 500], cls: [0.1, 0.25] };
function rate(metric, value) {
const [good, poor] = limits[metric];
if (value <= good) return "good";
if (value <= poor) return "needs improvement";
return "poor";
}
const ratings = [rate("lcp", 3200), rate("inp", 150), rate("cls", 0.05)];
let status = "good";
if (ratings.includes("needs improvement")) status = "needs improvement";
if (ratings.includes("poor")) status = "poor";
console.log("the group's status:", status);Needs improvement. LCP of 3,200 ms is over the 2,500 ms limit for good, but under 4,000 ms. INP of 150 ms and CLS of 0.05 are good. The worst of the three decides, so it prints the group's status: needs improvement.
These are the split playground's visits, printed one group at a time. For each kind of visitor, does speed change how often people order?
const visits = [
{ who: "returning", fast: true, count: 200, orders: 12 },
{ who: "returning", fast: false, count: 50, orders: 3 },
{ who: "new", fast: true, count: 100, orders: 2 },
{ who: "new", fast: false, count: 800, orders: 16 },
];
for (const group of visits) {
const rate = (group.orders / group.count) * 100;
console.log(`${group.who}, ${group.fast ? "fast" : "slow"}: ${rate}%`);
}No. Returning visitors order 6% of the time, fast or slow, and new visitors 2%. In these made-up visits, the gap between fast and slow visits came entirely from who the visitors were. Real data won't always be this clean. That's why a fair test splits visitors by chance.
A 5 MB page is viewed 1,000,000 times a month. By the Sustainable Web Design Model, how many kilograms of CO2e is that a month?
const kWhPerGB = 0.194 + 0.106;
const gramsPerKWh = 494;
function gramsPerView(bytes) {
return (bytes / 1000 ** 3) * kWhPerGB * gramsPerKWh;
}
// A 5 MB page, viewed 1,000,000 times a month: how many kg of CO2e?
console.log(gramsPerView(5000000));const kWhPerGB = 0.194 + 0.106;
const gramsPerKWh = 494;
function gramsPerView(bytes) {
return (bytes / 1000 ** 3) * kWhPerGB * gramsPerKWh;
}
const grams = gramsPerView(5000000) * 1000000;
console.log(Math.round(grams / 1000), "kg CO2e a month");About 741 kg of CO2e a month. One view of a 5 MB page is 0.005 GB, times 0.3 kWh, times 494 g: 0.741 g. A million views is 741,000 g, which is 741 kg. Remember, it's the model's estimate, not a measurement.
Open the heavy tea shop page in Chrome. Open DevTools, go to the Network panel, and reload the page. In the Waterfall column, which file starts downloading at about the same moment as styles.css?
hero.svg. The browser found both files in the HTML and asked for them together. In the recorded load, styles.css started at 600 ms and hero.svg at 601 ms. So the picture wasn't late to start. It was late to finish, at 4,406 ms, because it had so many bytes to bring.
Check your understanding
For each question, ask two things. Is the evidence a fair test or a correlation? And who pays for the speed or the bytes?
Question 1 of 7What does the last line of the extra-orders example print?
Read the code, then predictconst fastOrders = 1020; const slowOrders = 944; // from the same 34,000 visits const extra = fastOrders - slowOrders; const change = extra / slowOrders; console.log("extra orders:", extra); console.log("the fast page sold", Math.round(change * 100) + "% more");Choose an answer to see the explanation.
Question 2 of 7An analytics report shows that visits loading in 1 second ordered three times as often as visits loading in 5 seconds. What does it prove?
Choose an answer to see the explanation.
Question 3 of 7Why do delay experiments, like Google's in 2009 and the FT's in 2016, show that speed itself changes what people do?
Choose an answer to see the explanation.
Question 4 of 7What does Google's page experience documentation say about Core Web Vitals and ranking?
Choose an answer to see the explanation.
Question 5 of 7What does the last line of the rating example print?
Read the code, then predictconst limits = { lcp: [2500, 4000], inp: [200, 500], cls: [0.1, 0.25] }; function rate(metric, value) { const [good, poor] = limits[metric]; if (value <= good) return "good"; if (value <= poor) return "needs improvement"; return "poor"; } console.log("LCP 2100 ms:", rate("lcp", 2100)); console.log("INP 180 ms:", rate("inp", 180)); console.log("CLS 0.3:", rate("cls", 0.3));Choose an answer to see the explanation.
Question 6 of 7In the recorded tea shop, the heavy page's text showed about as early as the light page's, but its picture came seconds later. Why?
Choose an answer to see the explanation.
Question 7 of 7By the Sustainable Web Design Model, what happens to a page's carbon estimate if you halve its bytes?
Choose an answer to see the explanation.
Key takeaways
- Speed changes what people do. Slower pages got fewer searches (Google, 2009) and fewer articles read (FT, 2016). Faster pages brought more revenue and sales (Zalando, 2018; Vodafone, 2021).
- Conversion rate, bounce rate and pages per visit are the numbers speed moves. Tell percentage points from relative change.
- Comparing fast and slow visits shows a correlation. Only a fair test, which splits visitors by chance, shows that speed caused a difference.
- Many famous numbers are old or unsourced. Quote experiments with their source and year, and anything else with its limits.
- Google's ranking systems use Core Web Vitals, but the most relevant page comes first, and good scores guarantee nothing. Search Console rates them at the 75th percentile of real Chrome visits.
- Slow pages hit hardest the people with older devices, assistive tools or expensive data. Every byte costs the visitor, the site and the planet.
Remember the one-liner.
A faster page gets more searches, reads and sales, reaches more people, and costs less to send. Good evidence comes from fair tests, not from comparing fast and slow visits.
Sources
The facts on this page come from these studies, specifications and docs:
- Vodafone: A 31% improvement in LCP increased sales by 8% (2021), web.dev.
- [GA4] Engagement rate and bounce rate, Google Analytics Help.
- Speed Matters for Google Web Search (2009), Jake Brutlag, Google.
- A faster FT.com (2016), Matt Chadburn and Gadi Lahav, FT Product & Technology.
- How Zalando's overall site speed improved by more than 25% in five months (2018), Zalando Engineering.
- 100ms latency costs 1% revenue? (2018), Niels-Ole Kühl.
- The need for mobile speed (2016), Google.
- Milliseconds Make Millions (2020), Deloitte Ireland.
- Milliseconds Make Millions (2020), web.dev, summarizing Deloitte's study for Google.
- Understanding page experience in Google Search results, Google Search Central.
- Using page speed in mobile search ranking (2018), Google Search Central blog.
- Core Web Vitals report, Search Console Help.
- Web Vitals, web.dev.
- Performance, Web Almanac 2025, HTTP Archive.
- Americans with disabilities less likely than those without to own some digital devices (2021), Pew Research Center.
- Blink accessibility, Chromium source documentation.
- prefers-reduced-motion, MDN.
- Facts and Figures 2025: Affordability of ICT services, International Telecommunication Union (ITU).
- Facts and Figures 2025: Methodology, International Telecommunication Union (ITU).
- Page Weight, Web Almanac 2025, HTTP Archive.
- Cloud CDN pricing, Google Cloud.
- Resource Timing, W3C.
- Estimating digital emissions (the Sustainable Web Design Model, version 4), Sustainable Web Design.
- CO2.js, the model in code, Green Web Foundation.
Coming next: The performance inequality gap. It looks at the phones and networks most people really have, and the budget for bytes and JavaScript that follows from them.