← Back to all projects

LEARN BROWSER INTERNALS

Learn Browser Internals: From URL to Pixels

Goal: Deeply understand how web browsers work—from typing a URL to painting pixels on screen. Master the rendering pipeline, JavaScript execution, networking, security, and build your own browser components.


Why Learn Browser Internals?

Every web developer uses browsers daily, but few understand what happens under the hood. When you type a URL and press Enter, an incredibly complex symphony begins:

  • DNS resolution finds the server’s IP address
  • TCP/TLS handshakes establish secure connections
  • HTTP requests fetch HTML, CSS, JavaScript
  • Parsers tokenize and build trees
  • The rendering engine calculates layout and paints pixels
  • The JavaScript engine interprets and JIT-compiles code
  • GPU acceleration composites layers for smooth animations

Understanding this unlocks:

  • Better performance optimization - Know what causes jank and how to fix it
  • Deeper debugging skills - Understand why things break
  • Security awareness - Know how browsers protect users
  • Framework design insight - See why React/Vue work the way they do
  • Career opportunities - Browser engineers are in high demand

After completing these projects, you will:

  • Build your own HTML parser and DOM
  • Create a CSS selector engine
  • Implement a layout algorithm
  • Write a JavaScript interpreter
  • Understand the complete rendering pipeline
  • Build a mini browser from scratch

Core Concept Analysis

The Browser’s Journey: URL to Pixels

┌─────────────────────────────────────────────────────────────────────────────┐
│                           USER TYPES URL                                     │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│  1. NETWORKING                                                               │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │ DNS Lookup   │→ │ TCP Handshake│→ │ TLS Handshake│→ │ HTTP Request │    │
│  │ (find IP)    │  │ (SYN-ACK)    │  │ (encryption) │  │ (GET /page)  │    │
│  └──────────────┘  └──────────────┘  └──────────────┘  └──────────────┘    │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼ HTML/CSS/JS received
┌─────────────────────────────────────────────────────────────────────────────┐
│  2. PARSING                                                                  │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │ HTML Parser  │→ │ DOM Tree     │  │ CSS Parser   │→ │ CSSOM Tree   │    │
│  │ (tokenize)   │  │ (build tree) │  │ (tokenize)   │  │ (build tree) │    │
│  └──────────────┘  └──────────────┘  └──────────────┘  └──────────────┘    │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼ DOM + CSSOM ready
┌─────────────────────────────────────────────────────────────────────────────┐
│  3. RENDERING                                                                │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │ Render Tree  │→ │ Layout       │→ │ Paint        │→ │ Composite    │    │
│  │ (combine)    │  │ (geometry)   │  │ (draw)       │  │ (GPU layers) │    │
│  └──────────────┘  └──────────────┘  └──────────────┘  └──────────────┘    │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
                                    ▼ Pixels on screen!
┌─────────────────────────────────────────────────────────────────────────────┐
│  4. JAVASCRIPT EXECUTION (parallel to parsing)                               │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐    │
│  │ Parse JS     │→ │ Compile      │→ │ Execute      │→ │ Optimize     │    │
│  │ (tokenize)   │  │ (bytecode)   │  │ (interpret)  │  │ (JIT)        │    │
│  └──────────────┘  └──────────────┘  └──────────────┘  └──────────────┘    │
└─────────────────────────────────────────────────────────────────────────────┘

Key Concepts Explained

1. Browser Architecture (Multi-Process)

Modern browsers like Chrome use a multi-process architecture for security and stability:

┌─────────────────────────────────────────────────────────────────────────────┐
│                            BROWSER PROCESS                                   │
│  • UI (address bar, tabs, bookmarks)                                        │
│  • Network requests                                                          │
│  • Storage access                                                            │
│  • Coordinates other processes                                               │
└─────────────────────────────────────────────────────────────────────────────┘
         │                    │                    │                    │
         ▼                    ▼                    ▼                    ▼
┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│ RENDERER     │  │ RENDERER     │  │ GPU PROCESS  │  │ NETWORK      │
│ PROCESS      │  │ PROCESS      │  │              │  │ PROCESS      │
│ (Tab 1)      │  │ (Tab 2)      │  │ • Drawing    │  │              │
│              │  │              │  │ • Compositing│  │ • DNS        │
│ • Blink      │  │ • Blink      │  │ • WebGL      │  │ • TCP/TLS    │
│ • V8         │  │ • V8         │  │              │  │ • HTTP       │
│ • DOM        │  │ • DOM        │  │              │  │              │
└──────────────┘  └──────────────┘  └──────────────┘  └──────────────┘

Benefits:
• Tab crash doesn't bring down browser
• Site isolation for security
• Better memory management
• Parallel processing

2. The Rendering Pipeline

HTML String → Tokenizer → DOM Tree
                              │
                              ├──────────────────┐
                              ▼                  │
CSS String → Parser → CSSOM Tree                │
                              │                  │
                              ▼                  │
                        Attachment               │
                     (Style Calculation)         │
                              │                  │
                              ▼                  │
                        Render Tree              │
                    (only visible nodes)         │
                              │                  │
                              ▼                  │
                          Layout                 │
                    (calculate geometry)         │
                              │                  ▼
                              ▼            JavaScript
                          Paint             (can modify)
                    (draw to bitmap)             │
                              │                  │
                              ▼                  │
                        Composite ◄──────────────┘
                    (layer to screen)

Each step can trigger the next:
• JavaScript modifies DOM → Style → Layout → Paint → Composite
• CSS animation on transform/opacity → Composite only (fast!)

3. DOM Tree Structure

HTML:
<!DOCTYPE html>
<html>
  <head>
    <title>Page</title>
  </head>
  <body>
    <div id="app">
      <h1>Hello</h1>
      <p>World</p>
    </div>
  </body>
</html>

DOM Tree:
                    Document
                        │
                    html (Element)
                   /          \
           head (Element)    body (Element)
               │                  │
         title (Element)    div#app (Element)
               │              /        \
         "Page" (Text)  h1 (Element)  p (Element)
                            │              │
                      "Hello" (Text)  "World" (Text)

Node Types:
• Document - Root node
• Element - HTML tags
• Text - Text content
• Comment - HTML comments
• DocumentFragment - Lightweight container

4. CSS Specificity & Cascade

Specificity Calculation:
┌─────────────────────────────────────────────────────────────────┐
│ Selector               │ Inline │ ID │ Class │ Element │ Total │
├─────────────────────────────────────────────────────────────────┤
│ style="..."            │   1    │  0 │   0   │    0    │ 1,0,0,0│
│ #header                │   0    │  1 │   0   │    0    │ 0,1,0,0│
│ .nav.active            │   0    │  0 │   2   │    0    │ 0,0,2,0│
│ div.container p        │   0    │  0 │   1   │    2    │ 0,0,1,2│
│ ul li a                │   0    │  0 │   0   │    3    │ 0,0,0,3│
│ *                      │   0    │  0 │   0   │    0    │ 0,0,0,0│
└─────────────────────────────────────────────────────────────────┘

Cascade Order (highest to lowest):
1. !important declarations
2. Inline styles
3. ID selectors
4. Class/attribute/pseudo-class selectors
5. Element/pseudo-element selectors
6. Order of appearance (later wins)

5. JavaScript Engine Pipeline (V8)

JavaScript Source
       │
       ▼
┌──────────────┐
│   Parser     │ ─── Parse into AST (Abstract Syntax Tree)
└──────────────┘
       │
       ▼
┌──────────────┐
│   Ignition   │ ─── Compile to bytecode (fast startup)
│  (Interpreter)│     Execute immediately
└──────────────┘
       │
       │ Hot function detected
       │ (called many times)
       ▼
┌──────────────┐
│  TurboFan    │ ─── Compile to optimized machine code
│(Optimizing JIT)│    Uses type feedback
└──────────────┘
       │
       │ Type assumption wrong?
       ▼
┌──────────────┐
│ Deoptimization│ ─── Fall back to bytecode
│              │     Re-collect type info
└──────────────┘

Hidden Classes:
┌────────────────────────────────────────────────────────┐
│ const obj1 = { x: 1, y: 2 }  → Hidden Class C0        │
│ const obj2 = { x: 3, y: 4 }  → Hidden Class C0 (same!)│
│                                                        │
│ obj1.z = 5  → Creates Hidden Class C1 (transition)    │
│                                                        │
│ Monomorphic: All objects same shape → FAST            │
│ Polymorphic: 2-4 shapes → slower                      │
│ Megamorphic: Many shapes → SLOW (no caching)          │
└────────────────────────────────────────────────────────┘

