Lighthouse and Core Web Vitals reports tell you what is slow; the Performance panel in Chrome DevTools tells you why. It records everything the browser does over a span of time, from network requests to the execution of every JavaScript function, from style calculation to painting pixels. At first sight the screen is intimidating, but learning to read just a few parts of it is enough to diagnose most problems.
Preparing a reliable recording
A useful measurement needs a few precautions:
- an incognito window or clean profile: browser extensions run code in the page and pollute the profile;
- CPU throttling: in the panel settings you can slow the CPU down 4 or 6 times to approximate a mid-range phone. A problem invisible on a powerful laptop becomes obvious;
- network throttling, if you are studying loading;
- screenshots enabled, to see what appears on screen at each moment.
To analyse loading, use the button that reloads the page and records until it finishes. For an interaction, start recording, perform the action, for example opening a menu, and stop right after: short recordings are much easier to read.
The main tracks
The profile is organized in horizontal rows, called tracks, sharing the same timeline:
| Track | What it shows |
|---|---|
| Screenshots | Page thumbnails over time |
| Network | Requests, like a compact waterfall |
| Frames | Frames produced and frames dropped |
| Interactions | User interactions and their duration |
| Timings | Markers such as FCP, LCP and custom measures |
| Main | All main-thread work |
| Workers and other threads | Work done off the main thread |
The Main track is where you spend most of your time.
Reading the flame chart
In the Main track, work is drawn as a flame chart: each bar is an activity, its width is the duration and the bars beneath it are the functions called by the one above. At the top are tasks, the browser's units of work; below are events, functions and sub-functions.
Tasks longer than 50 milliseconds have a red triangle in the corner and a hatched area highlighting the excess. They are the first to look at, because during a long task the page cannot respond to the user.
Colours help you find your way: yellow for scripting, purple for style calculation and layout, green for painting and compositing. A task that is mostly purple points to a CSS or DOM problem, not a JavaScript one.
From task to cause
The method I use in my work is always the same:
- I find the long task closest to the problem, for example right after the click in the Interactions track;
- I select it and read the Summary pane, which splits the time by category;
- I go down the flame chart to the widest bars at the bottom: those are the functions really consuming the time;
- I select the function and, in the detail pane, find the file and line, clickable to open the source;
- if it is a third-party script, the domain name gives it away immediately.
For an aggregate view, use the tabs at the bottom:
- Bottom-Up lists functions sorted by self time, that is excluding sub-calls: it reveals the functions that cost the most wherever they are called from;
- Call Tree shows the call tree from the top, useful for understanding which event handler started the work;
- Event Log lists activities in chronological order.
In Bottom-Up you can also group by domain or by file, getting each third party's share of time in one go.
Patterns to recognize
Some patterns come up often:
- many purple bars alternating with yellow ones in a loop: forced reflows, i.e. reads of DOM geometry after changes. DevTools flags them with a warning on the layout entry;
- a single huge yellow block at startup: evaluation of a very large JavaScript bundle or a framework's hydration;
- long Recalculate Style bars after a click: a class change on an element high in the hierarchy forcing styles to be recalculated for the whole page;
- frequent garbage collection work: excessive creation of temporary objects, sometimes a sign of a memory leak.
Custom measures
To follow specific portions of your own code, the User Timing API lets you create markers that appear in the Timings track:
performance.mark('filter-start');
applyFilters();
performance.mark('filter-end');
performance.measure('Applying filters', 'filter-start', 'filter-end');
The measure appears as a bar with its own name, aligned with main-thread work, and it can also be collected from real users with a PerformanceObserver.
Saving and sharing
A profile can be exported to a file and reopened later, or sent to a colleague. It is the best way to document a problem: instead of "the menu is slow", you hand over the recording with the offending task already identified.
In short
- Record in a clean profile, with CPU throttling and short recordings.
- The Main track shows main-thread work as a flame chart.
- Tasks with the red triangle are the first to analyse.
- Go down to the widest bars at the bottom and open the file and line from the detail pane.
- Use Bottom-Up for the costliest functions and User Timing for custom measures.