Part 0 · 10 chapters · ~55 min

The Map

A browser is a program that takes untrusted text from strangers, turns it into pixels and behaviour on your machine, and does so without letting the stranger touch anything else. This part is the map: the processes, the threads inside a renderer, the pipeline from URL to frame, the two budgets every page spends, and how the rest of the course is laid out against them.

1

What a browser actually is

the question

"It's a program that shows web pages. What else is there to know?"

A browser is a program that takes untrusted text from strangers, turns it into pixels and behaviour on your machine, and does so without letting the stranger touch anything else. Every design decision in it follows from those three clauses.

the three jobs, and the engineering each one forces
  1. Turn text into pixels. HTML, CSS and images become geometry and paint. This is the rendering pipeline, and it has to finish a frame in 16.7 ms to feel smooth. Parts 2 to 5.
  2. Run a stranger's program. JavaScript from any origin executes on your CPU with access to the page. That needs an engine, a scheduler and a memory model. Parts 6 and 7.
  3. Contain it. The page must not read your files, your other tabs or your bank's cookies. That is the process model, the sandbox, the same-origin policy and everything in Part 9.
worked numbers
a browser is not one program. in chrome today it is:

  1 browser process     (UI, navigation, privilege)
  N renderer processes  (one per site, sandboxed)
  1 GPU process
  1 network process
  K utility processes   (audio, storage, printing, ...)

the thing you call "the browser" is the coordinator. the thing that runs your page is one of the N.
why this course exists
Most frontend advice is a list of tricks that work until they don't. Every trick is a consequence of a mechanism in this pipeline. Once you can name the mechanism, you can predict which tricks apply, which are superstition, and what to measure when something is slow. That is the difference between a senior who knows the tricks and a staff engineer who knows why.
2

The multi-process model

Early browsers were one process. One bad page took the whole window with it, and one compromised page could read every other page's memory. Chrome shipped in 2008 with a process per tab, and every major browser has converged on a version of the same split since.

the processes, and what each one is allowed to do
  1. Browser process. Owns the window, the tab strip, the address bar, bookmarks, downloads, and navigation. It is the only process that talks to the OS with full privilege. It never executes page JavaScript, which is the point.
  2. Renderer process. One per site (Chrome) or per group of sites under memory pressure. Runs Blink and V8: parsing, style, layout, paint recording, and all your JavaScript. Sandboxed: it cannot open files, sockets or the GPU directly.
  3. GPU process. Owns the graphics context. Receives compositing layers from renderers and draws the frame. Isolated because GPU drivers crash and because a renderer with GPU access is a renderer that can read other renderers' frames.
  4. Network process. Owns sockets, DNS, TLS, the HTTP cache and the cookie jar. Renderers request resources over IPC and receive bytes back. A renderer never sees a raw socket.
  5. Utility processes. Audio, storage, printing, PDF, the speech service. Short-lived or long-lived, each sandboxed to one job, each disposable.
the trade
Processes cost memory. Every renderer carries its own copy of Blink, V8 and a heap. Chrome spends memory to buy isolation, and under pressure it falls back to sharing renderers between sites in the same tab group. That is why a laptop with 60 tabs open feels the way it does, and why "one tab per site" is a policy with a budget rather than a law.
THE PROCESS MODEL
one browser, many processes
swipe the figure sideways, or tap expand for full screen
1/7
browser
One process, the browser process, owns the window, the tabs strip, the address bar, navigation and every privileged operation. It never runs page JavaScript.
3

Site isolation, and why a tab is not a process

the question

"Is each tab its own process?"

Not quite, and the difference matters for security. The unit of isolation is the site, which is the registrable domain plus scheme: https://bank.example. Two tabs on the same site may share a renderer. One tab containing an iframe from a different site puts that iframe in a different renderer from the page around it.

worked numbers
a tab on bank.example with an embedded ads.example iframe:

  renderer A  runs bank.example's document
  renderer B  runs ads.example's iframe
  the browser process stitches them into one tab

the ad's JavaScript is in a different address space from your balance.
it cannot read bank.example's memory even with a renderer exploit.
why it was forced, not chosen
  1. Spectre (2018). Speculative-execution side channels let JavaScript read any memory in its own process. If the ad and the bank share a process, timing attacks read the bank. The only fix that holds is to not share.
  2. Renderer exploits. Blink and V8 have bugs. A compromised renderer that only holds one site's data can only leak that site.
  3. Cross-origin read blocking. The network process refuses to deliver certain cross-site responses to a renderer that should not hold them, so even a compromised renderer cannot fetch the bank's JSON into its memory.
