Frontend Performance/Monitoring
Lesson 10 of 20 · Episode 10

Profiling with Chrome DevTools

Recording a performance trace, reading the flame chart, and hunting down long tasks that block interaction.

DevToolsFlame chartLong tasks
Watch on YouTube ↗

RUM finds the slow segment; WebPageTest shows the symptoms. To find the line of code at fault, you record a trace in the Chrome DevTools Performance panel. It looks intimidating, but it's really just a few linked views — and once you can read the flame graph, you can catch the classic killer: a forced reflow.

Orientation

The panel's linked views

Don't be scared by the wall of color. The Performance panel is a stack of time-aligned tracks — scroll/zoom one and they all move together:

The colour convention
Throughout the Performance panel, red means “problem here” — a long task, a dropped frame, a forced layout. Hunt the red.
The key skill

Reading the main-thread flame graph

The flame graph reads top-down: a task at the top calls functions below it, each bar a call in the stack. Wider = longer. A task over 50 ms gets flagged as a long task (the red corner). Click into the stack to find who's eating the time — and hover any frame to see whether it's scripting or rendering:

InteractiveA 93 ms long task, drilled into
46%
42%
🔴 Long task · 93ms
Layout (Forced) ⚠Rendering36 ms
Forced synchronous layout — the smoking gun. Reading offsetTop after a write forced this.
Scripting Rendering Painting System
Task → Animation Frame Fired → app.update() → Recalculate Style + Layout (Forced). The red-bordered 'Layout (Forced)' is the real culprit.
The smoking gun

The classic bug: forced reflow

That “Layout (Forced)” entry is the most common surprise in real traces — layout thrashing. It happens when you write a style and then immediately read a layout property (like offsetTop, offsetHeight, getBoundingClientRect()) in the same loop. The read can't be answered until layout is up to date, so the browser is forced to re-run layout synchronously — once per iteration. Toggle the fix:

InteractiveRead-after-write forces a layout every iteration
What the main thread does, per image:
writeread → 🔴 layoutwriteread → 🔴 layoutwriteread → 🔴 layoutwriteread → 🔴 layoutwriteread → 🔴 layoutwriteread → 🔴 layout
Forced reflows / frame
6
Frame health
Janky
❌ Forces a layout every iteration
for (const img of images) {
  img.style.top = top + "px";   // WRITE
  top = img.offsetTop + 3;       // READ → forced synchronous layout!
}
Reading offsetTop right after writing top forces the browser to recalculate layout immediately — for every image, every frame. That's the long task you saw in the flame graph.
Naïve: write → read → forced layout, ×N. Optimized: compute from a local variable, write only, and let the browser lay out once. Same logic, no jank.
The rule of thumb
Batch your reads and writes. Read all the layout values you need first, then do all your writes — never interleave them in a loop. Or, as in the demo, compute from a local variable so you never read the DOM back at all.
In practice

How to record a trace

Two ways to record

Record (then interact, then stop) captures a specific interaction — perfect for debugging a janky scroll or click. Start profiling and reload captures the whole page load from the first byte, and adds a Timings track with DCL, FP, FCP, and LCP marked.

Reproduce your users' conditions

Before recording, set CPU throttling (4×/6× slower to mimic a cheap phone) and network throttling (Slow 3G). A bug that's invisible on your M-series laptop is glaring at 6× CPU — which is exactly what your real users feel. This is how you turn the RUM finding (“mobile users in India are slow”) into a reproducible trace.

Q1Multiple choice
In the Performance panel flame graph, a wider bar means…
Q2Multiple choice
What triggers a “forced synchronous layout” (forced reflow)?
Q3Multiple choice
What's the fix for the layout-thrashing loop?
Q4Multiple choice
You can't reproduce a user's slowness on your fast laptop. What should you do first?
Key takeaways
  • The Performance panel is time-aligned tracks: Summary, CPU/Frames, Interactions, Main thread.
  • The flame graph shows tasks + call stacks; wider = longer; red = problem (long task).
  • Forced reflow = reading a layout prop right after a write — the classic jank bug.
  • Fix it by batching reads and writes (or caching in a local variable).
  • Record with CPU + network throttling to reproduce real users' conditions.
← Previous
9. Synthetic Monitoring — WebPageTest
Next →
11. Bundle Splitting