The DOM (Document Object Model) is the browser’s internal representation of a web page — a tree-shaped data structure that maps every element, attribute, and text node into a hierarchy that can be queried, traversed, and manipulated by JavaScript.
It is, in other words, a display list. The browser’s display list. And if that phrase stirs something in the memory of anyone who programmed an Amiga in 1990, it should, because display lists are as old as computer graphics itself. The only thing that changes between decades is what we call them, and how guilty we feel about touching them.
A Brief History of Display Lists
In 1985, the Amiga shipped with a coprocessor called the Copper. The Copper maintained a copper list — a sequence of instructions that wrote to display hardware registers at precise scanline positions, hundreds of times per frame, while the CPU did nothing. It was, in every meaningful sense, a programmable display list. You wrote to it directly. It wrote to the display chip directly. The display chip — affectionately known as Denise — rendered what it was told. Nobody built a shadow copy of the copper list to diff against the real one.
“The copper list. I spent three days — THREE DAYS — figuring out how to move sprites without using CPU cycles. The copper would do it. Per-scanline. While the CPU did nothing.”
— riclib, The Copper List Rides Again
In 1998, the W3C standardised the DOM. A tree of nodes. A programmable display list for the browser. You wrote to it directly. The browser rendered what it was told. document.getElementById('thing').innerHTML = 'new thing'. Four function calls. Done.
In 2013, Facebook invented React, and with it the Virtual DOM — a lightweight JavaScript copy of the actual DOM, maintained in memory, diffed against itself on every state change, reconciled into a minimal set of mutations, and then — with considerable ceremony — applied to the real DOM. This is the architectural equivalent of building a detailed scale model of your house so that you can rehearse walking through the front door before committing to actually doing it.
The copper list was manipulated directly. The DOM was manipulated directly. Then the industry decided that manipulating the DOM directly was dangerous, and built an intermediary — a shadow copy of the display list used to avoid touching the display list — which consumed more memory than the display list it was protecting.
“React’s virtual DOM was DESIGNED for this! Diff the state! Reconcile the tree! Only update what changed!”
— The Caffeinated Squirrel, proposing React to solve a problem solved by not re-rendering, The Copper List Rides Again
The Copper List Connection
The lifelog records a direct lineage, articulated at 10:15 AM on January 22, 2026, by a man who had been staring at an SSE stream and thinking about 1990:
THE BOY WHO WROTE TO DENISE'S BRAIN
WHILE SHE WAS RENDERING
BECAME THE MAN WHO WRITES
TO THE BROWSER'S DOM
WHILE IT'S STREAMING
SAME INSTINCT
DIFFERENT CHIP
THE COPPER LIST NEVER LEFT
IT JUST LEARNED HTML
The copper list wrote to hardware registers. The DOM exposes element registers. The Virtual DOM maintains a copy of those registers in case touching the real ones might be, somehow, impolite.
A boy in Lisbon wrote directly to Denise’s internal register — the display chip’s working register where it loaded sprite position data during rendering. He didn’t update sprite data structures in memory. He hijacked the display chip’s internal state, telling Denise “actually, the sprite is HERE now” as it was actively rendering. Hundreds of times per frame. In 488 bytes.
Thirty-five years later, the industry collectively decided that writing to the browser’s display list required an intermediary layer, a reconciliation algorithm, a fiber architecture, and 42KB of minified JavaScript.
“I proposed adding a 42KB framework to avoid re-rendering, when the solution is to just… not re-render.”
— The Caffeinated Squirrel, achieving clarity, The Copper List Rides Again
The Framework That Wasn’t
The most thorough examination of the DOM’s role in modern architecture was conducted across twenty-three episodes of the V3 Saga, in which reactive signal frameworks were deployed to manage DOM updates for server-authoritative forms. Components rewrote signal bindings at render time. SSE-patched content arrived after render. Signals became orphans at root level. The DOM was fine throughout. The frameworks were the problem.
The resolution came when someone noticed that the DOM already is the scene:
“Why am I tracking slots? The DOM already IS the scene.”
— riclib, waking up at an undisclosed hour, Raiders of the Lost Architecture A V3 Origin Story
Components self-register. data-init on mount. onCleanup on unmount. The browser handles the DOM lifecycle. It has been handling the DOM lifecycle since 1998. It is, by most accounts, quite good at it.
The architecture that ultimately shipped used HTMX — six swap targets, each writing directly to the DOM. The server sends HTML. The client says “okay.” No virtual DOM. No reconciliation. No fiber architecture. No suspense boundaries. Just a display list, being written to, the way display lists have been written to since 1985.
"Morphing over manual DOM" → hx-swap="innerHTML" (no diffing)
“The manifesto said: only render what changed. The code only renders what changed. The manifesto said: the copper list. The code has a comment about the copper list.”
— The narrator, The Framework That Wasn’t, or The Night the Squirrel’s Manifesto Shipped as Six Lines of HTMX
The Display List Through the Ages
| Year | Display List | Written By | Shadow Copy? |
|---|---|---|---|
| 1985 | Copper list | Copper coprocessor | No |
| 1990 | Copper list (hijacked) | A teenager with 488 bytes | Absolutely not |
| 1998 | The DOM | Browsers (Netscape, IE) | No |
| 2006 | The DOM (with jQuery) | $('#thing').html('new thing') |
No |
| 2013 | The Virtual DOM | React | Yes — the entire point |
| 2026 | The DOM (with HTMX) | hx-swap="innerHTML" |
No |
The pattern is clear. For twenty-eight years, display lists were written to directly and everything was fine. Then for thirteen years, a shadow copy was maintained at considerable expense. Then the shadow copy was abandoned and everything was fine again.
The Resource Accounting
A corporate laptop in 2026 runs three versions of React, each maintaining its own Virtual DOM. The HR portal has one. The expense system has one. The “quick internal dashboard from 2019” has one. Each shadow DOM consumes memory protecting the real DOM from updates that the real DOM would have handled without complaint.
The machines have 8GB of RAM. React is using 6GB of it. The DOM itself — the actual display list, the living tree of nodes that the browser maintains and renders — occupies a few megabytes and asks for nothing.
“The committed content is ROM. Rendered once. Append-only.”
— riclib, re-inventing the copper list for SSE streams, The Copper List Rides Again
The StreamState architecture that replaced all proposed frameworks operated on a principle that the Amiga copper list had established in 1985: committed content never changes, the preview is tiny and rewritten constantly, and SSE does the heavy lifting of DOM updates while the server just increments offsets. Like a copper list. Like a display list. Like the way things have always worked when people trust the display hardware to do its job.
On Being a Tree
The DOM is a tree. This is not a metaphor. It is a tree data structure, with a root, with branches, with leaves, with parent-child relationships, with sibling traversal, with all the properties of trees that have been understood since the 1960s.
React’s innovation was to grow a second tree next to the first one, compare the two on every state change, and then prune the first tree to match the second. This is the horticultural equivalent of planting a duplicate garden so that you can study which flowers need watering before approaching the real ones with a watering can.
HTMX’s innovation was to notice that the tree was already there.
The copper list’s approach was to not care about the tree at all and write directly to the display chip’s brain while it was actively rendering.
All three approaches work. Only one of them requires two trees.