what it means for you
postMessage between a page and a cross-site iframe is a real IPC hop, not a function call. A cross-site iframe cannot be synchronously inspected; the frame tree you see in DevTools is assembled across processes. And memory in the Task Manager is per site, not per tab, which is why one tab can show as three processes.
4

Inside a renderer: the threads

A renderer process is where the course spends most of its time, so it is worth knowing its threads by name before anything else.

the threads, and what each one owns
  1. Main thread. HTML parsing, CSS parsing and resolution, layout, paint recording, JavaScript execution, event dispatch, and the DOM itself. One thread, one task queue. The phrase "blocking the main thread" means blocking all of that.
  2. Compositor thread. Takes the layer tree and paint records the main thread produced and builds frames from them. Handles scrolling, and animations of transform and opacity, without asking the main thread.
  3. Raster threads. A pool that paints tiles of each layer into bitmaps. Parallel, so a large layer is painted by several threads at once.
  4. Worker threads. Each Web Worker gets a thread with its own event loop and no DOM access. Communication is by message only.
  5. IO and housekeeping threads. IPC, file access via the browser process, timers. Invisible unless you profile.
the asymmetry worth memorising
Scrolling and transform animations live on the compositor; clicks, input handling and anything that touches layout live on the main thread. A page can scroll smoothly while being completely unresponsive to taps. When a user says "it feels laggy", the first question is which thread they mean, because the fixes are different.
INSIDE ONE RENDERER
the threads that share a tab
swipe the figure sideways, or tap expand for full screen
1/5
main thread
The main thread. Everything you have ever heard called "blocking" happens here: HTML parsing, CSS resolution, layout, paint recording, and all of your JavaScript. One thread, one queue.
5

A request's life at 10,000 feet

Here is the whole thing once, fast, so the rest of the course has a map to pin to. Each stage below is a part of its own.

from typed URL to painted frame
  1. Navigation. The browser process parses what you typed, decides it is a URL, checks the HTTP cache, picks or spawns a renderer for the site, and asks the network process to fetch.
  2. Network. DNS lookup, TCP handshake, TLS handshake, HTTP request, first byte. On HTTP/3 the handshakes collapse into fewer round trips. The cache and resource hints can short-circuit any of this.
  3. Parse. Bytes become characters, characters become tokens, tokens become DOM nodes. A preload scanner reads ahead for scripts, styles and images so their fetches start before the parser reaches them.
  4. Style. CSS becomes the CSSOM. For every DOM element, matching selectors are found and the cascade produces a computed value for every property.
  5. Layout. Geometry. Every box gets position and size through the formatting context it lives in.
  6. Paint. A display list of drawing commands in paint order, split into compositing layers where the engine decides a layer pays for itself.
  7. Composite. Raster threads paint tiles, the compositor assembles layers, the GPU process presents the frame. First Contentful Paint is the first time this reaches the screen with content.
  8. Loop. JavaScript runs, input arrives, timers fire. Whatever they change reruns the pipeline from that stage forward, ideally inside the next 16.7 ms frame.
URL TO PIXELS
the whole pipeline at 10,000 feet
swipe the figure sideways, or tap expand for full screen
1/7
navigate
You type a URL. The browser process decides where it goes: a search, a cached page, a new navigation. It picks or spawns a renderer for the site before a single byte arrives.
6

The critical rendering path, named

the question

"Why does a 40 KB stylesheet delay the whole page when a 2 MB image doesn't?"

Because the stylesheet is on the critical rendering path and the image is not. The path is the minimum set of work between the first byte and the first pixel, and it is defined by what blocks what.

worked numbers
the blocking rules, which are the whole story:

  HTML parsing     blocks on  a synchronous <script> (it might document.write)
  script execution  blocks on  all CSS above it (it might read a computed style)
  first render     blocks on  all CSS in <head> (unstyled flash is worse than waiting)
  nothing          blocks on  images, async scripts, fonts (mostly)

so: CSS blocks render, sync JS blocks parsing, and JS waits for CSS.
a sync script after a stylesheet pays for both.
what shortens the path
  1. Fewer bytes on it. Critical CSS inline, the rest loaded with media or deferred.
  2. Fewer round trips on it. Preconnect to the origins it needs; preload the font or stylesheet the parser will not discover early.
  3. Nothing synchronous in the head that does not have to be. defer for scripts that touch the DOM; async for scripts that do not care about order.
  4. Server-render the shell so the first paint does not wait for JavaScript at all.