6. Event Loop & Task Queues

┌─────────────────────────────────────────────────────────────────────────────┐
│                              CALL STACK                                      │
│  (synchronous code execution)                                               │
└─────────────────────────────────────────────────────────────────────────────┘
                                    ↑
                                    │ Execute one task
                                    │
┌─────────────────────────────────────────────────────────────────────────────┐
│                              EVENT LOOP                                      │
│  1. Execute oldest task from Task Queue                                      │
│  2. Execute ALL microtasks                                                   │
│  3. Render if needed (rAF callbacks, then paint)                            │
│  4. Repeat                                                                   │
└─────────────────────────────────────────────────────────────────────────────┘
        ↑                           ↑                           ↑
        │                           │                           │
┌───────────────┐         ┌───────────────┐         ┌───────────────┐
│  TASK QUEUE   │         │MICROTASK QUEUE│         │  rAF QUEUE    │
│  (Macrotasks) │         │               │         │               │
├───────────────┤         ├───────────────┤         ├───────────────┤
│ • setTimeout  │         │ • Promise.then│         │ • requestAnim │
│ • setInterval │         │ • queueMicro  │         │   Frame       │
│ • I/O events  │         │ • MutationObs │         │               │
│ • UI events   │         │               │         │               │
└───────────────┘         └───────────────┘         └───────────────┘

Execution order example:
console.log('1');                    // Sync
setTimeout(() => console.log('2')); // Task queue
Promise.resolve().then(() => console.log('3')); // Microtask
requestAnimationFrame(() => console.log('4')); // rAF queue
console.log('5');                    // Sync

Output: 1, 5, 3, 4, 2 (microtasks before tasks, rAF before paint)

7. Browser Caching Layers

Request comes in:
       │
       ▼
┌──────────────────────────────────────────────────────────────┐
│                    MEMORY CACHE                               │
│  • Fastest (RAM)                                             │
│  • Short-lived (until tab closes)                            │
│  • Images, scripts loaded on current page                    │
└──────────────────────────────────────────────────────────────┘
       │ Miss
       ▼
┌──────────────────────────────────────────────────────────────┐
│                  SERVICE WORKER CACHE                         │
│  • Programmable (Cache API)                                  │
│  • Persists across sessions                                  │
│  • Enables offline support                                   │
│  • You control the caching strategy                          │
└──────────────────────────────────────────────────────────────┘
       │ Miss
       ▼
┌──────────────────────────────────────────────────────────────┐
│                     HTTP CACHE                                │
│  • Disk-based (persistent)                                   │
│  • Controlled by HTTP headers                                │
│  • Cache-Control, ETag, Last-Modified                        │
│  • Automatic browser management                              │
└──────────────────────────────────────────────────────────────┘
       │ Miss
       ▼
┌──────────────────────────────────────────────────────────────┐
│                       NETWORK                                 │
│  • DNS → TCP → TLS → HTTP                                    │
│  • Slowest option                                            │
│  • Response may be cached for next time                      │
└──────────────────────────────────────────────────────────────┘

8. Security Model

┌─────────────────────────────────────────────────────────────────────────────┐
│                        SAME-ORIGIN POLICY (SOP)                             │
│                                                                              │
│  Origin = protocol + host + port                                            │
│                                                                              │
│  https://example.com:443/page                                               │
│    │        │        │                                                       │
│    └──────┬─┴────────┘                                                       │
│           │                                                                  │
│        Origin                                                                │
│                                                                              │
│  Same origin examples:                                                       │
│  ✓ https://example.com/a and https://example.com/b                          │
│                                                                              │
│  Different origin:                                                           │
│  ✗ http://example.com (different protocol)                                  │
│  ✗ https://sub.example.com (different host)                                 │
│  ✗ https://example.com:8080 (different port)                                │
└─────────────────────────────────────────────────────────────────────────────┘
                                    │
         Can be relaxed with        │
                                    ▼
┌───────────────────────────────────┴───────────────────────────────────────┐
│                                                                            │
│  ┌──────────────────┐                      ┌──────────────────┐           │
│  │      CORS        │                      │       CSP        │           │
│  │                  │                      │                  │           │
│  │ Server says:     │                      │ Server says:     │           │
│  │ "Allow requests  │                      │ "Only allow      │           │
│  │  from origin X"  │                      │  scripts from    │           │
│  │                  │                      │  these sources"  │           │
│  │ Access-Control-  │                      │ Content-Security │           │
│  │ Allow-Origin:    │                      │ -Policy:         │           │
│  │ https://foo.com  │                      │ script-src 'self'│           │
│  └──────────────────┘                      └──────────────────┘           │
│                                                                            │
└────────────────────────────────────────────────────────────────────────────┘

Project List

The following 17 projects will take you from understanding to building browser components.


Project 1: Build an HTML Tokenizer

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: Rust, Python, Go
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Parsing / Lexical Analysis
  • Software or Tool: WHATWG HTML spec
  • Main Book: “Compilers: Principles, Techniques, and Tools” (Dragon Book)

What you’ll build: An HTML tokenizer that converts raw HTML strings into a stream of tokens (start tags, end tags, text, comments, doctype).

Why it teaches browser internals: Tokenization is the first step in HTML parsing. Understanding the state machine behind it reveals how browsers handle malformed HTML.

Core challenges you’ll face:

  • State machine implementation → maps to different parsing contexts
  • Character reference handling → maps to & < entities
  • Self-closing tags → maps to br, img, input, etc.
  • Error handling → maps to how browsers fix malformed HTML

Resources for key challenges:

Key Concepts:

  • Tokenizer State Machine: WHATWG Spec Section 13.2.5
  • Token Types: “Compilers” Ch. 3 - Aho et al.
  • Error Recovery: HTML Parser Idiosyncrasies

Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Understanding of state machines, basic parsing concepts

Real world outcome:

const html = `<div class="container"><p>Hello, <b>world</b>!</p></div>`;

const tokens = tokenize(html);
console.log(tokens);
// Output:
// [
//   { type: 'StartTag', name: 'div', attributes: { class: 'container' } },
//   { type: 'StartTag', name: 'p', attributes: {} },
//   { type: 'Character', data: 'Hello, ' },
//   { type: 'StartTag', name: 'b', attributes: {} },
//   { type: 'Character', data: 'world' },
//   { type: 'EndTag', name: 'b' },
//   { type: 'Character', data: '!' },
//   { type: 'EndTag', name: 'p' },
//   { type: 'EndTag', name: 'div' }
// ]

Implementation Hints:

The HTML tokenizer uses a state machine:

States:
• Data State (default)
• Tag Open State (saw '<')
• Tag Name State (reading tag name)
• Attribute Name State
• Attribute Value State
• Self-Closing Start Tag State (saw '/')
• End Tag Open State (saw '</')
• Comment Start State (saw '<!--')
... and many more (80+ states in the spec!)

Simplified state transitions:

Data State:
  '<' → Tag Open State
  '&' → Character Reference State
  EOF → Emit EOF token
  other → Emit character token

Tag Open State:
  '!' → Markup Declaration Open State
  '/' → End Tag Open State
  letter → Tag Name State, create start tag token
  '?' → Bogus Comment State

Tag Name State:
  whitespace → Before Attribute Name State
  '/' → Self-Closing Start Tag State
  '>' → Data State, emit tag token
  letter → Append to tag name

Questions to guide implementation:

  1. How do you handle <br/> vs <br> vs <br >?
  2. What happens with <div class="foo>bar">?
  3. How do you handle <script> and <style> differently?
  4. What about <!DOCTYPE html>?

Learning milestones:

  1. Tokenize simple HTML → Basic state machine works
  2. Handle attributes → Quote handling, escaping
  3. Handle edge cases → Malformed HTML, entities
  4. Match spec behavior → Test against browser output

Project 2: Build a DOM Tree

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: Rust, Python
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Tree Construction / Data Structures
  • Software or Tool: WHATWG DOM spec
  • Main Book: N/A (MDN Web Docs)

What you’ll build: A DOM tree implementation with Document, Element, Text, and Comment nodes, supporting traversal and manipulation.

Why it teaches browser internals: The DOM is the central data structure browsers use. Understanding its shape unlocks understanding of event handling, CSS matching, and rendering.

