How browsers work: from URL to pixels
Type a URL, press Enter, and ~100 milliseconds of machinery kicks in: DNS, TCP, TLS, parsing, layout, paint. This guide walks every step — and every step has an interactive simulator. Press play.
mindmap — quick refresh
Prerequisites: How HTTP works — this guide assumes you know what requests, responses, and round trips are.
Fast sites feel fast because their developers understand two hard facts about the web:
- Latency is the enemy. Every byte travels a physical distance, and the protocols underneath HTTP demand several round trips before the first byte of HTML even arrives.
- The browser is (mostly) single-threaded. One main thread parses your HTML, runs your JavaScript, computes layout, and responds to the user's taps. Whatever you make it do, it can't do anything else at the same time.
Everything a browser does between "Enter" and "pixels" is shaped by those two constraints. Let's walk the whole pipeline, step by step. Each step comes with a simulator — press play or step through them; they're the point of this guide.
Step 1 — Navigation: finding the server
DNS: turning a name into an address
learn.kevalabs.com means nothing to a network router. Before anything can be requested, the browser needs an IP address, and it gets one through DNS — a hierarchy of caches and servers, asked in order from cheapest to most authoritative.
The important performance detail: every hop in that chain caches the answer. The first visitor pays the full trip; subsequent lookups are answered in microseconds from a cache nearby. But every unique hostname on your page — fonts CDN, analytics, image host — pays its own lookup, which is why piling up third-party domains hurts, especially on mobile networks where each lookup can take hundreds of milliseconds.
TCP and TLS: opening a trustworthy pipe
With an IP in hand, the browser can't just start sending HTML requests. First it must establish a TCP connection (the three-way handshake), and for HTTPS, negotiate TLS encryption on top of it. Watch how many times messages cross the wire before a single byte of your page moves:
Three round trips before the first byte of HTML. If the server is 100 ms away, that's 300 ms of pure protocol overhead — nothing downloaded yet. This is why HTTPS session resumption, HTTP/2 connection reuse, and QUIC/HTTP-3 (which merges the transport and crypto handshakes) exist: the handshakes are the tax, and modern protocols work hard to pay it once.
Step 2 — The response and the 14 KB rule
The request is out; the server responds with the first byte of HTML (Time To First Byte, TTFB). But the server can't firehose the whole page at once. TCP starts cautiously — a mechanism called slow start — sending a small initial window (~10 packets, about 14 KB), then doubling it after each acknowledged round trip:
That first ~14 KB window is why performance guides obsess over what's in the top of your HTML: if enough of the document (and its critical CSS) fits in the first window, the browser can start working after a single round trip. And congestion control cuts both ways — when a packet is lost, the window shrinks and rebuilds, which is the transport layer telling you that every kilobyte of critical path matters.
Step 3 — Parsing: from bytes to trees
Building the DOM
As HTML bytes stream in, the parser tokenizes them and builds the DOM tree — the object model of your document. Two things make this step interesting: some resources block the parser, and the browser cheats around that with a second, lightweight scanner. Here's the document the simulator parses:
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Hi</h1>
<img src="cat.png">
<script src="app.js"></script>
<p>Bye</p>
</body>
</html>
Two takeaways from that run:
- A
<script>withoutasyncordeferhalts the parser — the script mightdocument.write(), so the browser can't safely continue until it's downloaded and executed. - The preload scanner softens the blow: it races ahead of the blocked parser, spotting
img,link, andscriptURLs and requesting them early, so the downloads are usually done by the time the parser catches up.
Building the CSSOM
CSS gets its own tree: the CSSOM. The browser parses every rule, then resolves what each node's final, computed style is — walking down the tree so that inheritance and the cascade (specificity, then source order) fall out naturally:
Building the CSSOM is fast, but it's render-blocking: the browser refuses to paint anything until it knows the final styles — a half-styled flash would be worse. Keep the CSS on the critical path small.
Meanwhile, off the main thread, JavaScript files are compiled, and the browser also builds an accessibility tree — the structure screen readers and other assistive tech actually consume.
Step 4 — Render: style → layout → paint → composite
The render tree
DOM and CSSOM combine into the render tree: only the nodes that will actually produce pixels. Non-visual nodes (<head>, <script>, <meta>) are dropped, and so is anything with display: none — but visibility: hidden stays, because an invisible box still occupies space:
Layout: where and how big?
With the render tree ready, the browser runs layout (also called reflow when it happens again): starting from the viewport width, it computes the exact position and size of every box. Resize the viewport in the simulator and watch every box get recomputed:
Layout is recursive and expensive — changing one element's width can dirty everything below it. This is why images without declared dimensions are a classic sin: each one that arrives forces another reflow (and a layout shift the user can see).
Paint and composite
Finally the browser paints — rasterizing each render-tree node into pixels (text, colors, borders, shadows, in a defined order) — and composites: layers get stitched together on the GPU. Some elements (transforms, will-change, videos, canvas) are promoted to their own compositor layer, and that has a huge consequence for animation:
Animate left and every frame re-runs style → layout → paint → composite on the main thread. Animate transform and the compositor slides an already-painted layer around — off the main thread, silky even while your JS is busy. This one habit separates janky UIs from smooth ones.
Step 5 — Interactivity: the main thread is a queue
The page looks ready — but "ready" only counts if it responds when tapped. Rendering work and your JavaScript share one main thread, and a long task blocks everything, including click handlers:
Time to Interactive is the moment the main thread is free enough to respond within ~50 ms. A page that paints in one second but ships a 2-second JavaScript boot task feels broken: clicks land in a queue and wait. Break long tasks up, defer what isn't critical, and move real computation to Web Workers.
Takeaways
- Round trips dominate first-visit latency: DNS + TCP + TLS cost ~3 round trips before any HTML moves. Fewer unique hostnames, reused connections, and modern protocols (HTTP/2, HTTP/3) are how you pay less.
- The first 14 KB is special — TCP slow start delivers it after one round trip. Fit your critical HTML/CSS in it.
- CSS blocks rendering; sync scripts block parsing. Keep critical CSS lean; use
defer/asynceverywhere you can. The preload scanner helps, but don't hide resources from it (e.g. behind JS-injected tags). - Layout is expensive and cascading. Declare image dimensions; batch DOM reads/writes.
- Animate with
transformandopacity, notleft/top/width— compositor-only animations skip layout and paint entirely. - The main thread is the whole game for interactivity. Under ~50 ms per task, or the user feels it.
References
- Allman, M., Paxson, V., & Blanton, E. (2009). TCP congestion control (RFC 5681). Internet Engineering Task Force. https://doi.org/10.17487/RFC5681
- Grigorik, I. (2013). High performance browser networking. O'Reilly Media. https://hpbn.co/
- Kosaka, M. (2018). Inside look at modern web browser (part 3). Chrome for Developers. https://developer.chrome.com/blog/inside-browser-part3
- MDN Web Docs. (n.d.). Critical rendering path. Mozilla. Retrieved August 16, 2026, from https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/Critical_rendering_path
- MDN Web Docs. (n.d.). How browsers work. Mozilla. Retrieved August 16, 2026, from https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/How_browsers_work
- Mockapetris, P. (1987). Domain names—Concepts and facilities (RFC 1034). Internet Engineering Task Force. https://doi.org/10.17487/RFC1034
- Rescorla, E. (2018). The Transport Layer Security (TLS) protocol version 1.3 (RFC 8446). Internet Engineering Task Force. https://doi.org/10.17487/RFC8446
- WHATWG. (2026). HTML living standard: Parsing HTML documents. https://html.spec.whatwg.org/multipage/parsing.html
Structure follows MDN's How browsers work (CC-BY-SA); the prose, mistakes, and simulators are ours.