the measurement
DevTools' Performance panel draws a vertical line at First Contentful Paint. Everything left of that line is the critical path. Part 12 shows how to read it; the DevTools course has you cause and fix it.
7

Where the time goes: two budgets

There are two clocks in a browser, and conflating them causes most bad performance advice.

the load budget
the frame budget
seconds.
From navigation to usable. Dominated by the network and the critical path. Measured by TTFB, FCP, LCP. Spent once per page.
16.7 ms. From one vsync to the next at 60 Hz (8.3 ms at 120 Hz). Dominated by main-thread work and paint. Measured by INP and frame drops. Spent sixty times a second, forever.
worked numbers
a realistic load budget on a mid-range Android over 4G:

  DNS + TCP + TLS       ~300 ms
  TTFB                   ~400 ms
  HTML + critical CSS   ~200 ms
  parse + style + layout ~150 ms
  JS parse + execute    ~900 ms  ← the usual villain
  images for LCP        ~500 ms

a frame budget, where it all has to fit:

  input handling    ~1 ms
  JS                ≤ 8 ms
  style + layout    ~3 ms
  paint + composite ~3 ms
  slack             ~1 ms

a 50 ms click handler is three dropped frames. the user calls that lag.
the rule
Load problems are usually network and JavaScript size. Frame problems are usually layout and long tasks. They need different tools, different fixes and different metrics, and a page can be excellent at one and terrible at the other.
8

Three engines, and where they differ

Three rendering engines matter: Blink (Chrome, Edge, Opera, Brave, Samsung Internet, every Android WebView), WebKit (Safari, and every browser on iOS until recently), and Gecko (Firefox). They implement the same specifications and the same pipeline shape, with real differences underneath.

ConcernBlinkWebKitGecko
JS engineV8JavaScriptCoreSpiderMonkey
Layout engineLayoutNG (rewritten 2019 to 2022)Legacy with incremental rewritesLayout rewritten in parts; Stylo for style (Rust, parallel)
Style resolutionSingle-threaded, bloom-filter acceleratedSingle-threadedParallel across threads (Stylo)
Compositingcc, GPU processCore Animation on Apple platformsWebRender (GPU-driven, Rust)
Process modelSite isolation by defaultPer-tab, site isolation partialFission: site isolation
Where it bites youMemoryiOS forced it on everyone until 2024; feature lagSmallest share, so the least-tested path
the practical consequence
This course describes the pipeline in Blink's terms because Blink is where most of your users are and where the public documentation is deepest. Where WebKit or Gecko differ in a way that changes what you should do, the chapter says so. The spec defines the behaviour; the engine defines the cost.
9

How to read this course

the shape of every part
  1. The mechanism first. What the engine does, drawn step by step. Press play on the figure or step through it; every figure expands to full screen.
  2. Then the cost. What is cheap, what is expensive, and why, with numbers where numbers exist.
  3. Then the lens. Which DevTools panel shows this stage and what to look for. The DevTools course has the companion app where you cause each problem on purpose.
  4. Then the consequence. What you should do differently in code because of it, with the trick named as a consequence rather than a rule.
the parts, as a route
  1. Parts 1 to 5 are the load pipeline in order: network, parse, style, layout, paint and composite.
  2. Parts 6 and 7 are the runtime: the event loop and JavaScript in the page.
  3. Parts 8 to 11 are the rest of the platform: storage, security, navigation, input and media.
  4. Parts 12 and 13 are measurement.
  5. Part 14 builds a browser. Everything before it is the specification for what you build.
a note on depth
Nothing here is simplified to be kind. Where a mechanism is genuinely complicated, the chapter is long. If a chapter feels like more than you need, that is the chapter that will be the answer to a question you have not been asked yet.
10

The lab, and the habit

This course is paired with the DevTools course and its companion app. The app is a small React application where every route triggers one specific pathology: a layout thrash, a long task, a render-blocking stylesheet, a memory leak, a hydration mismatch, a blocked request. The DevTools course walks each panel; this course points at the app whenever a mechanism is easier to see than to read.

the habit this course is trying to install
  1. Look before guessing. Open the Performance panel before changing a line.
  2. Name the stage. Network, parse, style, layout, paint, composite, script. The fix depends entirely on which one.
  3. Find the thread. Main thread or compositor. Scroll jank and click lag are different bugs.
  4. Change one thing, measure again. The profile is the proof; the before-and-after screenshot is the pull request description.
where the lab lives
The companion app ships with the DevTools course under modules/devtools/lab/. Chapters in this course that say "go to the lab" name the exact route and what to look for. Until that course is built, the chapters still stand on their own.