Core challenges you’ll face:

  • Node hierarchy → maps to parent/child/sibling relationships
  • Tree construction algorithm → maps to how tokens become nodes
  • Implicit elements → maps to html, head, body auto-creation
  • DOM API methods → maps to querySelector, appendChild, etc.

Resources for key challenges:

Key Concepts:

  • Node Types: DOM Spec - Node interface
  • Tree Traversal: MDN - TreeWalker
  • Tree Construction: WHATWG HTML Spec Section 13.2.6

Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Project 1 (HTML Tokenizer)

Real world outcome:

const html = `<html><head><title>Test</title></head><body><p>Hello</p></body></html>`;
const doc = parse(html);

console.log(doc.body.firstChild.textContent);  // "Hello"

const p = doc.querySelector('p');
console.log(p.tagName);  // "P"
console.log(p.parentNode.tagName);  // "BODY"

const newDiv = doc.createElement('div');
newDiv.textContent = 'New content';
doc.body.appendChild(newDiv);

console.log(doc.body.innerHTML);
// "<p>Hello</p><div>New content</div>"

Implementation Hints:

Node class hierarchy:

Node (base class)
├── Document
├── DocumentType
├── DocumentFragment
├── Element
│   ├── HTMLElement
│   │   ├── HTMLDivElement
│   │   ├── HTMLParagraphElement
│   │   └── ...
├── CharacterData
│   ├── Text
│   └── Comment
└── Attr

Tree construction uses a stack of open elements:

Processing: <html><body><div><p>text</p></div></body></html>

Stack: [html]
Stack: [html, body]
Stack: [html, body, div]
Stack: [html, body, div, p]
- Create Text node "text", append to p
Stack: [html, body, div] ← </p> pops p
Stack: [html, body] ← </div> pops div
Stack: [html] ← </body> pops body
Stack: [] ← </html> pops html

Key methods to implement:

  • appendChild(child) - Add node to end
  • insertBefore(newNode, referenceNode) - Insert at position
  • removeChild(child) - Remove and return
  • querySelector(selector) - Find first match
  • querySelectorAll(selector) - Find all matches
  • textContent - Get/set text

Learning milestones:

  1. Build tree from tokens → Basic construction works
  2. Implement traversal → firstChild, nextSibling, etc.
  3. Add manipulation methods → appendChild, removeChild
  4. Implement querySelector → CSS selector matching

Project 3: Build a CSS Tokenizer and Parser

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: Rust, Python
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Parsing / CSS Syntax
  • Software or Tool: CSS Syntax Module spec
  • Main Book: “CSS: The Definitive Guide”

What you’ll build: A CSS parser that produces an AST of rules, selectors, and declarations.

Why it teaches browser internals: CSS parsing is simpler than HTML but has its own quirks. Understanding it is essential for selector matching and cascading.

Core challenges you’ll face:

  • Tokenizing CSS → maps to identifiers, numbers, strings, operators
  • Parsing selectors → maps to combinators, pseudo-classes
  • Parsing declarations → maps to property: value pairs
  • At-rules → maps to @media, @keyframes, @import

Resources for key challenges:

Key Concepts:

  • CSS Tokenization: CSS Syntax Module Section 4
  • Selector Parsing: CSS Selectors Level 4
  • Value Parsing: CSS Values Module

Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Basic parsing knowledge

Real world outcome:

const css = `
  .container {
    display: flex;
    background: #f0f0f0;
  }

  .container > .item:hover {
    color: red;
    transform: scale(1.1);
  }
`;

const stylesheet = parseCSS(css);
console.log(JSON.stringify(stylesheet, null, 2));
// {
//   rules: [
//     {
//       selectors: [{ type: 'class', name: 'container' }],
//       declarations: [
//         { property: 'display', value: 'flex' },
//         { property: 'background', value: '#f0f0f0' }
//       ]
//     },
//     {
//       selectors: [
//         {
//           type: 'compound',
//           parts: [
//             { type: 'class', name: 'container' },
//             { type: 'combinator', value: '>' },
//             { type: 'class', name: 'item' },
//             { type: 'pseudo-class', name: 'hover' }
//           ]
//         }
//       ],
//       declarations: [
//         { property: 'color', value: 'red' },
//         { property: 'transform', value: 'scale(1.1)' }
//       ]
//     }
//   ]
// }

Implementation Hints:

CSS grammar is straightforward:

stylesheet: rule*
rule: selector-list '{' declaration-list '}'
selector-list: selector (',' selector)*
declaration-list: declaration (';' declaration)*
declaration: property ':' value

Selector types:

  • Type: div, p, span
  • Class: .className
  • ID: #idName
  • Attribute: [attr=value]
  • Pseudo-class: :hover, :nth-child(n)
  • Pseudo-element: ::before, ::after
  • Combinators: ` ` (descendant), > (child), + (adjacent), ~ (sibling)

Questions:

  1. How do you handle nested parentheses in values like calc(100% - 20px)?
  2. How do you parse @media queries?
  3. How do you handle vendor prefixes?

Learning milestones:

  1. Tokenize CSS → Handle all token types
  2. Parse rules → Selector + declarations
  3. Parse complex selectors → Combinators, pseudo-classes
  4. Handle at-rules → @media, @keyframes

Project 4: Build a CSS Selector Matching Engine

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: Rust, Python
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: DOM Traversal / Pattern Matching
  • Software or Tool: CSS Selectors Level 4 spec
  • Main Book: N/A (Specs and implementations)

What you’ll build: A selector engine that matches CSS selectors against DOM elements, implementing querySelector and querySelectorAll.

Why it teaches browser internals: Selector matching is called millions of times during page load. Understanding its algorithms reveals browser optimization strategies.

Core challenges you’ll face:

  • Right-to-left matching → maps to why browsers match from right
  • Specificity calculation → maps to which rule wins
  • Combinator handling → maps to traversing the tree
  • Pseudo-class matching → maps to :first-child, :nth-child

Resources for key challenges:

Key Concepts:

  • Right-to-Left Matching: Browser optimization technique
  • Specificity Algorithm: CSS Cascade Module
  • Pseudo-class Semantics: Selectors Level 4

Difficulty: Advanced Time estimate: 2 weeks Prerequisites: Projects 2-3 (DOM, CSS Parser)

Real world outcome:

const doc = parseHTML(`
  <div class="container">
    <ul id="menu">
      <li class="item active">Home</li>
      <li class="item">About</li>
      <li class="item">Contact</li>
    </ul>
  </div>
`);

// querySelector
const active = querySelector(doc, '.item.active');
console.log(active.textContent);  // "Home"

// querySelectorAll
const items = querySelectorAll(doc, '.container > ul > li');
console.log(items.length);  // 3

// Complex selectors
const firstLi = querySelector(doc, 'ul#menu li:first-child');
console.log(firstLi.textContent);  // "Home"

// Specificity
console.log(calculateSpecificity('#menu .item'));  // [0, 1, 1, 0]
console.log(calculateSpecificity('ul li.active'));  // [0, 0, 1, 2]

Implementation Hints:

Why right-to-left matching?

Selector: .container ul li a

Left-to-right (naive):
1. Find all .container elements
2. For each, check if it has ul descendants
3. For each ul, check if it has li descendants
4. For each li, check if it has a descendants
→ Very expensive if .container is near root!

Right-to-left (browsers do this):
1. Find all <a> elements (usually few)
2. For each <a>, check parent is <li>
3. Check that li's ancestor is <ul>
4. Check that ul's ancestor is .container
→ Usually eliminates candidates quickly!

Matching algorithm:

match(element, selector):
  if selector is simple:
    return matchSimple(element, selector)

  if selector has combinator:
    rightPart = lastSimpleSelector(selector)
    if not matchSimple(element, rightPart):
      return false

    leftPart = selectorWithoutLastPart(selector)
    combinator = getCombinator(selector)

    if combinator is ' ' (descendant):
      for ancestor in element.ancestors:
        if match(ancestor, leftPart):
          return true
      return false

    if combinator is '>' (child):
      return match(element.parent, leftPart)

    // ... other combinators

Learning milestones:

  1. Match simple selectors → Type, class, ID, attribute
  2. Handle combinators → Descendant, child, sibling
  3. Implement pseudo-classes → :first-child, :nth-child
  4. Calculate specificity → Score selectors correctly

Project 5: Implement the CSS Cascade

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: Rust, Python
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Style Resolution / Algorithm Design
  • Software or Tool: CSS Cascade Module spec
  • Main Book: “CSS: The Definitive Guide”

