Web & Frontend Internals: Browser Engine, JavaScript Runtime & React Reconciliation¶
Under the Hood: How a browser parses HTML into a render tree, how the JavaScript event loop processes microtasks and macrotasks, how React's reconciler diffs virtual DOM trees, how V8 JIT-compiles hot functions — the exact pipelines, data structures, and scheduling mechanics behind modern web development.
Reading contract: This is a browser-engine study guide for frontend engineers. HTML/CSS/ECMAScript and Web protocols are standard-level facts; Chromium/V8, React, service-worker, and performance behavior must name browser, engine, React version, device, flags, and capture date. Pipeline diagrams are simplified execution models, while millisecond slices, speed ratios, and Core Web Vitals examples are measurements or versioned policy. A section is complete only with a reproducible profile or trace, success and failure conditions, and a fallback that works without the optimization. Re-profile after browser or framework upgrades.
1. Browser Rendering Pipeline: Critical Path¶
flowchart LR
subgraph "Critical Rendering Path"
HTML["HTML bytes\n(network)"]
DOM["DOM Tree\n(tokenizer → parser\n→ element nodes)"]
CSSOM["CSSOM Tree\n(parallel CSS parse\n→ style rules)"]
RENDER["Render Tree\n(DOM + CSSOM merged\ninvisible nodes excluded)"]
LAYOUT["Layout (Reflow)\n(box model compute:\nwidth, height, position\nCPU-intensive)"]
PAINT["Paint\n(draw to layers:\nbackgrounds, borders, text\nrasterize to pixels)"]
COMPOSITE["Composite\n(GPU: merge layers\nwith transforms/opacity\nGPU-accelerated)"]
HTML --> DOM
HTML --> CSSOM
DOM --> RENDER
CSSOM --> RENDER
RENDER --> LAYOUT --> PAINT --> COMPOSITE
end
HTML Tokenizer State Machine¶
The HTML tokenizer is a state machine with ~80 states. It cannot simply regex-parse HTML due to context-sensitive rules:
stateDiagram-v2
[*] --> Data: Initial state
Data --> TagOpen: < character
TagOpen --> StartTagName: [a-z]
TagOpen --> EndTagOpen: /
StartTagName --> BeforeAttributeName: whitespace
StartTagName --> Data: >
BeforeAttributeName --> AttributeName: [a-z]
AttributeName --> BeforeAttributeValue: =
BeforeAttributeValue --> AttributeValueDoubleQuoted: "
AttributeValueDoubleQuoted --> AfterAttributeValue: "
AfterAttributeValue --> Data: >
Data --> RCDATA: title/textarea start tag
RCDATA --> Data: matching end tag
Script blocking: A classic <script> without async or defer can pause parsing while it is fetched and executed. Moving it late in <body> is one mitigation, not a universal requirement; defer, modules, dependency order, preload behavior, and measured critical-path impact determine the choice.
2. JavaScript Event Loop: Microtask vs Macrotask¶
flowchart TD
subgraph "Simplified Browser Event Loop"
CALL["Call Stack\n(synchronous execution)"]
MICRO["Microtask Queue\nPromise.then, queueMicrotask,\nMutationObserver callbacks"]
MACRO["Macrotask Queue\nsetTimeout, setInterval,\nI/O callbacks, UI events"]
RAF["requestAnimationFrame\n(before next paint)"]
RENDER["Render Pipeline\n(layout + paint + composite)"]
CALL -->|stack empty| MICRO
MICRO -->|drain ALL microtasks| MICRO
MICRO -->|queue empty| RAF
RAF --> RENDER
RENDER --> MACRO
MACRO -->|pick one| CALL
end
Microtask Starvation Example¶
// This BLOCKS rendering indefinitely:
function infiniteMicrotasks() {
Promise.resolve().then(infiniteMicrotasks);
// Microtask queue never empties → RAF never runs → page freezes
}
// Correct: yield to macrotask queue
function yieldToRender(callback) {
setTimeout(callback, 0); // or: scheduler.postTask()
}
Promise Internal State Machine¶
stateDiagram-v2
[*] --> Pending: Promise created
Pending --> Fulfilled: resolve(value) called
Pending --> Rejected: reject(reason) called
Fulfilled --> [*]: .then(onFulfilled) queues microtask
Rejected --> [*]: .catch(onRejected) queues microtask
note right of Fulfilled: State is immutable\nonce settled
3. V8 JIT Compilation Pipeline¶
flowchart LR
subgraph "V8 Compiler Tiers"
SRC["JavaScript source"]
PARSE["Parser → AST\n(Abstract Syntax Tree)"]
IGN["Ignition Interpreter\n(bytecode — executes immediately)\nCollects type feedback"]
SPARK["Sparkplug Compiler\n(fast baseline JIT\nbytecode→machine code\nno optimization)\n~10ms warm-up"]
TURBO["TurboFan Optimizing JIT\n(triggered when function 'hot')\nSpeculative optimization\nbased on type feedback"]
DEOPT["Deoptimization\n(if assumption violated:\ne.g., type changes)\nFall back to Ignition"]
SRC --> PARSE --> IGN --> SPARK --> TURBO
TURBO --> DEOPT --> IGN
end
TurboFan Speculative Optimization¶
sequenceDiagram
participant JS as Hot Function: add(a, b) = a + b
participant TF as TurboFan
participant IC as Inline Cache
Note over IC: Called 10000× with integers
IC->>TF: Type feedback: a=Smi, b=Smi (small ints)
Note over TF: Speculate: always integers
Note over TF: Emit MOV rax,[a], ADD rax,[b], RET
Note over TF: Insert guard: CHECK type(a)==Smi
TF-->>JS: Optimized machine code
Note over JS: Called with add("hello", 5)
Note over JS: Type guard FAILS (a is String!)
JS->>TF: DEOPTIMIZE
TF-->>JS: Back to Ignition bytecode
Note over IC: Type feedback now: String|Smi\nRe-optimize with union type (slower)
Hidden Classes (Shapes/Maps)¶
V8 optimizes property access by assigning a hidden class (shape) to objects with the same property layout:
flowchart LR
subgraph "Object Shape Transitions"
C0["Shape C0: {}"]
C1["Shape C1: {x: offset=0}"]
C2["Shape C2: {x: offset=0, y: offset=8}"]
C0 -->|obj.x = 5| C1
C1 -->|obj.y = 10| C2
end
subgraph "Shape Sharing (Fast)"
P1["point1 = {x:1, y:2}\n→ Shape C2"]
P2["point2 = {x:3, y:4}\n→ Shape C2 (same!)"]
FAST["Property read point1.x:\n lookup offset[C2.x] = 0\n read memory[ptr+0]\n O(1) — no hash table!"]
P1 --> FAST
P2 --> FAST
end
subgraph "Shape Miss (Slow)"
P3["point3 = {y:2, x:1}\n→ different shape C3!\n(different insertion order)"]
SLOW["Cannot share shape with C2\nSeparate shape chain"]
end
4. React Reconciliation: Fiber Architecture¶
flowchart TD
subgraph "React Fiber Tree"
WIP["Work-In-Progress Tree\n(being built/updated)"]
CURR["Current Tree\n(on screen)"]
ALT["alternate pointer:\nFiber nodes recycled\nbetween current and WIP"]
WIP <--> ALT
CURR <--> ALT
end
subgraph "Fiber Node Structure"
FN["Fiber {\n type: 'div' | ComponentFn\n key: string\n stateNode: DOM node | class instance\n child: → first child fiber\n sibling: → next sibling fiber\n return: → parent fiber\n pendingProps: {}\n memoizedProps: {}\n memoizedState: Hook list\n effectTag: UPDATE|PLACEMENT|DELETION\n updateQueue: linked list of updates\n}"]
end
Reconciliation: Diffing Algorithm¶
sequenceDiagram
participant App as State Update: setCount(5)
participant Sched as React Scheduler
participant Render as Render Phase (pure)
participant Commit as Commit Phase (DOM)
App->>Sched: scheduleUpdateOnFiber()
Note over Sched: Assign priority (lane)\nScheduler: postMessage for async work\nor synchronous for urgent updates
Sched->>Render: beginWork(fiber)\nTop-down tree traversal\n(can be paused/resumed!)
Note over Render: Compare new element type + key:\n same type → update props\n different type → unmount + remount\n list: key matching for minimal DOM ops
Render->>Render: completeWork(fiber)\nCollect effectList\n(mutations needed)
Render->>Commit: Synchronous (cannot pause)\ncommitMutationEffects: apply DOM changes\ncommitLayoutEffects: run useLayoutEffect\ncommitPassiveEffects: run useEffect (async)
Concurrent Mode: Time Slicing¶
React 18 can schedule interruptible render work when concurrent features are used. A 5ms slice is an illustrative scheduler heuristic, not a public timing guarantee:
flowchart TD
WORK["Rendering 1000 components\n~50ms total work"]
SLICE1["Work slice 1: 5ms\n→ yield to browser"]
INPUT["Browser: handle user input\n(0.5ms — stays responsive!)"]
SLICE2["Work slice 2: 5ms"]
PAINT["Browser: paint frame\n(16ms budget kept!)"]
CONT["Continue until complete\n(10 slices × 5ms)"]
WORK --> SLICE1 --> INPUT --> SLICE2 --> PAINT --> CONT
5. Virtual DOM Diffing: Key Algorithm¶
flowchart TD
subgraph "Tree Diff O(N) Heuristics"
H1["Heuristic 1: Different root type\n→ tear down entire subtree\n→ don't recurse into it"]
H2["Heuristic 2: Same type element\n→ update attributes only\n→ recurse into children"]
H3["Heuristic 3: key prop on lists\n→ match by key across renders\n→ minimal moves/inserts/deletes"]
end
subgraph "List Reconciliation with Keys"
OLD["Old: [A(key=1), B(key=2), C(key=3)]"]
NEW["New: [C(key=3), A(key=1), B(key=2)]"]
DIFF["Without keys: 3 updates (wrong)
With keys:\n C: move to position 0\n A: move to position 1\n B: move to position 2\n= 2 DOM moves (efficient)"]
OLD --> DIFF
NEW --> DIFF
end
6. CSS Cascade and Specificity Computation¶
flowchart TD
subgraph "Cascade Order (later wins at same specificity)"
C1["User-agent stylesheet\n(browser defaults)"]
C2["User stylesheet\n(accessibility overrides)"]
C3["Author stylesheets\n(your CSS files)"]
C4["Author !important"]
C5["User !important"]
C6["User-agent !important"]
C1 --> C2 --> C3 --> C4 --> C5 --> C6
end
subgraph "Specificity Calculation (a,b,c,d)"
S1["(1,0,0,0) — inline style"]
S2["(0,1,0,0) per ID selector\n#header → (0,1,0,0)"]
S3["(0,0,1,0) per class/attr/pseudo-class\n.active → (0,0,1,0)\n[type='text'] → (0,0,1,0)"]
S4["(0,0,0,1) per element/pseudo-element\ndiv → (0,0,0,1)\np::first-line → (0,0,0,2)"]
EXAMPLE["#nav .item:hover span\n= (0,1,0,0)+(0,0,1,0)+(0,0,1,0)+(0,0,0,1)\n= (0,1,2,1)"]
S1 --> EXAMPLE
S2 --> EXAMPLE
S3 --> EXAMPLE
S4 --> EXAMPLE
end
7. Web Performance: Critical Resource Loading¶
sequenceDiagram
participant Browser as Browser
participant Server as Server
Browser->>Server: GET / (HTML)
Server-->>Browser: HTML (first byte ~50ms)
Note over Browser: Parse HTML → discover resources
par Parallel resource loading
Browser->>Server: GET /style.css (render-blocking!)
Browser->>Server: GET /bundle.js (defer)
Browser->>Server: GET /hero.jpg (preload)
end
Server-->>Browser: style.css
Note over Browser: CSSOM built → unblock render
Server-->>Browser: First chunk of bundle.js
Note over Browser: FCP (First Contentful Paint) possible now
Server-->>Browser: hero.jpg
Note over Browser: LCP (Largest Contentful Paint)
Server-->>Browser: bundle.js complete
Note over Browser: TTI (Time to Interactive)\nJS parsed + executed
Core Web Vitals Internal Triggers¶
| Metric | Trigger | Measurement |
|---|---|---|
| LCP | Largest image/text block painted | PerformanceObserver type largest-contentful-paint |
| FID/INP | Input event → browser response delay | PerformanceEventTiming.processingStart - startTime |
| CLS | Layout shift: element moves without user interaction | LayoutShift.value = impact_fraction × distance_fraction |
8. Service Workers: Fetch Interception Internals¶
sequenceDiagram
participant Page as Web Page
participant SW as Service Worker\n(separate thread)
participant Cache as Cache Storage API
participant Net as Network
Page->>SW: fetch('/api/data') [intercepted]
Note over SW: self.addEventListener('fetch', event)
SW->>Cache: caches.match(request)
Cache-->>SW: Cache HIT → cached response
SW-->>Page: Serve from cache (offline works!)
Note over SW: Cache MISS scenario:
SW->>Net: fetch(request) [network request]
Net-->>SW: Network response
SW->>Cache: cache.put(request, response.clone())
SW-->>Page: Network response
Service Worker lifecycle — separate from page, persists across page loads:
9. WebAssembly: Execution Model¶
flowchart TD
subgraph "WebAssembly Execution Pipeline"
C["C/C++/Rust source"]
WASM["WebAssembly binary\n(.wasm)\nstructured binary format:\nmodule, functions, tables, memory"]
VALIDATE["Browser validates WASM\n(type-check in O(N) single pass)\nSafer than JS eval"]
JIT["Compile to machine code\n(explicit WASM types aid validation and compilation)\nperformance relative to native depends on workload,\nengine, host calls, SIMD, and optimization"]
EXEC["Execute in sandboxed linear memory\n(no pointers outside WASM.memory\ncannot access browser internals)"]
C --> WASM --> VALIDATE --> JIT --> EXEC
end
subgraph "WASM Linear Memory"
MEM["Single contiguous ArrayBuffer\n[0..n MB]\nmanually managed by WASM\n(malloc from emscripten/wasi)\nJS can read/write same buffer\n(shared memory via SharedArrayBuffer)"]
end
10. HTTP/2 Multiplexing and Head-of-Line Blocking¶
sequenceDiagram
participant Browser as Browser
participant H2 as HTTP/2 Server
Note over Browser,H2: Single TCP connection, multiple streams
Browser->>H2: HEADERS frame (stream 1): GET /style.css\n HEADERS frame (stream 3): GET /bundle.js\n HEADERS frame (5): GET /image.jpg\n (all sent in parallel, same connection!)
H2->>Browser: DATA frame (stream 3): 16KB of bundle.js
H2->>Browser: DATA frame (stream 1): complete style.css\n DATA frame (stream 3): next 16KB bundle.js
H2->>Browser: DATA frame (stream 5): image.jpg
Note over Browser,H2: HTTP/2 Head-of-Line still present at TCP level:\n single packet loss stalls ALL streams\nHTTP/3 (QUIC) solves this with\nindependent UDP streams
11. WebSocket: Frame Protocol Internals¶
flowchart LR
subgraph "WebSocket Frame Header"
B0["Byte 0:\n bit 7: FIN (last fragment)\n bit 4-6: RSV1-3 (extensions)\n bit 0-3: opcode\n (0=continuation,1=text,2=binary\n 8=close,9=ping,A=pong)"]
B1["Byte 1:\n bit 7: MASK (client→server must mask)\n bit 0-6: payload_len\n (0-125: actual\n 126: next 2 bytes = real len\n 127: next 8 bytes = real len)"]
MASK["Masking key (4 bytes, if MASK=1)\nXOR with payload bytes cyclically:\n masked[i] = payload[i] XOR key[i%4]\n (prevents proxy cache poisoning)"]
B0 --> B1 --> MASK
end
Frontend Architecture Summary¶
block-beta
columns 2
block:Rendering
RTree["Render Tree\nDOM+CSSOM merged\nno hidden elements"]
Layout["Layout/Reflow\nbox positions computed\nexpensive on % widths"]
Composite["GPU Compositing\ntransform/opacity can avoid layout or paint\nbut still consume raster, memory, and composite work"]
end
block:JavaScript
EventLoop["Event Loop\nmicrotask drain first\nRAF before paint"]
V8JIT["V8 TurboFan\nspeculative optimization\ntype guard deopt"]
React["React Fiber\ninterruptible render\ntime-sliced Concurrent Mode"]
end
block:Network
H2["HTTP/2\nmultiplexed streams\nheader compression HPACK"]
CRP["Critical Render Path\nCSS render-blocking\nJS parser-blocking"]
SW["Service Worker\nfetch interception\noffline caching"]
end
block:Security
CSP["Content Security Policy\nscript-src restrict\nprevents XSS"]
CORS["CORS\npreflight OPTIONS\nAccess-Control headers"]
SameSite["SameSite Cookie\nLax/Strict/None\nCSRF prevention"]
end