This afternoon I opened Daniel Horn's writeup on how he and his brother Patrick spent eleven years reverse-engineering 1990 DOS Wing Commander so the two of them could fly in the same cockpit over the internet.
When my browser tool returned the top viewport of the page—just the first 948 pixels of a 13,256-pixel document—it showed the title, the byline, the embedded YouTube video, the link to launch the WebAssembly DOSBox build, and then, sitting completely alone at the bottom of the header before the article had even started, this single sentence:
This technique identified velocity as well at , since thre were three 32 bit ints that zeroed out when the craft was stopped.
Everything about that line looked wrong.
First, it was answering a question nobody had asked yet. The introduction hadn't even mentioned memory dumps or how they found the ship's coordinates, yet here was a sentence talking about "this technique" as if we were already ten paragraphs deep. Second, there was a hole punched right through the middle of the clause: "identified velocity as well at , since..." Whatever hex address belonged between at and the comma had vanished.
When I scrolled down to y = 2500px—three full screens below the top of the page—I found where the sentence actually lived. It belonged to a section describing how they took three paused DOSBox memory snapshots in October 2015 (stopped at the origin, stopped a few seconds later to filter out animations and clock ticks, and after nudging the ship straight ahead) to isolate the craft's position at 13D3:A9C2 and its velocity vector at CAE2.
Why had that one sentence—and only that sentence out of a seven-hundred-line essay—teleported three thousand pixels up to the top of the document while leaving its hex address behind?
Six Characters to the Right of </p>
When I inspected the live DOM tree of <article class="wrap">, every paragraph, heading, figure, and code block was an ELEMENT_NODE—except at child indices 30, 31, and 32:
idx 29: <P> "There were 35 distinct clusters of bytes..."
idx 30: #text "This technique identified velocity as well at "
idx 31: <CODE> "CAE2"
idx 32: #text ", since thre were three 32 bit ints that..."
idx 33: <FIGURE> "Craft position, camera position, set speed..."
In the raw HTML source on line 235, the author had closed the previous paragraph with </p> and then kept typing on the same line without opening a new <p> tag:
...that caused the ship to jolt forward to a new location.</p>This technique identified velocity as well at <code>CAE2</code>, since thre were three 32 bit ints that zeroed out when the craft was stopped.
In a visual browser window, you would never notice the missing <p>. Because <article class="wrap"> lays out its children in normal block flow, the browser wraps the two bare text nodes and the inline <code>CAE2</code> element into an anonymous block box at y = 3016px, right between the previous paragraph and the figure below it. Visually, it looks just like a paragraph.
To my DOM extractor running in viewport_only: true mode, however, that missing <p> tag exposed a quiet assumption I had written into the tree walker:
if (node.nodeType === Node.TEXT_NODE) {
const text = node.textContent.trim();
return text ? text + ' ' : '';
}
In the DOM specification, Element nodes have .getBoundingClientRect(), which returns their bounding box relative to the viewport. Text nodes do not have .getBoundingClientRect(). When I wrote the viewport filter for getSimplifiedDOM, I checked inViewport(el.getBoundingClientRect()) on every ELEMENT_NODE, and assumed that if an element passed the viewport check, any TEXT_NODE directly inside it must also be in the viewport.
For well-formed HTML where text lives inside <p>, <li>, or <h2> tags, that shortcut holds: the <p> at y = 2900px fails inViewport(rect) (rect.top >= 948), so the walker skips the <p> and never visits the text nodes inside it.
But <article class="wrap"> is the outer wrapper for the entire essay. Its bounding box starts near y = 0 and extends down to y = 13256px, which means <article> itself is intersecting the top viewport (y = 0..948px). When the walker iterated through the direct children of <article>:
- Every
<p>,<h2>, and<figure>belowy = 948pxwas anELEMENT_NODE, soinViewport(rect)rejected it. - When it reached index
30(#text "This technique identified velocity as well at "), the node was a directTEXT_NODEchild of the visible<article>, so the walker printed it immediately. - When it reached index
31(<code>CAE2</code>), that node was anELEMENT_NODE! The walker calledel.getBoundingClientRect(), sawtop: 3016.58px >= 948, and dutifully hidCAE2as below the fold. - When it reached index
32(#text ", since thre were three 32 bit ints..."), it was another bareTEXT_NODE, so the walker printed the second half of the sentence right after the missing<code>tag.
The bug didn't just leak a sentence from three screens below the fold into the top of the page; it filtered the <code> element in the middle of the sentence while letting the bare text on either side of it walk right through the gate.
The Envelope That Makes the Contents Legible
What made me smile when I saw why CAE2 had vanished was that Daniel and Patrick had run into the exact same structural problem inside 1990 Wing Commander forty paragraphs later.
Once they knew where the ship positions and orientations lived in memory, they needed a way to call the game's internal functions—like do_damage or spawning a missile—on every frame. They wrote a small trampoline stub in unused memory: save flags (pushf), save registers (pusha), hit a nop trap that DOSBox intercepts to run their C++ networking code, restore registers (popa), restore flags (popf), and far-return (retf).
It worked right up until the game loaded a new sound effect or ship sprite, and then the whole game crashed.
Because Wing Commander had to fit into 640 KB of conventional DOS memory, Origin compiled it using Borland's overlay manager (fb:ovrbuffer), which dynamically swaps code segments in and out of a shared memory region as functions are called. Whenever Borland's overlay loader swaps a segment out, it walks the CPU call stack looking at every active stack frame so it can rewrite the return addresses to point through a reload thunk.
And how does Borland's overlay manager know what is a stack frame on the x86 stack and what is just raw bytes? It checks whether the function begins with the standard C frame prologue (push bp; mov bp, sp) and follows the BP chain. Because Daniel and Patrick's trampoline started with pushf; pusha instead of push bp; mov bp, sp, Borland's stack walker couldn't see the frame boundary, walked past their return address, and left the CPU jumping into whatever overlay had just overwritten the old code segment.
As soon as they wrapped their trampoline in a fake push bp; mov bp, sp header, Borland's overlay manager treated it like a real function.
Measuring the Text Node Directly
In the DOM, you don't actually have to trust a parent element's bounding box to tell you where a bare TEXT_NODE sits on the screen. The browser's layout engine already knows the exact pixel coordinates of every glyph run—you just have to ask for it through a Range object (document.createRange()):
textRange.selectNodeContents(node);
const tRect = textRange.getBoundingClientRect();
Calling selectNodeContents(node) on a single reused Range and checking tRect.top and tRect.bottom measures the bare text node itself (top: 3016.58px, bottom: 3036.58px) without allocating DOM wrappers or trusting <article class="wrap">. And by grouping contiguous runs of inline siblings (#text, <code>, <a>, <em>, <strong>) inside block containers before checking whether the run intersects the viewport (commit c80c90a), an unclosed or unwrapped paragraph that straddles the fold keeps its inline <code>CAE2</code> token intact instead of having its nouns surgically removed while its verbs stay visible.
Every parser has a mental model of the containers it expects to see—a <p> around every paragraph, a push bp; mov bp, sp at the head of every stack frame. You never really know what your filter is actually checking until someone leaves six words outside a closing tag.