Inside the Browser: From HTML to Pixels
Table of Contents
- The Browser Has a Page of Text
- Parsing: Text Becomes the DOM
- One Page, Many Requests
- CSS and Scripts Can Hold Things Up
- Layout, Paint, and the Screen
- JavaScript Changes the Page Later
- Reading the Waterfall
- View Source vs. Elements
- Try It Yourself
1. The Browser Has a Page of Text
At the end of chapter 6, the browser has a 200 OK response with
Content-Type: text/html and a body like this — the index.html from
the Git series, with a photo and a script added:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Dara Sok</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Hi, I'm Dara</h1>
<img src="photo.jpg" alt="Dara smiling">
<p><a href="about.html">About me</a></p>
<script src="app.js"></script>
</body>
</html>
That's just text. To you, it's obviously a page with a heading, a photo, and a link. To the browser, it's a few hundred bytes that need to become coloured pixels. This chapter follows those bytes. You'll learn to write HTML and CSS in the next series on the roadmap; here, you only need to know what the browser does with them.
2. Parsing: Text Becomes the DOM
The browser reads the HTML from top to bottom and builds a tree of everything in it. This tree is called the DOM — the Document Object Model:
document
└── html
├── head
│ ├── meta
│ ├── title ── "Dara Sok"
│ └── link (style.css)
└── body
├── h1 ── "Hi, I'm Dara"
├── img (photo.jpg)
├── p
│ └── a (about.html) ── "About me"
└── script (app.js)
Each box is a node. Elements inside other elements become their children, just like folders inside folders in a file system. The DOM is the browser's real model of the page — what it draws, and what JavaScript reads and changes.
The browser starts parsing as soon as the first bytes arrive. It doesn't wait for the whole file, which is one reason pages can start appearing before they've fully downloaded.
HTML parsers are also very forgiving. Forget a closing </p> and the
browser quietly guesses what you meant. That's friendly, but it means
broken HTML often "works" in a way you didn't intend.
3. One Page, Many Requests
While parsing, the browser finds things the page needs but doesn't
contain: the style.css in <link>, the photo.jpg in <img>, the
app.js in <script>. For each one, it sends a new request — the
same kind you saw in chapter 5, usually over the same connection:
GET /my-site/ → index.html (the page itself)
GET /my-site/style.css → the styles
GET /my-site/photo.jpg → the image
GET /my-site/app.js → the script
GET /favicon.ico → the little icon in the tab
The relative paths from chapter 2 are resolved here: style.css on the
page /my-site/ becomes /my-site/style.css.
A tiny personal site might need five requests. A news site or online shop can need two hundred: fonts, images, scripts, styles, ads, analytics, videos. Each one is a full request and response. That's why chapter 4 cared so much about reusing connections, and why chapter 9 cares so much about caching.
The link to about.html is not requested yet. Links only load when
you click them.
4. CSS and Scripts Can Hold Things Up
Some resources make the browser wait before it shows anything:
- CSS blocks rendering. The browser won't draw the page until the
stylesheets in
<head>arrive — otherwise you'd see a flash of unstyled page, then a jump when the styles load. - Scripts block parsing. When the parser hits a plain
<script>, it stops, downloads the script, runs it, and only then carries on, because the script might change the page.
That's why scripts were traditionally put at the end of <body>,
like app.js above: by the time the parser reaches it, the visible
page is already built. Modern pages often use <script defer> instead,
which means "download this in the background and run it after parsing
is done":
<script src="app.js" defer></script>
Images don't block anything. The page appears without them and they pop in when they arrive — the async behaviour you noticed at the end of How Computers Work's Input/Output chapter.
5. Layout, Paint, and the Screen
Once the browser has the DOM and the CSS, it turns them into pixels in three main steps:
DOM + CSS ──▶ Style ──▶ Layout ──▶ Paint ──▶ Composite ──▶ Screen
- Style: work out which CSS rules apply to each node. "This
h1is 32px, bold, dark blue." - Layout: work out where everything goes and how big it is. "The
h1starts 16px from the left, is 600px wide, and 40px tall." This depends on the window size — which is why a page rearranges when you resize it or turn your phone sideways. - Paint: fill in the actual pixels — text, colours, borders, images.
- Composite: stack the painted layers together, using the GPU, and hand the final image to the operating system to show on screen.
This is the very end of the journey How Computers Work's final chapter traced: the browser is a process, these steps run on its threads, and the GPU puts the result on your display.
6. JavaScript Changes the Page Later
Rendering doesn't happen only once. JavaScript can change the DOM at any time — add a node, change some text, hide a menu — and the browser redoes the style, layout, and paint steps for what changed.
JavaScript can also send its own requests, using a function called
fetch():
const response = await fetch("/api/messages");
const messages = await response.json();
That's an ordinary HTTP request, exactly like the ones in chapter 5 — it just happens after the page has loaded, without the address bar changing. It's how a chat app shows new messages or a shop updates your cart without reloading the whole page. You'll see these in DevTools' Network tab under the Fetch/XHR filter, and you'll learn to write them in JavaScript & TypeScript Fundamentals. Chapter 10 explains what kind of server answers them.
7. Reading the Waterfall
The Network tab has a Waterfall column: one bar per request, showing when it started and how long it took. On a simple page it looks something like this:
index.html ████
style.css ███
photo.jpg ██████████
app.js ██
favicon.ico █
└────────────────────▶ time
Read it from top to bottom:
- The page itself comes first. Nothing else can start until the browser has parsed enough HTML to discover it.
- Then the resources it found start together, in parallel.
- Big files like images take the longest bars.
Hover over a bar to see its breakdown — the same DNS, connection, and TLS steps you saw in chapter 4's Timing tab, plus Waiting (the server thinking) and Content Download.
At the bottom of the panel you'll find a summary: how many requests, how many bytes, and two timings. DOMContentLoaded is when the HTML has been fully parsed; Load is when everything, including every image, has finished.
8. View Source vs. Elements
DevTools gives you two different views of a page, and they don't always match:
- View source (right-click → View Page Source, or put
view-source:in front of a URL) shows the HTML the server sent — exactly whatcurlprints. - The Elements tab (or Inspector in Firefox) shows the DOM right now, after the parser has fixed mistakes and JavaScript has made its changes.
On your simple site, they'll look almost the same. On a modern web app,
view source might show an almost empty page with just a <div id="root"></div> and some scripts — and Elements shows a full app,
because JavaScript built all of it in the browser. That difference is
the subject of chapter 10.
9. Try It Yourself
- Count the requests. Open your GitHub Pages site and a big news or shopping site, each with DevTools → Network open, and reload. Compare the request count and total size at the bottom of the panel.
- Slow it down. In the Network tab, find the throttling menu (it says No throttling) and choose Slow 4G or 3G. Tick Disable cache and reload a big site. Watch the page build up piece by piece: text first, then styles, then images. Set it back to No throttling when you're done.
- Break the styles. Right-click your
style.cssrequest and choose Block request URL, then reload. That's your page with only the HTML. Unblock it afterwards (in the Network request blocking panel, or by right-clicking again). - Edit the DOM. In Elements, double-click the text of your
h1and change it. The page updates instantly — but reload, and it's gone. You changed the browser's DOM, not the file on the server. - Compare the two views. Open
view-source:for your site, then for a big web app like YouTube. Which one looks like the Elements tab, and which one doesn't?
The page is on the screen. Next, we'll look at how a website remembers who you are from one request to the next — even though HTTP itself forgets everything.
Buy Me a Coffee
Found this tutorial helpful? Consider buying me a coffee to support more free content!