The waterfall chart is the richest diagnostic tool available to anyone working on performance. It shows every request the page makes as a horizontal bar on a time axis. At first glance it looks like a tangle, but with a little practice you can read at a glance the points where loading gets stuck. Here I use the Chrome DevTools Network panel as a reference, but the concepts apply to any tool that produces a waterfall.
Preparing the recording
Before looking at the chart it is worth setting sensible conditions:
- tick «Disable cache» to simulate a first visit;
- choose a network throttling profile, because on fibre everything looks instant and dependencies hide;
- reload the page with the panel already open;
- add the useful columns: priority, protocol, transferred size and resource size.
Anatomy of a bar
Hovering over a request's bar shows the timing breakdown. The main phases are:
| Phase | What it means |
|---|---|
| Queueing | the browser has discovered the resource but is waiting to request it |
| Stalled | waiting before sending, for example because of connection limits |
| DNS lookup | resolving the domain name |
| Initial connection and SSL | TCP handshake and TLS negotiation |
| Request sent | sending the request, usually negligible |
| Waiting (TTFB) | time the server takes to respond with the first byte |
| Content download | transfer of the response body |
A bar dominated by waiting indicates a server that is slow to process. A bar dominated by download indicates a heavy file or limited bandwidth. A long queue indicates that the resource was discovered early but given low priority, or that the browser was busy with something else.
The vertical lines
The chart also shows some vertical markers, typically the DOMContentLoaded event and the load event. They are useful for orientation but do not measure what the user sees. It is more meaningful to compare the waterfall with the filmstrip: in the Performance panel you can see the screenshots next to the network timeline and understand which request unblocked the first paint.
Patterns that signal a problem
The staircase
A series of bars each starting only when the previous one has finished, forming a descending staircase, indicates a dependency chain. Typical example: the HTML loads a CSS file, the CSS uses @import to pull in a second CSS file, and the second declares a font. Each step adds a full round trip to the server. The solutions are to flatten the chain (no @import, resources declared directly in the HTML) or to bring discovery forward with a preload.
The gap
An empty space in which no request is active, followed by a burst of downloads, indicates that the browser was executing JavaScript or parsing something before discovering new resources. It is common in applications that render on the client: the HTML is almost empty, the bundle is awaited and executed, and only then do the requests for data and images start.
Repeated connections
If many bars show DNS, connection and SSL, the page is contacting too many different domains. Each new domain costs at least one round trip for the connection and one or more for TLS. Reducing third-party domains, or using preconnect for the indispensable ones, lowers this cost.
The important resource at the bottom
If the main image or the heading font appear among the last requests, with low priority, the browser did not consider them important. This often happens because they are declared in CSS or inserted by JavaScript. Moving them into the HTML and setting fetchpriority="high" on the main image moves them up.
Reading the Initiator column
The «Initiator» column tells you which file triggered each request. Holding the Shift key while hovering over a row, DevTools highlights in green what started it and in red the requests it in turn generated. It is the quickest way to reconstruct a chain and discover, for example, that a seemingly harmless chat widget downloads ten files of its own from three domains.
Transferred size and actual size
Comparing the two columns helps you see whether compression works. A 400 KB JavaScript file transferred as 390 KB is not compressed; with Brotli or gzip a text file usually shrinks to a fraction of its original size. Likewise, «(disk cache)» or «(memory cache)» in the size column confirms that the browser cache is doing its job on later visits.
A five-step method
- Identify the LCP element and find its request in the waterfall.
- Follow the chain of initiators back to the HTML.
- Count how many round trips separate the HTML from that resource.
- Remove the unnecessary links or bring discovery forward.
- Repeat the recording and compare.
In short
- Each bar breaks down into queueing, connection, server wait and download.
- Staircases indicate dependency chains to flatten.
- Gaps reveal JavaScript delaying resource discovery.
- Too many handshakes signal too many different domains.
- The Initiator column and the size comparison clarify causes and compression.