macemu

Classic Mac software, running in your browser tab

How a 1991 Macintosh Boots in a Browser Tab in Two Seconds

September 2026 · Andrew Nakas

A Macintosh Quadra 650 with System 7.5.3 and a two-hundred-megabyte hard disk starts on this site in about two seconds. Nothing is installed and nothing is downloaded in advance. This is how.

The emulators are old, and that is the point

None of the emulation here is new work. Mini vMac has been emulating compact Macintoshes since 2001. Basilisk II and SheepShaver go back to the late 1990s. All three are mature C and C++ codebases that have been correct for a very long time, and all three compile to WebAssembly with Emscripten.

The packaging — getting them to run well in a browser, with a disk layer, sound, and input that behaves — is the work of Mihai Parparita's Infinite Mac, which is Apache-licensed and which this site builds on directly. If you find macemu useful, donate to that project. It is the layer everything here stands on.

The disk is the interesting part

The naive way to put an emulated hard disk on the web is to download the image. A System 7.5.3 disk with a useful amount of software on it is a couple of hundred megabytes, so that is a minute of waiting before anything appears — which is the difference between a thing people use and a thing people bounce off.

Instead the image is split into 256 KB chunks, each named after the hash of its own contents, with a small JSON manifest listing the chunks in order. The emulator asks for a sector; the disk layer works out which chunk holds it and fetches that one. Booting a Mac touches a few megabytes at most — the system file, the Finder, the extensions it actually loads — so a boot is a few dozen small requests, in parallel, from a CDN edge.

Naming a chunk after its contents has a second effect: a name always means the same bytes, so every chunk can be cached immutably for a year, and a browser that has booted one title already holds part of the next.

It does less than you would hope across titles, though. The intuition is that every title is the same System Folder plus one application, so the disks should overlap almost completely. They do not. Writing an HFS volume rewrites its allocation blocks, so the same files land at different offsets and almost no 256 KB window is byte-identical between two builds. Measured across the first title: zero shared chunks out of eighty-one. Budget a full disk per title, not a delta. Deduplication is real, but it works within a disk and across rebuilds of the same disk, not between siblings.

Cross-origin isolation, and the page split it forces

The emulator runs in a Web Worker and is dramatically faster when it can share memory with the main thread through a SharedArrayBuffer. Browsers only hand that out to a document that is cross-origin isolated: it must send Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp.

Those headers have a cost. require-corp blocks every cross-origin subresource that has not explicitly opted in — including every advertising iframe, and most third-party embeds. So a page cannot be both maximally fast and freely composable. It has to choose.

macemu chooses twice. The /run/ page is the article: history, controls, licensing, screenshots, the thing a search engine indexes. The /play/ page is the machine: isolated, no ads, no article, noindex, canonical pointing back at /run/. And /embed/ is the opposite extreme — framable by anyone, deliberately not isolated, running the slower fallback path, with a link back to the fast page.

This is not a hypothetical concern. On exebrowser.com, this site's DOS and Windows sibling, a site-wide COEP header meant that an advertising reviewer loaded the site and saw no advertisements at all, because the iframes were being blocked before they could render. That contributed to three rejections. One header, applied one level too broadly.

There is no build system

The site is static files on Cloudflare Pages. The generators are plain Node ESM scripts with no dependencies — node scripts/gen-pages.mjs reads a JSON catalogue and writes HTML. There is no framework, no bundler for the site itself, and no node_modules directory in the repository.

What there is instead is a gate. scripts/check-consistency.mjs runs before every deploy and refuses if anything has drifted: a title claiming to be playable with no disk manifest behind it, a screenshot declared but missing, a broken internal link, an advertising tag on an isolated page, a /play/ route without noindex, a sitemap listing a page that does not exist. Every one of those checks exists because the corresponding mistake is invisible from the outside until a visitor hits it.

What it still cannot do

No printing. No network inside the emulated machine. Getting files back out is not wired up. Mac OS X is a different architecture and a different problem, and is not attempted here. The what runs page is honest about all of it.

More