• Thread Author
Dave Plummer’s confession that the worst bug he ever shipped was tied to the beloved Windows pack‑in game Pinball is more than a nostalgic anecdote — it’s a compact lesson in resource management, legacy code risk, and the kind of tiny design decisions that can balloon into systemic problems as hardware outpaces assumptions. What began as a straightforward port of a 1990s table wound up consuming an entire CPU core on modern machines because the game’s render loop drew frames as fast as it could; the fix — adding a modest frame‑rate limiter — reduced CPU use to near‑negligible levels and restored sanity to developer workflows. This story, told in Plummer’s own interviews and amplified by veteran Windows engineer Raymond Chen, is a case study in how software behaves in the long tail: small mistakes seeded in one era can become critical failures when the environment changes. (lexfridman.com, archive.ventilaar.net)

Background: the Windows Pinball legacy​

3D Pinball for Windows — more commonly remembered as Space Cadet Pinball — was originally derived from Full Tilt! Pinball and licensed into Microsoft’s Microsoft Plus! package for Windows 95. The Space Cadet table later appeared bundled with Windows NT, Windows 98, Windows 2000, Windows Me and Windows XP (the latter being the last mainstream client release to include the classic Pinball executable). Over the decades the tiny executable has become a nostalgic touchstone for millions of Windows users, a compact artifact of the era when operating systems shipped with small, self‑contained casual games. (en.wikipedia.org, howtogeek.com)
Dave Plummer — the engineer best known in public for authoring the original Windows Task Manager and for porting the Space Cadet table to the multiple architectures Windows NT supported — later documented his work in interviews and videos. His practical approach was straightforward: keep the original gameplay logic intact and rewrite or replace the platform‑specific layers (sound, drawing) so the old engine could run on RISC platforms such as MIPS, Alpha, and PowerPC. That conservative porting strategy preserved the look and play of the original while making the codebase portable. (theregister.com, en.wikipedia.org)

How a tiny design choice became a CPU‑hunger bug​

The render loop that never slept​

When Plummer ported the table to Windows NT he wrote a new engine layer around the original game logic for drawing and sound. That wrapper had a simple, fatal design choice: it rendered as many frames as possible, with no explicit frame limiter or sleep in the main loop. On the hardware of the day — Plummer has referenced MIPS workstations such as the R4x00 family clocked in the low hundreds of megahertz — this was benign and often performant enough, producing frame rates in the tens of frames per second. But the code assumed the world wouldn’t get dramatically faster. (lexfridman.com, en.wikipedia.org)
As Windows moved into the XP era and desktop hardware accelerated, that harmless loop turned hostile. Plummer later recounted that on faster processors the game could render thousands of frames per second — his own recollection mentions figures in the multiple thousands — and that translated into the process busy‑spinning a core to draw useless extra frames. Raymond Chen tracked down the problem in the running builds and discovered the absence of any frame‑rate cap: debug counters showed three asterisks (indicating a frame count above three digits), and the process simply ate CPU while offering no benefit to playability. (lexfridman.com, archive.ventilaar.net)

Why resource‑wasteful loops matter at scale​

The symptom — excess CPU usage by a single user‑level process — seems trivial, but at OS scale it’s dangerous for two reasons:
  • Polling loops that run at full speed waste CPU cycles that could otherwise be shared, scheduled, or power‑managed. On servers or multitasking desktops, one such loop can drag down responsiveness system‑wide.
  • Modern power and thermal constraints mean continuously pegged cores increase heat and battery drain aggressively, a critical problem for laptops and mobile devices that weren’t considered in the original design.
Raymond Chen framed the fix in classic engineering terms: stop burning resources when there’s no reason to. The chosen remedy was simple — insert a frame‑rate limiter — and the empirical result was dramatic: CPU usage collapsed to roughly one percent after capping the render rate to a sane number (Chen has described setting the limiter to 100 frames per second). That small change converted a runaway process into a well‑behaved application. (archive.ventilaar.net, theregister.com)

Technical analysis: what went wrong, why, and how it was fixed​

The root cause: tight loops without pacing​

At heart, the Pinball bug is a classic busy‑wait/polling anti‑pattern. The engine’s main loop performed drawing and advancement of the game state repeatedly with no explicit pacing mechanism (sleep, vsync, or event‑driven timing). Busy loops like this can be acceptable when they’re paired with expensive rendering or blocking I/O that implicitly paces work. In Pinball’s case, the drawing was cheap enough that modern CPUS could render tens or thousands of frames per second, causing the logic’s interpolation to run far faster than intended — which, paradoxically, changed the perceived physics and behavior of the game. (lexfridman.com, devblogs.microsoft.com)