What you’ll build: A style resolver that takes a DOM tree and stylesheet, and computes the final styles for each element.

Why it teaches browser internals: The cascade is how browsers determine which CSS rule wins. Understanding it explains unexpected styling behavior.

Core challenges you’ll face:

  • Origin sorting → maps to author, user, user-agent styles
  • Specificity comparison → maps to which selector wins
  • Inheritance → maps to font, color inherit by default
  • Initial and computed values → maps to percentage resolution

Resources for key challenges:

Key Concepts:

  • Cascade Algorithm: CSS Cascade Module
  • Inheritance: CSS Cascade Section 3
  • Computed Values: CSS Values Module

Difficulty: Intermediate Time estimate: 1-2 weeks Prerequisites: Projects 2-4 (DOM, CSS Parser, Selector Engine)

Real world outcome:

const html = `
  <div id="app" class="container" style="color: blue;">
    <p class="text">Hello</p>
  </div>
`;

const css = `
  p { color: black; font-size: 16px; }
  .container p { color: green; }
  #app .text { color: red; }
`;

const doc = parseHTML(html);
const stylesheet = parseCSS(css);
const styles = computeStyles(doc, stylesheet);

// Check computed styles
const pStyles = styles.get(doc.querySelector('p'));
console.log(pStyles.color);     // "red" (ID selector wins)
console.log(pStyles.fontSize);  // "16px" (from base rule)

// But inline styles win!
const divStyles = styles.get(doc.querySelector('#app'));
console.log(divStyles.color);   // "blue" (inline style)

Implementation Hints:

Cascade algorithm steps:

1. Find all declarations that match the element

2. Sort by origin and importance:
   a. User-agent normal (browser defaults)
   b. User normal (user stylesheets)
   c. Author normal (website CSS)
   d. Author !important
   e. User !important
   f. User-agent !important

3. For same origin, sort by specificity:
   - Compare (inline, id, class, type) tuples
   - [1,0,0,0] > [0,1,0,0] > [0,0,1,0] > [0,0,0,1]

4. For same specificity, use source order:
   - Later declaration wins

Handle inheritance:

Inherited properties (partial list):
- color
- font-family, font-size, font-weight
- line-height
- text-align
- visibility

Non-inherited properties (partial list):
- background
- border
- margin, padding
- width, height
- display, position

Learning milestones:

  1. Match rules to elements → Collect applicable declarations
  2. Sort by origin → User-agent < user < author
  3. Compare specificity → Calculate and compare scores
  4. Handle inheritance → Propagate inherited values

Project 6: Build a Layout Engine (Box Model)

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: Rust, C++
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 4: Expert
  • Knowledge Area: Layout / Geometry Calculation
  • Software or Tool: CSS Box Model spec
  • Main Book: “Computer Graphics from Scratch”

What you’ll build: A layout engine that calculates the position and size of every element in a document.

Why it teaches browser internals: Layout is one of the most complex parts of a browser. Understanding it reveals why certain CSS is slow and how to optimize.

Core challenges you’ll face:

  • Block layout → maps to vertical stacking
  • Inline layout → maps to text flow, line breaks
  • Box model calculation → maps to content + padding + border + margin
  • Containing blocks → maps to percentage resolution

Resources for key challenges:

Key Concepts:

  • Box Model: CSS Box Model Module
  • Block Formatting Context: CSS Display Module
  • Containing Block: CSS Positioned Layout Module

Difficulty: Expert Time estimate: 3-4 weeks Prerequisites: Projects 2-5 (DOM, CSS, Cascade)

Real world outcome:

const html = `
  <div style="width: 200px; padding: 10px; border: 1px solid black;">
    <p style="margin: 10px;">Hello</p>
    <p style="margin: 10px;">World</p>
  </div>
`;

const doc = parseHTML(html);
const layoutTree = layout(doc, viewportWidth: 800, viewportHeight: 600);

// Each node has computed layout
const divLayout = layoutTree.root.firstChild;
console.log(divLayout);
// {
//   x: 0,
//   y: 0,
//   width: 222,  // 200 + 10*2 + 1*2 (content + padding + border)
//   height: 82,  // content height + padding + border
//   contentBox: { x: 11, y: 11, width: 200, height: 60 },
//   paddingBox: { x: 1, y: 1, width: 220, height: 80 },
//   borderBox: { x: 0, y: 0, width: 222, height: 82 },
//   children: [...]
// }

Implementation Hints:

Box model visualization:

┌───────────────────────────────────────────────┐
│                  MARGIN                        │
│  ┌─────────────────────────────────────────┐  │
│  │               BORDER                     │  │
│  │  ┌───────────────────────────────────┐  │  │
│  │  │            PADDING                 │  │  │
│  │  │  ┌─────────────────────────────┐  │  │  │
│  │  │  │          CONTENT            │  │  │  │
│  │  │  │                             │  │  │  │
│  │  │  │   width × height            │  │  │  │
│  │  │  │                             │  │  │  │
│  │  │  └─────────────────────────────┘  │  │  │
│  │  │                                    │  │  │
│  │  └───────────────────────────────────┘  │  │
│  │                                          │  │
│  └─────────────────────────────────────────┘  │
│                                                │
└───────────────────────────────────────────────┘

Block layout algorithm:

layoutBlock(node, containingWidth):
  // 1. Calculate width
  contentWidth = resolveWidth(node.style.width, containingWidth)

  // 2. Layout children
  y = node.paddingTop + node.borderTop
  for child in node.children:
    if child.isBlock:
      layoutBlock(child, contentWidth)
      child.x = node.marginLeft
      child.y = y
      y += child.totalHeight + marginBetween

  // 3. Calculate height
  if node.style.height == 'auto':
    node.height = y - node.paddingTop
  else:
    node.height = resolveHeight(node.style.height)

Learning milestones:

  1. Calculate box dimensions → Content, padding, border, margin
  2. Layout block elements → Vertical stacking
  3. Handle percentages → Resolve against containing block
  4. Handle auto margins → Centering, margin collapse

Project 7: Build a Simple Paint System

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript (with Canvas)
  • Alternative Programming Languages: Rust (with graphics library)
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Graphics / Rendering
  • Software or Tool: Canvas API, WebGL
  • Main Book: “Computer Graphics from Scratch”

What you’ll build: A paint system that takes a layout tree and draws it to a canvas.

Why it teaches browser internals: Painting is where the abstract DOM becomes visible pixels. Understanding it reveals optimization strategies like layer promotion.

Core challenges you’ll face:

  • Drawing order (z-index) → maps to stacking contexts
  • Clipping and overflow → maps to overflow: hidden
  • Background and borders → maps to proper rendering order
  • Text rendering → maps to font metrics, line height

Resources for key challenges:

Key Concepts:

  • Painting Order: CSS Painting Order
  • Stacking Contexts: CSS Positioned Layout
  • Canvas Drawing: MDN Canvas Tutorial

Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Project 6 (Layout Engine)

Real world outcome:

const html = `
  <div style="background: #f0f0f0; padding: 20px; border: 2px solid #333;">
    <h1 style="color: #0066cc; font-size: 24px;">Hello Browser!</h1>
    <p style="color: #333;">This is rendered by our engine.</p>
  </div>
`;

const doc = parseHTML(html);
const layoutTree = layout(doc, 800, 600);
const canvas = document.getElementById('output');
const ctx = canvas.getContext('2d');

// Paint the layout tree to canvas
paint(layoutTree, ctx);

// Result: A visual rendering of the HTML on the canvas!

Implementation Hints:

Painting order (for each stacking context):

1. Background of root element
2. Child stacking contexts with negative z-index
3. Block-level descendants (non-positioned)
4. Floats
5. In-flow inline content
6. Positioned descendants with z-index: auto or 0
7. Child stacking contexts with positive z-index

Paint commands:

paint(node, ctx):
  // 1. Paint background
  if node.style.background:
    ctx.fillStyle = node.style.background
    ctx.fillRect(node.x, node.y, node.width, node.height)

  // 2. Paint border
  if node.style.border:
    ctx.strokeStyle = node.style.borderColor
    ctx.lineWidth = node.style.borderWidth
    ctx.strokeRect(...)

  // 3. Paint children (recursive)
  for child in node.children:
    paint(child, ctx)

  // 4. Paint text
  if node.isText:
    ctx.fillStyle = node.style.color
    ctx.font = `${node.style.fontSize} ${node.style.fontFamily}`
    ctx.fillText(node.text, node.x, node.y)

