DEBUGGING WEB PERFORMANCE

jackf.io

A tunnel and lie-fi...

No signal

Underground

It's just nice when a site feels nice.

DEBUGGING WEB PERFORMANCE

jackf.io

Largest Contentful Paint (LCP) is an important, stable Core Web Vital metric for measuring perceived load speed because it marks the point in the page load timeline when the page's main content has likely loaded—a fast LCP helps reassure the user that the page is useful.

 

1: Remember what problem you are trying to solve.

2: Look for potential problems that DevTools is showing you.

3: Verify & investigate each one.

LCP happens when the dark blue image has been painted

But sometimes you have to apply nuance

Now we hunt for clues

No network activity + lots of activity on the main thread

Requests start again after the JavaScript is finished

Lots of main thread JavaScript after this request is completed

fetch heavy-processing.js
Evaluate Script (aka "run your JavaScript")

But the browser needs time to paint, too!

1. Fetch the JavaScript file

2. Execute the JavaScript

3. Finally! A chance to paint to the user.

Issue 1: heavy-processing.js blocks the main thread

We load Roboto-Bold.ttf here

We also have some more heavy JavaScript

And here is when our text is rendered onto the page and we call it "done"

Issue 1: heavy-processing.js blocks the main thread

Issue 2: our page's font is loaded very late

Issue 1: heavy-processing.js blocks the main thread

Issue 2: our page's font is loaded very late

Issue 3: our page's font is discovered via main.css

Issue 4: our server's response time to serve the font is poor

TTFB

How quickly did the server respond?

Render delay

The browser was busy so could not render the image.

Issue 1: heavy-processing.js blocks the main thread

Issue 2: our page's font is loaded very late

Issue 3: our page's font is discovered via main.css

Issue 4: our server's response time to serve the font is poor

Issue 4: our server's response time is poor

Issue 1: heavy-processing.js blocks the main thread

<script src="js/heavy-processing.js"></script>
<script src="js/heavy-processing.js" defer></script>

Diving into the flame chart

function planHoliday() {
  bookHotel();
  bookTrains();
}

function bookHotel() {
  const nights = calculateNights();
  book(nights)
}

function bookTrains() {

}
planHoliday()
bookHotel()
bookTrains()
calculateNights()
book(n...
time

A sampling profiler

your program running over time
sample
sample
sample
sample
planHoliday()
bookHotel()
bookTrains()
calculateNights()
book(nights)
time
planHoliday()
bookHotel()
bookTrains()
calculateNights()
book(nights)
time
planHoliday()
book(nights)

1: Do we need it at all?

2: Do we need it now?

// Also run some operations immediately (before DOMContentLoaded)
console.log("🔥 Starting immediate heavy processing...");
heavyDataProcessing();
heavySyncOperations();

// Add even more blocking operations before page render
console.log("🔥 Additional blocking operations...");
performExtraBlockingWork();
requestIdleCallback(() => {
  // Also run some operations immediately (before DOMContentLoaded)
  console.log("🔥 Starting immediate heavy processing...");
  heavyDataProcessing();
  heavySyncOperations();

  // Add even more blocking operations before page render
  console.log("🔥 Additional blocking operations...");
  performExtraBlockingWork();
});  

Issue 1: heavy-processing.js blocks the main thread

Issue 2: our page's font is loaded very late

Issue 3: our page's font is discovered via main.css

Issue 4: our server's response time is poor

localhost:3000/home
main.css
Roboto-Bold.ttf
localhost:3000/home
main.css
Roboto-Bold.ttf
<link
  rel="preload"
  href="assets/Roboto-Bold.ttf"
  as="font"
  type="font/ttf"
  crossorigin
/>

The DOMContentLoaded event fires when the HTML document has been completely parsed, and all deferred scripts have downloaded and executed. It doesn't wait for other things like images, subframes, and async scripts to finish loading.

 

The load event is fired when the whole page has loaded, including all dependent resources such as stylesheets, scripts, iframes, and images, except those that are loaded lazily. This is in contrast to DOMContentLoaded, which is fired as soon as the page DOM has been loaded, without waiting for resources to finish loading.

Expensive JS still runs but after the initial load.

Issue 1: heavy-processing.js blocks the main thread

Issue 2: our page's font is loaded very late

Issue 3: our page's font is discovered via main.css

Issue 4: our server's response time is poor

Issue 1: heavy-processing.js blocks the main thread

Issue 2: our page's font is loaded very late

Issue 3: our page's font is discovered via main.css

Issue 4: our server's response time is poor

Performant user interactions

Chrome usage data shows that 90% of a user's time on a page is spent after it loads, Thus, careful measurement of responsiveness throughout the page lifecycle is important. This is what the INP metric assesses.

Good responsiveness means that a page responds quickly to interactions. When a page responds to an interaction, the browser presents visual feedback in the next frame that it paints.

Reproducing what your users see

1: What screen size are most of your users using?

2: What device (e.g. how powerful?) are your users using?

3: What network connection do they have?

Recreating issues on your machine

(You can't)

Incognito mode can help avoid extension overhead

1: Remember what problem you are trying to solve.

2: Look for potential problems that DevTools is showing you.

3: Verify & investigate each one.

4: Make sure your environment is as accurate as it can be.

5: Apply your own wisdom and knowledge; performance is full of nuance.

6: You can make trade-offs and you don't have to fix everything.

jackf.io

Thank you!