The fix: frame limiter and sensible timing​

A robust remedy for render‑driven loops is to decouple physics updates from rendering frequency, and to cap rendering to either the display refresh rate or a reasonable upper bound:
  1. Update the physics at a fixed timestep (e.g., 30–120 updates per second).
  2. Render at a capped rate, typically synchronized to the display (V‑sync) or a configurable FPS cap.
  3. If the rendering is much cheaper than physics updates, use sleep/yield syscalls or OS timers to avoid busy waiting.
  4. Provide a fallback adaptive mode for low‑FPS environments.
In Pinball’s case a pragmatic, backwards‑compatible change — add a frame‑rate limiter (Chen used 100 fps) — preserved the original gameplay while drastically lowering CPU usage and eliminating the pathological behaviour on fast hardware. The limiter also avoided unintended side‑effects to game logic: by keeping the logic loop untouched (rather than rewriting the inner physics engine), the behavioral fidelity to the original game was retained. (archive.ventilaar.net, lexfridman.com)

Cross‑checks and corroboration​

  • Dave Plummer himself has explained the bug and its symptoms in public interviews and long‑form talks where he describes the render‑as‑fast‑as‑possible approach and the thousands‑of‑frames‑per‑second behavior on modern CPUs. Those firsthand comments are explicit and form the primary account. (lexfridman.com)
  • Raymond Chen — a long‑time Windows engineer and author of the Old New Thing blog — recounted locating the bug, finding debug counters that overflowed display expectations, and applying a frame limiter that reduced CPU to roughly one percent. Chen later called the fix one of his proudest Windows contributions. Chen’s account appears in video interviews and public blog posts and matches Plummer’s technical description. (archive.ventilaar.net, theregister.com)
  • The historical facts about Space Cadet Pinball’s inclusion in Windows and its removal from mainstream client releases after Windows XP are consistent across independent documentation: game histories, how‑to guides that show how to port the Windows XP executable forward, and the Wikipedia/archival record. These sources confirm that Windows XP was the last client OS to ship the standard Pinball executable by default. (en.wikipedia.org, howtogeek.com)
  • The hardware context that made the problem visible — workstation CPUs in the R4000/R4400 families operating in the low‑hundreds of MHz and later general‑purpose x86 chips reaching many times that throughput — is consistent with contemporaneous processor documentation. The family of MIPS R4x00 processors, including variants in the 100–250 MHz range, reflects the kind of RISC workstation hardware Plummer referenced in his anecdotes. Where precise MHz figures vary across model and vendor, the narrative remains the same: hardware got much faster while the code had an implicit pacing assumption. (en.wikipedia.org)
Note on anecdotal metrics: Plummer’s claim of “5,000 frames per second” and Chen’s “three asterisks” debug counter are both recollections from human memory and interview transcripts. They are useful to illustrate scale and symptom, but precise frame‑rate numbers can vary by machine and build; they should be treated as illustrative measurements rather than exact scientific measurements taken under controlled conditions. The engineering point — that the loop ran orders of magnitude faster than intended — remains robust across sources. (lexfridman.com, archive.ventilaar.net)

Why this matters now: legacy code, hardware drift, and modern OS stewardship​

Legacy code ages poorly — not because it’s wrong, but because assumptions change​

The Pinball episode is one of the cleanest modern reminders that code is written against assumptions about environment. When those assumptions (clock rates, floating‑point default modes, syscalls, threading and scheduling models) change, subtle behaviors can appear. Porting exercises that preserve logic but replace platform layers can be the safest path forward — but they still require defensive checks for timing, precision, and performance.
This story also highlights the ripple effects of compatibility choices: Windows historically supported multiple architectures (MIPS, Alpha, PowerPC), which forced porters to rewrite platform‑specific assembly to portable C. Those moves were necessary and laudable, but they left some glue that was brittle across architectures and runtimes. The subsequent transition to 64‑bit and newer CPUs exposed assumptions Plummer and colleagues had never needed to revisit. (en.wikipedia.org)

Small fixes can have outsized impact — but they must be discovered​

The fix that saved Pinball’s CPU usage — a simple frame limiter — is a reminder that sometimes the most effective patches are modest in code but large in impact. That Chen’s change allowed developers to run builds and play Pinball simultaneously again is an anecdote about developer productivity, but it’s also emblematic of modern quality‑of‑life engineering: reduce waste, make the environment usable, and keep the feedback loop tight. (archive.ventilaar.net)