Learning milestones:

  1. Render backgrounds → Fill rectangles
  2. Render borders → Stroke rectangles
  3. Render text → Font rendering
  4. Handle z-index → Stacking contexts

Project 8: Build a JavaScript Interpreter

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: C, Rust
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 4: Expert
  • Knowledge Area: Interpreters / Language Implementation
  • Software or Tool: Acorn parser (or build your own)
  • Main Book: “Crafting Interpreters” by Robert Nystrom

What you’ll build: A JavaScript interpreter that can execute a subset of JavaScript, including variables, functions, closures, and objects.

Why it teaches browser internals: V8 and other JS engines are at the heart of browsers. Understanding interpretation unlocks performance optimization.

Core challenges you’ll face:

  • Parsing to AST → maps to syntax analysis
  • Environment/scope → maps to lexical scoping, closures
  • Function execution → maps to call stack, activation records
  • Object semantics → maps to prototype chain, this binding

Resources for key challenges:

Key Concepts:

  • AST Interpretation: “Crafting Interpreters” Part II
  • Closures: “Crafting Interpreters” Chapter 11
  • Objects: “Crafting Interpreters” Chapter 12

Difficulty: Expert Time estimate: 4-6 weeks Prerequisites: Understanding of programming language concepts

Real world outcome:

const code = `
  function makeCounter() {
    let count = 0;
    return function() {
      count = count + 1;
      return count;
    };
  }

  const counter = makeCounter();
  console.log(counter());  // 1
  console.log(counter());  // 2
  console.log(counter());  // 3
`;

const result = interpret(code);
// Output:
// 1
// 2
// 3

Implementation Hints:

Interpreter architecture:

Source Code
     │
     ▼
┌─────────┐     ┌─────────┐     ┌─────────┐
│  Lexer  │ ──▶ │ Parser  │ ──▶ │Evaluator│ ──▶ Result
└─────────┘     └─────────┘     └─────────┘
  tokens           AST         execution

Environment for scoping:

class Environment {
  values: Map<string, any>;
  parent: Environment | null;

  get(name) {
    if (this.values.has(name)) {
      return this.values.get(name);
    }
    if (this.parent) {
      return this.parent.get(name);
    }
    throw new Error(`Undefined variable: ${name}`);
  }

  set(name, value) {
    this.values.set(name, value);
  }
}

Evaluating expressions:

evaluate(node, env):
  switch (node.type):
    case 'NumberLiteral':
      return node.value

    case 'BinaryExpression':
      left = evaluate(node.left, env)
      right = evaluate(node.right, env)
      switch (node.operator):
        case '+': return left + right
        case '-': return left - right
        // ...

    case 'Identifier':
      return env.get(node.name)

    case 'FunctionDeclaration':
      // Capture current environment for closure
      return { params: node.params, body: node.body, closure: env }

    case 'CallExpression':
      func = evaluate(node.callee, env)
      args = node.arguments.map(a => evaluate(a, env))
      // Create new env with func.closure as parent
      // Bind params to args
      // Evaluate func.body

Learning milestones:

  1. Evaluate expressions → Numbers, operators, variables
  2. Implement functions → Declaration, calling
  3. Implement closures → Capture environment
  4. Add objects → Properties, methods, this

Project 9: Implement the Event Loop

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: Python, Rust
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Concurrency / Async Programming
  • Software or Tool: N/A (custom implementation)
  • Main Book: N/A (JavaScript.info)

What you’ll build: A browser-like event loop with task queue, microtask queue, and requestAnimationFrame handling.

Why it teaches browser internals: The event loop determines JavaScript’s execution model. Understanding it is crucial for writing performant, non-blocking code.

Core challenges you’ll face:

  • Task vs microtask priority → maps to Promise timing
  • Microtask drain → maps to all microtasks before next task
  • Rendering integration → maps to rAF timing
  • setTimeout resolution → maps to minimum 4ms delay

Resources for key challenges:

Key Concepts:

  • Task Queue: HTML Spec - Event Loop Processing Model
  • Microtasks: ECMA-262 Jobs and Job Queues
  • Animation Frames: HTML Spec - Update the Rendering

Difficulty: Advanced Time estimate: 1-2 weeks Prerequisites: Project 8 (JavaScript Interpreter)

Real world outcome:

// Your event loop simulates browser behavior
const loop = new EventLoop();

loop.queueTask(() => console.log('task 1'));
loop.queueMicrotask(() => console.log('microtask 1'));
loop.queueTask(() => console.log('task 2'));
loop.queueMicrotask(() => console.log('microtask 2'));

loop.setTimeout(() => console.log('timeout'), 0);

loop.queueTask(() => {
  console.log('task 3');
  loop.queueMicrotask(() => console.log('microtask 3'));
});

loop.run();

// Output (correct order):
// task 1
// microtask 1
// microtask 2
// task 2
// task 3
// microtask 3
// timeout

Implementation Hints:

Event loop structure:

class EventLoop {
  taskQueue: Task[] = [];
  microtaskQueue: Microtask[] = [];
  rafCallbacks: Function[] = [];

  run() {
    while (true) {
      // 1. Run oldest task
      if (this.taskQueue.length > 0) {
        const task = this.taskQueue.shift();
        task.run();
      }

      // 2. Run ALL microtasks
      while (this.microtaskQueue.length > 0) {
        const microtask = this.microtaskQueue.shift();
        microtask.run();
      }

      // 3. Render if needed (~16ms has passed)
      if (this.shouldRender()) {
        // Run rAF callbacks
        const callbacks = this.rafCallbacks;
        this.rafCallbacks = [];
        for (const cb of callbacks) {
          cb(performance.now());
        }

        // Actually render (in real browser)
        this.render();
      }

      // 4. Idle (wait for new tasks)
      if (this.taskQueue.length === 0) {
        this.waitForTasks();
      }
    }
  }
}

Key insight: Microtasks run after every task, not just between tasks:

task 1 runs
  └── spawns microtask A
  └── spawns microtask B
microtask A runs
  └── spawns microtask C
microtask B runs
microtask C runs  ← All microtasks drain before next task!
task 2 runs

Learning milestones:

  1. Basic task queue → FIFO task execution
  2. Add microtasks → Priority over tasks
  3. Microtask drain → Run all before next task
  4. Add rAF → Frame-aligned callbacks

Project 10: Build an HTTP Client

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript (Node.js)
  • Alternative Programming Languages: Python, Go, Rust
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Networking / Protocol Implementation
  • Software or Tool: Node.js net/tls modules
  • Main Book: “TCP/IP Illustrated”

What you’ll build: An HTTP/1.1 client that handles DNS, TCP, TLS, and HTTP protocol parsing.

Why it teaches browser internals: Understanding the network stack reveals how browsers optimize loading (keep-alive, multiplexing, caching).

Core challenges you’ll face:

  • DNS resolution → maps to hostname to IP lookup
  • TCP connection → maps to three-way handshake
  • HTTP parsing → maps to headers, chunked encoding
  • Connection reuse → maps to keep-alive, pooling

Resources for key challenges:

Key Concepts:

  • HTTP Message Format: RFC 7230 Section 3
  • Keep-Alive: RFC 7230 Section 6
  • Chunked Transfer: RFC 7230 Section 4

Difficulty: Intermediate Time estimate: 2 weeks Prerequisites: Basic networking knowledge

Real world outcome:

const client = new HttpClient();

// Simple GET request
const response = await client.get('https://example.com/api/users');
console.log(response.status);  // 200
console.log(response.headers['content-type']);  // 'application/json'
console.log(response.body);  // '{"users": [...]}'

// POST with body
const postResponse = await client.post('https://api.example.com/data', {
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Test' })
});

// Connection reuse (keep-alive)
const r1 = await client.get('https://example.com/a');  // New connection
const r2 = await client.get('https://example.com/b');  // Reuses connection!

Implementation Hints:

HTTP request format:

