Performance

Testing on simulated 3G: what actually changes

With 400 ms of RTT and 1.6 Mbps the home page goes from LCP 708-1,360 ms to 2,672 ms over 340 KB in 24 requests. Bandwidth is the bottleneck.

Key points

  • With 400 ms of RTT and 1.6 Mbps the TTFB stays at 335 ms: the rest of the page is what slows down.
  • LCP moves from 708-1,360 ms unthrottled to 2,672 ms on simulated 3G.
  • The page transfers 340 KB over 24 requests, nearly all of them needed for the first screen.
  • Simulated 3G measures the network, not the CPU: without CPU throttling the number stays optimistic.

A simulated 3G test throttles the network and latency inside the browser, not the router: with 400 ms of RTT and 1.6 Mbps our home page moves from an LCP of 708-1,360 ms to 2,672 ms, while TTFB stays at 335 ms. The server is not involved: the 340 KB across 24 requests are what weigh.

Three profiles, three answers

ProfileRTTBandwidthLCPLoad
Unthrottled0 mslocal network708-1,360 ms435-1,084 ms
Simulated 3G400 ms1.6 Mbps2,672-2,676 ms2,424-3,178 ms
Fast network20 ms8 Mbps752-780 ms553-644 ms

TTFB barely moves between profiles: 335 ms against 352 ms, against 319-529 ms unthrottled. When TTFB holds and LCP triples, the time goes after the first byte, downloading CSS, JavaScript and images. That is the most useful diagnosis this test produces.

The command

const cdp = await page.createCDPSession();
await cdp.send("Network.enable");
await cdp.send("Network.emulateNetworkConditions", {
  offline: false,
  latency: 400,
  downloadThroughput: 200 * 1024,
  uploadThroughput: 100 * 1024,
  connectionType: "cellular3g",
});
await page.goto("https://example.com/", { waitUntil: "load" });

The code needs Puppeteer, Chrome and a page already open in page. downloadThroughput is in bytes per second, not bits: 200 × 1024 corresponds to 1.6 Mbps. You can set the same conditions by hand in DevTools, under Network, with the "Slow 3G" profile.

What the test does not say

Network throttling does not slow the processor, so a page that spends 1.2 s of JavaScript hydrating looks faster than it is on a real phone. Lighthouse mobile adds a four-times CPU slowdown on top of the network limit, and uses 150 ms of RTT instead of 400: its 3G is gentler on latency and harsher on the processor. A budget needs both, not one of the two.

The limitation to name

Two runs per profile is a small sample, on one machine and one network: absolute values move by hundreds of milliseconds. Real 3G has no constant bandwidth either, and packet queues change with the cell. The test compares two versions of the site under the same conditions; it does not predict one user's time.

Where to continue

The EneUI catalogue indexes 111,985 components, the hero blocks and the record scroll expansion hero, a hero that pays for every image on 3G. To see what must not load at once read lazy loading: when it makes things worse; to split the JavaScript, splitting the bundle; for the first byte, TTFB: what it measures; and for the comparison between architectures, frontend frameworks compared.

Frequently asked questions

How does simulated 3G differ from real 3G?

Simulated has constant bandwidth and latency, real does not: the cell changes, packets queue and bandwidth swings. Simulated compares two versions under the same conditions; it does not predict a single user.

Why does TTFB not get worse with 400 ms of RTT?

Because TTFB measures the first byte of the main response, after a handshake that stays short here. The network time is paid later, downloading the page assets.

Which values does Lighthouse use for 3G?

Lighthouse mobile simulates 1.6 Mbps down, 750 kbps up, 150 ms of RTT and a processor slowed four times. Our test uses 400 ms of RTT to sit closer to a real mobile network.

How much does the CPU matter in a mobile test?

A lot, when the page hydrates plenty of JavaScript. Without a CPU slowdown the test measures only the network and blames bandwidth when the processor is the cause.

How do I use the test in CI?

Fix a budget on LCP measured under the same network profile and compare versions. A network test in CI is slow and noisy: a budget on asset weight plus one manual test before release works better.