Performance and debugging · 8 / 8
lesson 8

Profiling

Record what the browser was doing during a slow moment, read the flamegraph, find the slow path.

~ 14 min read·lesson 8 of 8
0 / 8

The page hitches when you scroll past a certain point. Or animation drops frames during the first second. Or clicking a button takes 800ms before anything happens. You can stare at the code, guess at the cause, change something, and reload — but you'd be guessing. The Performance panel takes the guesswork out: record the slow moment, then read what the browser was actually doing.

This is the densest panel in devtools. The first time you open it, the recording looks like a garbage dump of bars and labels. By the end of this lesson it should look like a story — one that tells you exactly which function ate the budget and what called it.

Recording a session

Open Performance. The two buttons that matter:

  • Record (the dot, or Ctrl+E) — start capturing.
  • Reload (the curved arrow) — start capturing and reload the page in one click. The right choice when you want to profile the page load itself.

After you click record, do the slow thing — scroll, click, type, whatever produces the jank. Then click stop. The panel fills with a multi-section view of what happened during the recording.

A useful discipline: keep recordings short. A 30-second recording is hard to read; a 3-second one focused on the actual slow moment is easy. Profile a single interaction at a time.

The top of the result has a Summary strip showing what the browser was busy with: yellow for scripting, purple for rendering and layout, green for painting, blue for parsing, idle in light gray. A glance at this strip tells you the shape of the problem before you read any details:

  • Lots of yellow → JavaScript is the bottleneck.
  • Lots of purple → layout/style recalculation is dominating.
  • Lots of green → painting (often big repaints from poorly-batched changes).
  • Idle → the browser was waiting (network, paint).
Tip

Open the Web Vitals checkbox at the top of the Performance panel before you record. It overlays markers on the timeline showing exactly when LCP fired, when interactions happened, when layout shifts hit. Ties the abstract metric to the concrete recording.

Reading a flamegraph

The main view is a flamegraph (sometimes labelled "Main"). It's a stacked bar chart of function calls over time, but rotated and styled in a specific way:

  • The X axis is time. Moving right is later.
  • The Y axis is call depth. A bar above another bar means the upper bar called the lower bar.

Wait — upper called lower? Yes. Devtools shows the root of the call (the event handler, the rendering callback) at the top, and what it called underneath. Most other tools draw flamegraphs the opposite way (called function on top of caller); Chrome flips it. Once you know, the rest is straightforward.

So you read it top-down: the top bar is the root cause (a click event, a timer fired, a resize), and the bars beneath show what that did, layer by layer, until the bottom bars are leaf functions doing actual work.

Width = time spent. A bar that takes up a third of the screen took a third of the recording. That's where the cost lives.

A practical reading workflow:

  1. Spot the wide region (the part of the timeline where the page was slow).
  2. Find the top-most bar in that region — usually a Task, Event (click), Timer Fired, or similar.
  3. Drill down: each layer below shows what was called. Keep going until you find a wide bar with a recognizable function name from your code.
  4. Click the bar — the panel below opens that function in Sources.
click handlervalidaterenderResultsbuildRow × 2,000sortBigList ← slow time →
A flamegraph: top bars are roots (events), bars beneath are what they called. Width is time spent.
check your understanding
You're staring at a flamegraph. Two bars are stacked vertically. The bar on top is wider; the bar beneath it is narrower. What's the relationship?

The frame chart — finding dropped frames

Above the flamegraph there's a row labelled Frames, with little colored squares — one per rendered frame. Green is good (the frame painted within budget). Yellow is a "partially presented" frame. Red is a dropped frame.

If the page should run at 60fps, each frame has 16.67ms to do everything. The Frames row makes it obvious which frames blew the budget — the red ones. Click any red square to zoom the timeline to exactly that frame's time window. Now the flamegraph below shows only what was running during that one slow frame.

This is the highest-leverage move in the whole panel. Instead of reading a 3-second flamegraph and guessing where the slow part is, you click the red frame and read the 50ms flamegraph that contains exactly the work that made that frame drop.

Watch out

A single yellow or red frame is normal — every page drops one occasionally. A long red streak is the signal. If 30 frames in a row are red, you have a sustained jank problem, not a one-off blip.

Bottom-Up and Call Tree

The flamegraph shows time left-to-right. Sometimes you don't care when a function ran — you care which function used the most total time across the whole recording, even if it ran in tiny slices spread out.

Below the flamegraph are tabs: Summary, Bottom-Up, Call Tree, Event Log.

Bottom-Up is the answer to "which leaf functions ate the most time, totalled across the recording?" It lists every function that ran, sorted by self-time (time spent in that function alone, excluding what it called). Click a row to see who called it.

This view finds the function that individually is slow. If a tiny utility was called 5,000 times for 0.4ms each, Bottom-Up makes the 2-second total visible — even though no single call would catch your eye in the flamegraph.

Call Tree is the same data with the inverse perspective: roots at the top, drilled down. It's useful for "what does this event handler add up to?" — pick a root and see the totals down the call chain.

A useful workflow: Frames → red frame → Flamegraph to find a specific slow moment. Bottom-Up to find functions that are slow in aggregate across the whole session. They answer different questions; both views earn their place.

check your understanding
You record 3 seconds of scrolling and the flamegraph looks busy but no single bar is unusually wide. The page does feel janky. Which view should you switch to?

Profile under realistic conditions

A profile on your dev machine, with no other tabs and no other apps, is useful but optimistic. Real users have slower CPUs, slower networks, and lots of background work.

The Performance panel has two throttling controls at the top of the recording sidebar:

  • CPU throttling4× slowdown and 6× slowdown simulate slower devices. A function that takes 10ms on your laptop takes 40ms or 60ms on a low-end phone.
  • Network throttlingSlow 4G, Fast 4G, custom profiles. Affects the load-time recording.

For interaction profiling (the slow click, the janky scroll), turn on 4× slowdown before recording. The flamegraph stretches to show what the work actually costs on a slower device. A 5ms function that wasn't worth optimizing becomes a 20ms function that drops a frame.

A good default before any recording: open an incognito window, throttle to 4× slowdown, and record. If the page is fine in those conditions, it's fine for most users. If it's slow even there, you have a real problem to fix.

check your understanding
You record a click that triggers a 200-row table re-render. The flamegraph shows one wide bar at the top labelled (anonymous), with hundreds of identical narrow bars beneath labelled renderRow. What's the most likely interpretation?
check your understanding
You're profiling on a fast desktop. The flamegraph looks clean — every frame green. Should you ship?
check your understanding
You see one red frame in the Frames row at the moment the user clicked a button. The Performance panel timeline is 5 seconds long. What's the fastest way to focus on what made that single frame drop?
← prevfinish course →
KeepLearningcertificate
for completing
Performance and debugging
0 of 8 read