DEV Community

Wendell Charles NeSmith
Wendell Charles NeSmith

Posted on

I built an operating system in a single HTML file, and it boots into a game

I just finished something I did not plan to build: a complete desktop operating system that lives in one HTML file. No framework, no build step for users, no server, no images. You open it in a browser, it runs a verbose boot sequence, and then it drops you onto a desktop with a game already waiting in the taskbar.

Try it: https://republicka.life/terminal-velocity.html Android app (APK): https://republicka.life/files/Terminal-Velocity.apk

It is released under CC0. Copy it, fork it, put it on your own server, do whatever you like.

How it started

It started as a retro CRT strategy RPG called Terminal Velocity. You ingest video into permanent flat files, mirror them across a "Hydra network", and defend them from corporate probes. It was a single HTML file too. Then I wanted real files inside it, so the game learned to read the public-domain archive I have been building for years (books, music, film, audiobooks, a comic). Then the archive needed a file manager. Then the file manager needed a window manager. At some point I looked up and had an OS.

The game is still the first app. Boot the OS and it launches Terminal Velocity minimised in the taskbar, with the desktop in front of you.

What is in the box

One file, around 370 KB, and the whole UI is drawn with CSS, SVG and canvas. No image files.

A window manager: drag, resize, snap, minimise, maximise, taskbar, Start menu, and a Spotlight-style search (Ctrl+Space).
26 apps, each with a desktop icon.
Terminal: a real shell with pipes, redirects, globs, tab completion and history (ls -lahs, grep, find, tree, cat, head, sort, wc, and more).
Files, Notepad (with real export, including ZIP), Calculator with its own expression parser (no eval), Paint, Clock, Calendar, Metronome, Task Manager, Toolbox, Snake, Minesweeper.
Media apps for the archive: Music (albums as playlists), Books (an e-reader for 250 text files, with chapters, themes and bookmarks), Audiobooks (resume, speed control, sleep timer), Comics (page, spread and scroll modes), plus a general Media Player and Image Viewer.
17 themes ("X environments") and animated canvas wallpapers.
A boot sequence that does real checks (IndexedDB, storage quota, the archive manifest) and prints what it is doing. You can slow it down, speed it up or skip it.
The virtual filesystem

One tree, several backends:

/archive: read-only, built from a manifest.json (about 2,800 files, roughly 290 GB). Files are streamed from the web, not downloaded.
/home/user: stored in IndexedDB.
/system and /tmp: generated or in memory.
/mnt/: real folders on your computer, in Chromium browsers, through the File System Access API.

Every app talks to the same small API (V.read, V.write, V.ls and so on), so the terminal, the file manager and Notepad all see the same files.

You can point the OS at your own manifest in Settings, so it works as a front end for any archive with a compatible file list.

A few tricks that made single-file possible

Embedding a whole app inside another. The game is stored inside the OS as an inert block, with &lt;/script escaped so the HTML parser leaves it alone. At runtime I restore it, inject a small prelude, and run it in an iframe through srcdoc. The prelude overrides fetch for the manifest and forwards keyboard shortcuts to the parent. That is also why you can run the OS inside the OS inside the OS.</p> <p>A boring build. The source is a handful of JS files plus a CSS head. A short Python script concatenates them into the one deployable file. That is the whole toolchain.</p> <p>Streaming big media. Audio and video use the browser&#39;s own range requests, so a 1 GB audiobook starts playing straight away. The audio visualiser needs CORS, so it falls back to a synthetic animation when the host does not allow it, and the playback itself never depends on it.</p> <p>Things that bit me<br> Name collisions in a big single scope. In a single file with no modules, I added a function with a name the game already used. The symptom was a baffling error message in the game&#39;s UI. I now scan for duplicate declarations before every build.<br> Mobile keyboards. Autofocus on a text field is fine on desktop and miserable on a phone, because the keyboard pops up when you only wanted to tap a button. Buttons now do not steal focus on touch screens, and the keyboard appears only when you tap the actual text area.<br> Phone layouts. Side-by-side panels that look good at 1000 px collapse into unreadable strips at 400 px. I audited every app with a phone-sized headless browser and fixed the ones that broke.<br> Caching. After updating a single-file app, browsers and app wrappers hold onto the old copy. I added a build stamp to the Start menu and boot log so I can tell which version I am running.</p> <p>For testing I used Playwright against a local server, with scripted checks for boot, the game in its iframe, terminal pipelines, Notepad and ZIP export, themes, mobile layout and each media app.</p> <p>What it is not</p> <p>It is not a replacement for Windows, macOS or Android. It is a portable personal shell that sits on top of whatever device you have:</p> <p>Your files live in the browser&#39;s database. They do not sync between devices, and a browser can evict them. Notepad has &quot;Export all (ZIP)&quot; for backups.<br> Reading and writing real folders only works in Chromium browsers.<br> There are no native programs or background services.</p> <p>What it does give you is a complete workspace you can email, mirror, host anywhere, or run from disk. The software is a file, and the file is yours.</p> <p>Credits and licence</p> <p>I built this in a long series of sessions with Claude (Anthropic&#39;s AI) writing and testing much of the code under my direction, and a playtester helping me find the bugs I missed. The design, the content and the decisions are mine, and all the code and archive material are CC0 (public domain).</p> <p>If you try it, I would love to hear what breaks, especially on iOS and Safari, which I have not been able to test properly.</p> <p>Try it: <a href="https://republicka.life/terminal-velocity.html">https://republicka.life/terminal-velocity.html</a> Source and archive: <a href="https://github.com/BardPresident">https://github.com/BardPresident</a></p>

Top comments (0)