4.1 A page is not a file
This is the single shift in thinking the module asks for. The index.html you author is the root of a tree, not the whole thing. Demo Company makes the tree visible by building it one branch at a time: the home page needs only itself and a favicon; the image page adds a .webp; the CSS page adds a stylesheet; the font page adds a font discovered inside that stylesheet; the JavaScript page adds a local script and a third-party one from a CDN; the iframe page adds an entire embedded document with its own dependencies. Each page prints its own request list, so you can read the dependency tree off the screen.
4.2 Discovery: finding the dependencies
The browser learns of most dependencies while parsing, as the tokenizer and tree builder of Module 3 encounter <link>, <script>, <img>, and <iframe> elements. But discovery is not uniform. A font referenced by @font-face or a background image set in url() is invisible until the CSS itself has been fetched and parsed — the browser has to load one resource to find out it needs another. That is the amber layer in Figure 4.1, and it is why a web font often starts downloading conspicuously late.
Worse, discovery competes with the parser-blocking problem from Module 3: a synchronous <script> stops the parser, which would stop discovery of everything below it in the document. The browser's answer is the preload scanner — a secondary, lightweight pass that races ahead through the raw bytes while the main parser is blocked, finds src and href references, and starts fetching them early. Discovery order and network order are therefore not the same thing, and a good deal of browser engineering exists to make the network busy as early as possible.
4.3 The waterfall
Plot every request against time and you get the waterfall — the most important diagnostic view in web performance, and the one in your DevTools Network panel. Each bar is one resource; its horizontal position is when it started and how long it took; its vertical stacking shows what happened in parallel and what had to wait.
Two structural facts jump out of any waterfall. Parallelism is limited — over HTTP/1.1 a browser opens only a handful of connections per host, so requests queue; HTTP/2 and HTTP/3 multiplex many over one connection and largely dissolve that queue (Module 2). And chains are expensive — a resource that depends on another cannot start until the first finishes, so a three-deep chain (HTML → CSS → font) costs three sequential round trips no amount of bandwidth can collapse. The art of loading performance is turning chains into parallel fetches.
4.4 Render-blocking, async, and defer
Not all dependencies are equal in how they interrupt the page. Two behaviors dominate, and controlling them is most of what front-end performance work consists of.
CSS is render-blocking by default. The browser will not paint until it has the CSS, because painting first and restyling second would flash unstyled content at the user. So a stylesheet in the head holds back the first paint of the entire page — which is usually what you want, and is why the critical CSS should be small and fast.
Scripts choose their blocking behavior. A plain <script> is parser-blocking and ordered; the modern attributes change that:
async fetches alongside parsing and runs whenever it lands; defer fetches alongside parsing but runs in order after the document is built. For a script that touches the DOM, defer is almost always the right default. (Module-type scripts defer by nature.)4.5 Priorities, limits, and hints
The browser does not fetch the dependency tree in document order; it fetches by priority. Render-blocking CSS and the fonts needed for visible text are high priority; an image far down the page or an async analytics script is low. The browser assigns these priorities automatically from the kind of resource and where it sits, and you can nudge them: the fetchpriority attribute raises or lowers a specific request, and the resource hints — preconnect to warm up a connection, preload to start a critical fetch early, prefetch to grab something for the next navigation — let the author correct the browser's guesses. These are how you fix the “font discovered late” problem: preload it so it does not wait for the CSS.
The browser is a scheduler for the network. Priorities decide what the user sees first; hints are how the author argues with the defaults.
4.6 First party, third party: what another host costs
Every waterfall so far came from one host. Put a request on a second host — a library from a CDN, an analytics tag, a stock photo — and its bar grows a prefix before the first byte: look up the name (DNS), open a connection (TCP), and agree on encryption (TLS 1.3 needs one more round trip; HTTP/3 folds the connection and the encryption together, as Module 2 showed). Your own host paid those costs once, for the page itself. Each new host pays them again.
The hints from 4.5 exist for this. <link rel="preconnect" href="https://cdn.example"> does the lookup, connection, and encryption early, for a host you know the page will need; rel="dns-prefetch" does only the lookup. Add crossorigin when the files will be fetched with CORS, as fonts are, or the browser opens a second connection anyway. Hints save the setup, not the download, and a preconnect to a host the page never uses is a connection wasted.
The old case for a shared CDN was the cache: load the popular library from the popular address, and visitors probably have it already. That case is closed. Browsers now keep a separate HTTP cache for each top-level site (Safari since 2013, Chrome since version 86, Firefox since 85), so a copy fetched while visiting someone else's site is not reused on yours. Served from your own host, the same file costs the same bytes and no new connection.
Which requests count as third party? An origin is a URL's scheme, host, and port. A site is its scheme and registrable domain, the part a person can register, as the Public Suffix List decides. static.democompany.com is a different origin from www.democompany.com but the same site: usually a second connection, yet still the first party. www.someothercompany.com is another site, a true third party. Try any pair in the URL Explorer’s Compare tab.
A third party also costs control. Its speed, its uptime, and the file at its address tomorrow are not yours. For any other origin, the browser shows your own scripts only how long a file took — not its size and not its DNS, connection, and download phases — unless that server sends Timing-Allow-Origin. Demo Company's page 5 lists the ZingGrid script's time beside a size marked “hidden” in its request log. The film Everything You Embed follows what goes wrong next.
Every new host is a toll: a name, a connection, a handshake, and some trust. Pay it on purpose.
4.7 The critical rendering path
Out of the whole dependency tree, only a subset is required before the browser can show anything: the HTML, the render-blocking CSS, and any parser-blocking JavaScript ahead of the visible content. That subset is the critical rendering path, and shortening it — less blocking CSS, deferred scripts, preloaded essentials — is the most direct lever on how quickly a page paints. Everything outside the critical path (below-the-fold images, async scripts, non-essential fonts) can arrive afterward without delaying first paint. Demo Company's critical-render-path page exists to let you watch a single synchronous script sit squarely in that path and hold the whole page hostage until it loads and runs.
This is the bridge to Module 5. Once the critical resources are in — the DOM from Module 3 and the CSSOM built from the render-blocking CSS — the browser has everything it needs to compute styles, lay out boxes, and paint. The resource model decides when the pixel pipeline can start.
4.8 When dependencies fail
The network is not reliable terrain, and a dependency tree is only as robust as its handling of missing branches. A resource can be slow, can 404, can hang, can be blocked. A well-built page degrades rather than breaks: a failed image leaves its alt text and reserved space rather than collapsing the layout; a font that does not arrive falls back to a system face via font-display; a failed enhancement script leaves working HTML behind it. This is progressive enhancement seen from the loader's side — build the critical path so that the non-critical failing does not take the page down with it.
4.9 The lab: watch the tree grow
Walk Demo Company's resource pages in order with the Network panel open, and read each new request against this module:
image.html + cat.webp a leaf: an image referenced straight from the DOM
css.html + main.css render-blocking; holds first paint
font.html + font.css → font a chain: the font is named inside the CSS
javascript.html + local-script.js + zinggrid.min.js local and third-party (cdn.zinggrid.com) scripts
iframe.html + embedded document a whole subtree with its own resourcesSort the waterfall by start time and watch the parallelism; sort by the dependency and watch the chains. Find the font on the font page and confirm it begins only after font.css finishes — then imagine the preload that would fix it. You are looking at the resource model's own ledger.
4.10 What you now have
The page as a dependency tree rather than a file; discovery through parsing and the preload scanner racing ahead of blocked parsing; the waterfall as the view that exposes parallelism and chains; the blocking behaviors that matter most — render-blocking CSS and the plain/async/defer choice for scripts; priorities and hints as the controls; the price of every new host, and the difference between a second origin and a second site; and the critical rendering path as the subset that gates first paint. The unifying idea: the browser is a scheduler for the network, and how it orders the fetch decides how fast the page feels.
With the critical resources in hand — the DOM and the CSSOM — the browser can finally turn structure and style into something on screen. Module 5 is the pixel pipeline: computing styles, laying out boxes, painting, and compositing layers on the GPU, and the reason some changes are cheap and others force the whole machine to run again.
You wrote one file. The browser fetched a forest. Now it has to draw it.