Home / Software y Cloud / Between HTML and the pixel: the rendering that draws every page you see

Between HTML and the pixel: the rendering that draws every page you see

Renderizado de una página web: del árbol DOM a los píxeles en pantalla

You type a web address, press Enter and, in under a second, the page appears. Between those two moments the browser does not simply “show” a file: it runs one of the most complex operations in everyday computing, rendering — the process by which code becomes pixels on your screen. This article walks through that assembly line, step by step, with real names.

From HTML to the node tree

Everything starts when the browser receives the HTML document. Its engine —Blink in Chrome and Edge, Gecko in Firefox, WebKit in Safari— does not treat HTML as plain text: it interprets it as a structured document. To do so it runs a parser (a syntax analyzer) that breaks the stream of bytes into a sequence of tokens: opening tags, attributes, text and closing tags.

That token stream is assembled node by node into the DOM (Document Object Model), an in-memory tree where each HTML element —<html>, <body>, <div>— is a node with its children. The DOM is the living representation of the document: when JavaScript adds an element with appendChild(), what it modifies is this tree, not the original file.

The CSSOM: the other half of the puzzle

In parallel, the browser processes the style sheets with a parser dedicated to CSS. The result is the CSSOM (CSS Object Model), a second tree that stores each rule with its selectors and, crucially, the already-computed values with their cascade (the precedence order between rules) and their specificity (how much weight one selector has over another). This is where the engine decides, for example, whether a div inherits its parent’s color or overrides it with a more specific class.

The render tree and the layout phase

With the DOM and CSSOM ready, the browser builds the render tree: the union of both that keeps only the nodes that are actually visible. An element with display:none does not appear in it; one with visibility:hidden does, but is not painted. From here on, geometry begins.

The layout phase (also called reflow) calculates the exact position and size of every node on screen. The engine applies the box model (every element is a box with content, padding, border and margin) and resolves the arrangement according to the layout system: normal flow, flexbox, grid or absolute positioning. With hundreds of nested elements this calculation cascades: the parent’s size constrains its children’s.

Paint and compositing: when the pixel lights up

With the boxes measured comes the paint phase: the engine rasterizes each node, converting its vector properties —borders, gradients, fonts, shadows— into a grid of colored pixels. In Chrome this rasterization is done by Skia, the 2D graphics library also used by Android and Flutter.

But a modern page is not painted in a single pass. To scroll or animate without repainting everything, the browser splits content into compositing layers and hands them to the GPU (graphics processing unit). When you scroll or animate an element with transform, the engine only moves or blends those already-painted layers —a process called compositing— instead of recalculating every pixel from scratch. That is why animating opacity or transform is cheap while animating width is expensive: the latter forces layout and paint to repeat on every frame.

Why we notice the stutters

The browser aims for 60 fps (frames per second), that is, redrawing the screen every 16.7 milliseconds. If the layout, paint and compositing block exceeds that time, the frame is dropped and the user perceives jank (a stutter or micro-freeze). Mutating the DOM in a loop, forcing style reads in the middle of an animation, or using overly complex CSS selectors are classic recipes for causing it.

The key tool to measure it is the paint flashing in DevTools: it highlights the regions being repainted each frame in green and reveals, in real time, which animation is forcing the engine to overwork.

The critical rendering path

This whole flow —HTML → DOM, CSS → CSSOM, render tree, layout, paint, compositing— forms the Critical Rendering Path, the minimum sequence that must complete before the first useful pixel appears. Optimizing it is the whole point of web performance: that is why it is recommended to inline critical CSS, defer the JavaScript that blocks parsing with defer, and load non-essential CSS asynchronously. A blocking CSS file delays first paint; a script in the middle of the HTML halts the parser until it runs.

Next time a page takes “just a second” to appear, remember: in that second your processor has built two object trees, measured and laid out every box, rasterized millions of pixels and blended layers on the GPU. That it feels instantaneous is not magic — it is finely tuned engineering.