The managerial angle: why throwing time at exotic rewrites isn’t always the right answer​

Chen’s account also underscores a practical management lesson: when code is old, undocumented, or external, digging for a perfect fix can be costly. The decision to apply a conservative, low‑risk mitigation rather than re‑engineer collision detection or rewrite the physics system reflects an engineering tradeoff often made in large, multi‑priority projects. It’s a valid strategy when schedules and broader platform priorities overshadow the value of the legacy feature. That’s why Pinball disappeared and why, later, a small fix was preferred once the symptom was isolated. (theregister.com, archive.ventilaar.net)

Broader implications for Windows users and developers​

  • For developers: always assume external change. Use explicit timing mechanisms, avoid busy waits, and decouple simulation/logic from rendering. Unit tests and stress tests that run across a variety of CPU speeds can reveal assumptions early.
  • For maintainers of legacy code: preserve observable behaviors with regression tests, but also document and defend implicit assumptions (e.g., expected frame rates, floating‑point rounding modes). Where possible, instrument builds with telemetry to detect runaway loops automatically.
  • For OS designers: provide clear guidance and APIs for timing and rendering (e.g., V‑sync integration, high‑resolution timers) and make it simple for apps to adopt them. Tools that surface CPU usage patterns and cross‑process impact can help isolate pathological behavior more quickly.
  • For power and battery engineers: consider the hidden cost of background processes written before energy budgets mattered. Busy loops are energy leeches and should be constrained on battery‑powered platforms by default.

Strengths and risks: technical and human dimensions​

Notable strengths revealed by the story​

  • Conservative porting: Plummer’s decision to leave the original gameplay logic untouched and implement portability at the wrapper level preserved fidelity to the source game — a wise trade for compatibility. (en.wikipedia.org)
  • Pragmatic triage: Chen’s fix shows how a minimal, targeted mitigation can restore usability and stability without risky invasive rewrites. (archive.ventilaar.net)
  • Institutional memory: Because long‑time engineers talk and publish their recollections, the ecosystem can learn from past mistakes rather than repeating them blindly. Plummer and Chen’s public recounting is a public good. (lexfridman.com, archive.ventilaar.net)

Potential risks and caveats​

  • Undocumented assumptions: The underlying physics engine — originally external, partially in assembly — was fragile and poorly documented. That makes long‑term maintenance expensive and increases the risk that other variants of the bug could emerge under edge‑case hardware or compiler behaviors. (en.wikipedia.org)
  • Anecdote vs. measurement: Memory‑based recollections of frame rates and exact CPU percentages are useful for narrative but should be treated as anecdotal. Reproducing the exact numbers requires controlled measurement. The key technical claim — that an uncapped loop can peg a core — is fully reproducible, but any specific FPS number will vary by environment. (lexfridman.com)
  • Legacy legal/licensing constraints: Pinball’s origin as a licensed table (Full Tilt! / Cinematronics / Maxis) complicated any attempt to modernize, repackage, or re‑release the game officially. That limited Microsoft’s options for cleanly replacing or reworking the title in later OS releases. These non‑technical limitations are common and can frustrate mitigation plans. (en.wikipedia.org)

Practical takeaways and a short checklist​

  1. Assume hardware will get faster; do not rely on CPU slowness to hide bugs.
  2. Avoid busy‑wait rendering loops; use OS timers, sleeps, or vsync to pace work.
  3. Decouple physics updates from rendering and place physics on a fixed timestep.
  4. Add regression tests and telemetry to catch pathological resource usage as soon as builds run in CI.
  5. When dealing with legacy third‑party code, prefer low‑risk mitigations (caps, wrappers) over invasive rewrites unless the value proposition justifies the effort.

Conclusion​

The Pinball episode — where one of Microsoft’s own engineers called a tiny bug his “worst shipped bug” because it consumed an entire CPU core — is deceptively instructive. It compresses dozens of software‑engineering truths into a single, memorable vignette: code ships in contexts; contexts change; assumptions break; small mitigations can have outsized returns; and the human cost — embarrassment, bug reports, broken builds — is real. Dave Plummer’s honesty and Raymond Chen’s pragmatic debugging are both part of a larger narrative about responsible stewardship of software over time. For engineers and maintainers, the lesson is timeless: pace your loops, document your assumptions, and never trust that “it’s fine on my machine” will survive the next decade of progress. (lexfridman.com, archive.ventilaar.net, en.wikipedia.org)

Source: theregister.com Microsoft veteran's worst Windows bug was all about Pinball
 

Back
Top