GET /path HTTP/1.1\r\n
Host: example.com\r\n
User-Agent: MyBrowser/1.0\r\n
Accept: */*\r\n
Connection: keep-alive\r\n
\r\n

HTTP response parsing:

HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 1234\r\n
\r\n
<html>...</html>

Parsing state machine:

1. Read status line: "HTTP/1.1 200 OK\r\n"
   - Parse version, status code, reason

2. Read headers until empty line: "\r\n"
   - Parse "Header-Name: value\r\n"
   - Build headers object

3. Read body:
   - If Content-Length: Read exactly that many bytes
   - If Transfer-Encoding: chunked: Read chunks
   - Otherwise: Read until connection closes

Learning milestones:

  1. DNS resolution → Resolve hostname to IP
  2. TCP connection → Connect and send data
  3. HTTP parsing → Parse response headers and body
  4. Keep-alive → Reuse connections

Project 11: Implement Same-Origin Policy

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: Any
  • Coolness Level: Level 3: Genuinely Clever
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 2: Intermediate
  • Knowledge Area: Security / Access Control
  • Software or Tool: N/A
  • Main Book: “Web Application Security” by Andrew Hoffman

What you’ll build: A security layer that enforces same-origin policy for DOM access, cookies, and XHR.

Why it teaches browser internals: SOP is the foundation of web security. Understanding it reveals why certain attacks work and how to prevent them.

Core challenges you’ll face:

  • Origin comparison → maps to protocol + host + port
  • Cross-origin restrictions → maps to what’s blocked
  • Exceptions → maps to images, scripts, CORS
  • Storage isolation → maps to localStorage per origin

Resources for key challenges:

Key Concepts:

  • Origin Definition: RFC 6454
  • Cross-Origin Restrictions: Fetch Spec
  • CORS: Fetch Spec - CORS Protocol

Difficulty: Intermediate Time estimate: 1 week Prerequisites: Projects 2, 8 (DOM, JS Interpreter)

Real world outcome:

// Your mini-browser enforces SOP

// Page from https://a.com tries to access DOM of https://b.com
const pageA = browser.createWindow('https://a.com');
const pageB = browser.createWindow('https://b.com');

// This should FAIL
try {
  pageA.frames[0].document.body;  // Accessing b.com's DOM
} catch (e) {
  console.log(e);  // "SecurityError: Cross-origin access denied"
}

// This should FAIL
try {
  const xhr = pageA.XMLHttpRequest();
  xhr.open('GET', 'https://b.com/api/secret');
  xhr.send();
} catch (e) {
  console.log(e);  // "SecurityError: Cross-origin request blocked"
}

// But images and scripts are allowed (with restrictions)
pageA.document.createElement('img').src = 'https://b.com/image.png';  // OK

Implementation Hints:

Origin structure:

class Origin {
  scheme: string;   // 'https'
  host: string;     // 'example.com'
  port: number;     // 443

  equals(other: Origin): boolean {
    return this.scheme === other.scheme &&
           this.host === other.host &&
           this.port === other.port;
  }

  toString(): string {
    return `${this.scheme}://${this.host}:${this.port}`;
  }
}

SOP checks:

canAccessDOM(from: Origin, to: Origin): boolean {
  return from.equals(to);
}

canMakeRequest(from: Origin, to: Origin, withCredentials: boolean): boolean {
  // For simple requests, always allowed (but response may be blocked)
  // For non-simple requests, need preflight
  // Check CORS headers in response
}

canAccessStorage(origin: Origin, key: string): Storage {
  // Each origin gets isolated storage
  return storageByOrigin.get(origin.toString());
}

What’s allowed cross-origin:

  • Embedding: <img>, <script>, <link>, <iframe>, <video>
  • Writes: Links, redirects, form submissions
  • Reads: BLOCKED by default (need CORS)

Learning milestones:

  1. Define origins → Parse and compare
  2. Block DOM access → Cross-origin frame blocking
  3. Block XHR/Fetch → Without CORS headers
  4. Isolate storage → Per-origin localStorage

Project 12: Build a Service Worker

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: JavaScript
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Caching / Offline Support
  • Software or Tool: Service Worker API
  • Main Book: N/A (web.dev guides)

What you’ll build: A simplified service worker runtime that intercepts fetch requests and manages caches.

Why it teaches browser internals: Service workers revolutionized web apps by enabling offline support and programmatic caching.

Core challenges you’ll face:

  • Lifecycle management → maps to install, activate, fetch events
  • Request interception → maps to proxy pattern
  • Cache management → maps to Cache API
  • Update flow → maps to waiting, skipWaiting

Resources for key challenges:

Key Concepts:

  • Service Worker Lifecycle: MDN Service Worker API
  • Cache API: MDN Cache interface
  • Fetch Event: Service Worker Spec

Difficulty: Advanced Time estimate: 2 weeks Prerequisites: Projects 9-10 (Event Loop, HTTP)

Real world outcome:

// Your service worker runtime
const runtime = new ServiceWorkerRuntime();

// Register a service worker
runtime.register('/sw.js', { scope: '/' });

// Service worker code (sw.js)
const sw = `
  self.addEventListener('install', (event) => {
    event.waitUntil(
      caches.open('v1').then((cache) => {
        return cache.addAll(['/index.html', '/styles.css', '/app.js']);
      })
    );
  });

  self.addEventListener('fetch', (event) => {
    event.respondWith(
      caches.match(event.request).then((response) => {
        return response || fetch(event.request);
      })
    );
  });
`;

// First load: Files fetched from network, cached
runtime.fetch('https://example.com/index.html');

// Second load (offline): Served from cache!
runtime.goOffline();
const cached = runtime.fetch('https://example.com/index.html');
console.log(cached);  // Returns cached response

Implementation Hints:

Service worker lifecycle:

      ┌──────────────┐
      │   parsed     │
      └──────┬───────┘
             │ install event
             ▼
      ┌──────────────┐
      │  installing  │
      └──────┬───────┘
             │ installed
             ▼
      ┌──────────────┐
      │   waiting    │ ← Wait for old SW to stop controlling pages
      └──────┬───────┘
             │ activate event (old SW gone)
             ▼
      ┌──────────────┐
      │   activated  │ ← Now controls all pages in scope
      └──────┬───────┘
             │
             ▼
      ┌──────────────┐
      │   running    │ ← Handles fetch events
      └──────────────┘

Fetch interception:

class ServiceWorkerRuntime {
  async fetch(request) {
    const sw = this.getControllingWorker(request.url);

    if (sw) {
      // Create FetchEvent
      const event = new FetchEvent(request);

      // Dispatch to service worker
      sw.dispatchEvent('fetch', event);

      // If respondWith was called, use that response
      if (event.response) {
        return event.response;
      }
    }

    // Fall through to network
    return this.networkFetch(request);
  }
}

Learning milestones:

  1. Lifecycle events → Install, activate
  2. Fetch interception → respondWith
  3. Cache API → Store and retrieve responses
  4. Update flow → New versions, skipWaiting

Project 13: Implement GPU Compositing Simulation

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript (with Canvas/WebGL)
  • Alternative Programming Languages: Rust, C++
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 1. The “Resume Gold”
  • Difficulty: Level 4: Expert
  • Knowledge Area: Graphics / GPU Programming
  • Software or Tool: WebGL, Canvas
  • Main Book: “WebGL Programming Guide”

What you’ll build: A compositing system that separates elements into layers and composites them on the GPU.

Why it teaches browser internals: GPU compositing is how browsers achieve smooth 60fps animations. Understanding it reveals why transform/opacity are fast.

Core challenges you’ll face:

  • Layer creation → maps to when to promote to layer
  • Texture upload → maps to rasterizing to GPU memory
  • Layer transforms → maps to matrix operations
  • Compositing → maps to blending layers together

Resources for key challenges:

Key Concepts:

  • Layer Tree: Chromium Design Docs
  • Texture Atlases: GPU optimization
  • Transform Matrices: Linear algebra basics

Difficulty: Expert Time estimate: 3-4 weeks Prerequisites: Project 7 (Paint System), linear algebra

Real world outcome:

// Elements with transforms get their own GPU layer
const html = `
  <div style="transform: translateZ(0);">
    <p>This is on a GPU layer!</p>
  </div>
  <div style="opacity: 0.5; transition: opacity 0.3s;">
    <p>This too, because opacity!</p>
  </div>
`;

const compositor = new Compositor();
const layerTree = compositor.buildLayerTree(doc);

console.log(layerTree);
// {
//   root: Layer {
//     children: [
//       Layer { reason: 'transform', ... },
//       Layer { reason: 'opacity', ... }
//     ]
//   }
// }

// Animate opacity (no repaint, just compositor!)
compositor.animateOpacity(layerTree.children[1], 1.0, duration: 300);

// Composite layers with WebGL
compositor.composite(layerTree);

Implementation Hints:

When browsers create layers:

Reasons for layer promotion:
• 3D transform (translateZ, rotate3d, perspective)
• will-change: transform, opacity
• Accelerated CSS animation
• <video>, <canvas>, <iframe>
• Overlapping a composited layer

Layer tree structure:

class Layer {
  bounds: Rect;
  transform: Matrix4x4;
  opacity: number;
  texture: WebGLTexture;  // Rasterized content
  children: Layer[];
}

Compositing process:

1. Build layer tree from render tree
   - Identify elements needing own layer
   - Create layer hierarchy

2. Rasterize each layer to texture
   - Paint layer contents to bitmap
   - Upload bitmap to GPU as texture

3. Composite layers
   - For each layer (back to front):
     - Apply transform matrix
     - Apply opacity
     - Draw texture to screen

Transform animation (GPU only):

animateTransform(layer, from, to, duration):
  // No repaint needed!
  // Just update the transform matrix each frame
  requestAnimationFrame((time) => {
    const t = (time - start) / duration;
    layer.transform = interpolate(from, to, t);
    compositor.composite();  // Just recomposite!
  });

Learning milestones:

  1. Create layer tree → Identify layers
  2. Rasterize to textures → Paint to GPU
  3. Apply transforms → Matrix operations
  4. Composite → Blend layers together

Project 14: Build a Simple DevTools

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: JavaScript
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 2. The “Micro-SaaS / Pro Tool”
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Developer Tools / Debugging
  • Software or Tool: Chrome DevTools Protocol
  • Main Book: N/A (Chrome DevTools docs)

What you’ll build: Developer tools that inspect DOM, show computed styles, and profile performance.

Why it teaches browser internals: DevTools are a window into browser internals. Building them requires understanding the inspection APIs.

Core challenges you’ll face:

  • DOM inspection → maps to tree visualization
  • Style computation → maps to showing matched rules
  • Performance profiling → maps to measuring paint/layout
  • Network inspection → maps to timing breakdown

Resources for key challenges:

Key Concepts:

  • DOM Protocol: CDP DOM domain
  • CSS Protocol: CDP CSS domain
  • Performance Protocol: CDP Performance domain

Difficulty: Advanced Time estimate: 2-3 weeks Prerequisites: Projects 1-7 (all rendering projects)

Real world outcome:

// Your DevTools inspect a running page
const devtools = new DevTools(browser);

// Elements panel
const domTree = devtools.getDOMTree();
console.log(domTree);
// {
//   nodeId: 1,
//   nodeName: 'HTML',
//   children: [
//     { nodeId: 2, nodeName: 'HEAD', ... },
//     { nodeId: 3, nodeName: 'BODY', ... }
//   ]
// }

// Computed styles
const styles = devtools.getComputedStyle(nodeId: 5);
console.log(styles);
// {
//   color: { value: 'rgb(0, 0, 0)', from: 'p { color: black }' },
//   fontSize: { value: '16px', from: 'user-agent stylesheet' }
// }

// Performance timeline
devtools.startProfiling();
// ... user interacts with page ...
const timeline = devtools.stopProfiling();
console.log(timeline);
// [
//   { type: 'script', duration: 50, function: 'handleClick' },
//   { type: 'style', duration: 2 },
//   { type: 'layout', duration: 15 },
//   { type: 'paint', duration: 8 }
// ]

Implementation Hints:

DevTools architecture:

┌─────────────────┐     Protocol      ┌─────────────────┐
│    DevTools     │ ◄───────────────► │    Browser      │
│   (Frontend)    │     (JSON-RPC)    │   (Backend)     │
└─────────────────┘                   └─────────────────┘
     │                                       │
     ▼                                       ▼
┌─────────────────┐                   ┌─────────────────┐
│   Elements UI   │                   │   DOM Agent     │
│   Styles Panel  │                   │   CSS Agent     │
│   Network Panel │                   │   Network Agent │
│   Performance   │                   │   Profiler      │
└─────────────────┘                   └─────────────────┘

DOM inspection API:

getDocument() → { root: Node }
querySelector(nodeId, selector) → nodeId
getOuterHTML(nodeId) → string
setOuterHTML(nodeId, html) → void
getComputedStyle(nodeId) → { property: value }[]
getMatchedStyles(nodeId) → { rule, selectors, declarations }[]

Performance API:

startProfiling()
stopProfiling() → Timeline

Timeline = {
  events: [
    { type: 'script', startTime, endTime, callStack },
    { type: 'style', startTime, endTime, elementsAffected },
    { type: 'layout', startTime, endTime, rootBounds },
    { type: 'paint', startTime, endTime, clipRect }
  ]
}

Learning milestones:

  1. DOM inspection → Tree view, selection
  2. Style inspection → Computed and matched rules
  3. Network timeline → Request waterfall
  4. Performance profiler → Script/layout/paint timing

Project 15: Build a Toy Web Browser

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: Rust
  • Alternative Programming Languages: C++, TypeScript
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 4. The “Open Core” Infrastructure
  • Difficulty: Level 5: Master
  • Knowledge Area: Complete Browser / System Integration
  • Software or Tool: GTK/Qt for UI, Servo for inspiration
  • Main Book: “browser.engineering” (online book)

What you’ll build: A complete (minimal) web browser that can render simple web pages with HTML, CSS, and basic JavaScript.

Why it teaches browser internals: This capstone project integrates everything you’ve learned into a working browser.

Core challenges you’ll face:

  • Integration → maps to connecting all components
  • Window management → maps to tabs, navigation
  • Resource loading → maps to parallel fetching
  • User interaction → maps to scrolling, clicking

Resources for key challenges:

Key Concepts:

  • Integration: All previous concepts
  • Window Management: Platform-specific UI
  • Navigation: History, back/forward

Difficulty: Master Time estimate: 3-6 months Prerequisites: All previous projects

Real world outcome:

$ ./my-browser https://example.com

# A window opens showing the rendered page!
# Features:
# - Address bar with navigation
# - Back/forward buttons
# - Page title in tab
# - Clickable links
# - Basic CSS layout
# - Simple JavaScript execution

# The browser can render:
# - Static HTML pages
# - Pages with external CSS
# - Basic interactive pages

Implementation Hints:

Browser architecture:

┌─────────────────────────────────────────────────────────────┐
│                        BROWSER SHELL                         │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Address Bar  │  Back  │ Forward │ Reload │  Menu  │    │
│  └─────────────────────────────────────────────────────┘    │
│  ┌─────────────────────────────────────────────────────┐    │
│  │                    RENDER AREA                       │    │
│  │                                                      │    │
│  │    ┌──────────────────────────────────────────┐     │    │
│  │    │                                          │     │    │
│  │    │           RENDERED CONTENT               │     │    │
│  │    │                                          │     │    │
│  │    └──────────────────────────────────────────┘     │    │
│  │                                                      │    │
│  └─────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────┘

Main loop:

main():
  window = createWindow()

  while window.isOpen():
    // Handle UI events
    for event in window.pollEvents():
      if event.type == 'addressBarEnter':
        navigate(event.url)
      elif event.type == 'click':
        handleClick(event.x, event.y)
      elif event.type == 'scroll':
        handleScroll(event.delta)

    // Run event loop tick (timers, promises)
    eventLoop.tick()

    // Render if needed
    if needsRender:
      layout()
      paint()
      composite()
      window.present()

Navigation flow:

navigate(url):
  1. Resolve URL (relative → absolute)
  2. Fetch main resource (HTML)
  3. Parse HTML → DOM
  4. Fetch subresources (CSS, JS, images)
  5. Parse CSS → CSSOM
  6. Execute JavaScript
  7. Compute styles
  8. Layout
  9. Paint
  10. Display

Learning milestones:

  1. Window with address bar → Basic UI
  2. Load and render HTML → Static pages
  3. Support CSS → Styled pages
  4. Basic interactivity → Links, forms
  5. JavaScript support → Dynamic pages

Project 16: Implement WebAssembly Runtime (Bonus)

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: Rust
  • Alternative Programming Languages: C++
  • Coolness Level: Level 5: Pure Magic (Super Cool)
  • Business Potential: 4. The “Open Core” Infrastructure
  • Difficulty: Level 5: Master
  • Knowledge Area: Virtual Machines / Low-Level Execution
  • Software or Tool: WASM spec
  • Main Book: “WebAssembly: The Definitive Guide”

What you’ll build: A WebAssembly interpreter that can execute WASM modules.

Why it teaches browser internals: WebAssembly is how browsers run near-native code. Understanding it reveals the future of web performance.

Core challenges you’ll face:

  • Binary format parsing → maps to WASM module structure
  • Stack machine execution → maps to operand stack
  • Memory management → maps to linear memory
  • Host function binding → maps to JavaScript interop

Resources for key challenges:

Key Concepts:

  • Module Structure: WASM Binary Format
  • Stack Machine: WASM Execution
  • Linear Memory: WASM Memory Model

Difficulty: Master Time estimate: 2-3 months Prerequisites: Understanding of VMs, binary formats

Real world outcome:

// Load and execute WASM
const wasm = new WasmRuntime();

// Load a module that exports a factorial function
const module = await wasm.loadModule('factorial.wasm');

// Call the exported function
const result = module.exports.factorial(5);
console.log(result);  // 120

// With imported functions
const imports = {
  env: {
    log: (n) => console.log('WASM says:', n)
  }
};
const moduleWithImports = await wasm.loadModule('example.wasm', imports);
moduleWithImports.exports.run();  // "WASM says: 42"

Project 17: Build a Performance Profiler

  • File: LEARN_BROWSER_INTERNALS.md
  • Main Programming Language: TypeScript
  • Alternative Programming Languages: Rust
  • Coolness Level: Level 4: Hardcore Tech Flex
  • Business Potential: 3. The “Service & Support” Model
  • Difficulty: Level 3: Advanced
  • Knowledge Area: Performance / Profiling
  • Software or Tool: Performance APIs
  • Main Book: N/A (web.dev Performance guides)

What you’ll build: A performance profiler that measures and visualizes rendering performance.

Why it teaches browser internals: Understanding what causes jank requires measuring every step of the rendering pipeline.

Core challenges you’ll face:

  • Instrumentation → maps to adding timing points
  • Frame timing → maps to 60fps = 16.67ms budget
  • Long task detection → maps to > 50ms blocks input
  • Visualization → maps to flame charts, timelines

Resources for key challenges:

Key Concepts:

  • Frame Budget: 16.67ms for 60fps
  • Long Tasks: Tasks > 50ms
  • Core Web Vitals: LCP, FID, CLS

Difficulty: Advanced Time estimate: 2 weeks Prerequisites: Projects 6-9 (Layout, Paint, Event Loop)

Real world outcome:

const profiler = new PerformanceProfiler(browser);

profiler.start();

// User interacts with page...

const report = profiler.stop();
console.log(report);
// {
//   frames: [
//     { timestamp: 0, duration: 12, breakdown: { script: 5, style: 2, layout: 3, paint: 2 } },
//     { timestamp: 16, duration: 45, breakdown: { script: 40, style: 1, layout: 2, paint: 2 } },  // JANK!
//     { timestamp: 62, duration: 14, breakdown: { script: 8, style: 2, layout: 2, paint: 2 } },
//   ],
//   longTasks: [
//     { startTime: 16, duration: 45, attribution: { function: 'handleScroll', file: 'app.js', line: 234 } }
//   ],
//   fps: {
//     average: 45,
//     min: 22,
//     max: 60,
//     jankFrames: 3
//   }
// }

// Generate flame chart
profiler.generateFlameChart(report);  // Visual output

Implementation Hints:

Instrumentation points:

Frame lifecycle:
┌─────────────────────────────────────────────────────────┐
│ Frame Start                                              │
│ ├── Input events                    ← Measure           │
│ ├── requestAnimationFrame           ← Measure           │
│ ├── JavaScript execution            ← Measure           │
│ ├── Style calculation               ← Measure           │
│ ├── Layout                          ← Measure           │
│ ├── Paint                           ← Measure           │
│ └── Composite                       ← Measure           │
│ Frame End                                                │
│                                                          │
│ Budget: 16.67ms total                                   │
└─────────────────────────────────────────────────────────┘

Learning milestones:

  1. Instrument rendering → Add timing points
  2. Detect jank → Frames > 16.67ms
  3. Detect long tasks → Script > 50ms
  4. Visualize → Flame charts, timelines

Project Comparison Table

# Project Difficulty Time Key Skill Fun
1 HTML Tokenizer ⭐⭐ 1-2 weeks State Machine ⭐⭐⭐
2 DOM Tree ⭐⭐ 1-2 weeks Data Structures ⭐⭐⭐
3 CSS Parser ⭐⭐ 1-2 weeks Parsing ⭐⭐⭐
4 Selector Engine ⭐⭐⭐ 2 weeks Algorithm Design ⭐⭐⭐⭐
5 CSS Cascade ⭐⭐ 1-2 weeks Algorithm ⭐⭐⭐
6 Layout Engine ⭐⭐⭐⭐ 3-4 weeks Geometry ⭐⭐⭐⭐
7 Paint System ⭐⭐⭐ 2-3 weeks Graphics ⭐⭐⭐⭐⭐
8 JS Interpreter ⭐⭐⭐⭐ 4-6 weeks Languages ⭐⭐⭐⭐⭐
9 Event Loop ⭐⭐⭐ 1-2 weeks Concurrency ⭐⭐⭐⭐
10 HTTP Client ⭐⭐ 2 weeks Networking ⭐⭐⭐
11 Same-Origin Policy ⭐⭐ 1 week Security ⭐⭐⭐
12 Service Worker ⭐⭐⭐ 2 weeks Caching ⭐⭐⭐⭐
13 GPU Compositing ⭐⭐⭐⭐ 3-4 weeks GPU ⭐⭐⭐⭐⭐
14 DevTools ⭐⭐⭐ 2-3 weeks Tools ⭐⭐⭐⭐
15 Toy Browser ⭐⭐⭐⭐⭐ 3-6 months Integration ⭐⭐⭐⭐⭐
16 WASM Runtime ⭐⭐⭐⭐⭐ 2-3 months VMs ⭐⭐⭐⭐⭐
17 Profiler ⭐⭐⭐ 2 weeks Performance ⭐⭐⭐⭐

Phase 1: Foundations (6-8 weeks)

Build the core parsing and data structures:

  1. Project 1: HTML Tokenizer - Understand HTML parsing
  2. Project 2: DOM Tree - Build the core data structure
  3. Project 3: CSS Parser - Parse stylesheets
  4. Project 4: Selector Engine - Match CSS to elements
  5. Project 5: CSS Cascade - Compute final styles

Phase 2: Rendering (6-8 weeks)

Build the visual pipeline:

  1. Project 6: Layout Engine - Calculate positions/sizes
  2. Project 7: Paint System - Draw to canvas
  3. Project 13: GPU Compositing - Smooth animations

Phase 3: JavaScript (4-6 weeks)

Add dynamic capabilities:

  1. Project 8: JS Interpreter - Execute code
  2. Project 9: Event Loop - Async execution model

Phase 4: Networking & Security (3-4 weeks)

Handle resources and security:

  1. Project 10: HTTP Client - Fetch resources
  2. Project 11: Same-Origin Policy - Security model
  3. Project 12: Service Worker - Offline support

Phase 5: Integration & Tools (2-4 months)

Put it all together:

  1. Project 14: DevTools - Inspect your work
  2. Project 17: Profiler - Measure performance
  3. Project 15: Toy Browser - The capstone!

Bonus:

  1. Project 16: WASM Runtime - Advanced topic

Summary

# Project Main Language
1 HTML Tokenizer TypeScript
2 DOM Tree TypeScript
3 CSS Parser TypeScript
4 CSS Selector Engine TypeScript
5 CSS Cascade TypeScript
6 Layout Engine (Box Model) TypeScript
7 Paint System TypeScript
8 JavaScript Interpreter TypeScript
9 Event Loop TypeScript
10 HTTP Client TypeScript
11 Same-Origin Policy TypeScript
12 Service Worker Runtime TypeScript
13 GPU Compositing Simulation TypeScript
14 DevTools TypeScript
15 Toy Web Browser Rust
16 WebAssembly Runtime Rust
17 Performance Profiler TypeScript

Resources

Essential Reading

Tutorials & Articles

Books

  • “Crafting Interpreters” by Robert Nystrom - For JS interpreter
  • “Computer Graphics from Scratch” by Gabriel Gambetta - For rendering
  • “TCP/IP Illustrated” by W. Richard Stevens - For networking
  • “Web Application Security” by Andrew Hoffman - For security

Reference Implementations

Specifications


Total Estimated Time: 12-18 months of dedicated study

After completion: You’ll understand every step from typing a URL to seeing pixels on screen. This knowledge is invaluable for web performance optimization, framework design, security research, and potentially contributing to browser development itself.