Infinite Mac: An Instant-Booting Quadra in Your Browser #

tl;dr

I’ve extended James Friend’s in-browser Basilisk II port to create a full-featured classic 68K Mac in your browser. You can see it in action at system7.app or macos8.app. For a taste, see also this screencast:

Backstory

It’s a golden age of emulation. Between increasing CPU power, WebAssembly, and retrocomputing being so popular The New York Times is covering it, it’s never been easier to relive your 80s/90s/2000s nostalgia. Projects like v86 make it easy to run your chosen old operating system in the browser. My heritage being of the classic Mac line, I was curious what the easiest to use emulation option was in the modern era. I had earlier experimented with Basilisk II, which worked well enough, but it was rather annoying to set up, as far as gathering a ROM, a boot image, messing with configuration files, etc. As far as I could tell, that was still the state of the art, at least if you were targeting late era 68K Mac emulation.

Some research into browser-based alternatives uncovered a few options:

However, none of these setups replicated the true feel of using a computer in the 90s. They’re great for quickly launching a single program and playing around with it, but they don’t have any persistence, way of getting data in or out of it, or running multiple programs at once. macintosh.js comes closest to that — it packages James’s Basilisk II port with a large (~600MB) disk image and provides a way of sharing files with the host. However, it’s an Electron app, and it feels wrong to download a ~250MB binary and dedicate 1 CPU core to running something that was meant to be in a browser.

I wondered what it would take to extend the Basilisk II support to have a macintosh.js-like experience in the browser, and ideally go beyond it.

Streaming Storage and Startup Time

The first thing that I looked into was reducing the time spent downloading the disk image that the emulator uses. There was some low-hanging fruit, like actually compressing it (ideally with Brotli), and dropping some unused data from it. However, it seemed like this goal was fundamentally incompatible with the other goal of putting as much software as possible onto it — the more software there was, the bigger the required disk image.

At this point I switched my approach to downloading pieces of the disk image on demand, instead of all upfront. After some false starts, I settled on an approach where the disk image is broken up into fixed-size content-addressed 256K chunks. Filesystem requests from Emscripten are intercepted, and when they involve a chunk that has not been loaded yet, they are sent off to a service worker who will load the chunk over the network. Manually chunking (as opposed to HTTP range requests) allows each chunk to be Brotli-compressed (ranges technically support compression too, but it’s lacking in the real world). Using content addressing makes the large number of identical chunks from the empty portion of the disk map to the same URL. There is also basic prefetching support, so that sequential reads are less likely to be blocked on the network.

Along with some old fashioned web optimizations, this makes the emulator show the Mac’s boot screen in a second, and be fully booted in 3 seconds, even with a cold HTTP cache.

Building Disk Images, or Docker 1995-style

I wanted to have a sustainable and repeatable way of building a disk image with lots of Mac software installed. While I could just boot the native version of Basilisk II and manually copy things over, if I made any mistakes, or wanted to repeat the process with a different base OS, I would have to repeat everything, which would be tedious and error-prone. What I effectively wanted was a Dockerfile I could use to build a disk image out of a base OS and a set of programs. Though I didn’t go quite that far, I did end up something that is quite flexible:

  1. A bare OS image is parsed using machfs (which can read and write the HFS disk format)
  2. Software that’s been preserved by the Internet Archive as disk images can be copied into it, by reading those images with machfs and merging them in
  3. Software that’s available as Stuffit archives or similar is decompressed with the unar and lsar utilities from XADMaster and copied into the image (the Macintosh Garden is a good source for these archives).
  4. Software that’s only available as installers is installed by hand, and then the results of that are extracted into a zip file that can be also copied into the image.

(I later discovered Pimp My Plus, which uses a similar approach, including the use of the machfs library)

I wanted to have a full-fidelity approach to the disk image creation, so I had to extend both machfs and XADMaster to preserve and copy Finder metadata like icon positions and timestamps. There was definitely some cognitive dissonance in dealing with late 80s structures in Python 3 and TypeScript.

Interacting With The Outside World

Basilisk II supports mounting a directory from the “host” into the Mac (via the ExtFS module). In this case the host is the pseudo-POSIX file system that Emscripten creates, which has an API. It thus seemed possible to handle files being dragged into the emulator by reading them on the browser side and sending the contents over to the worker where the emulator runs, and creating them in a “Downloads” folder. That worked out well, especially once I switched a custom lazy file implementation and fixed encoding issues.

To get files out, the reverse process can be used, where files in a special “Uploads” folder are watched, and when new ones appear, the contents are sent to the browser (as a single zip file in the case of directories).

Persistence

While Emscripten has an IDBFS mode where changes to the filesystem are persisted via IndexedDB, it’s not a good fit for the emulator, since it relies on there being an event loop, which is not the case in the emulator worker. Instead I used an approach similar to uploading to send the contents of a third ExtFS “Saved” directory, which can then be persisted using IndexedDB on the browser side.

Performance

The emulator using 100% of the CPU seems like a fundamental limitation — it’s simulating another CPU, and there’s always another instruction for it to run. However, Basilisk II is working at a slightly higher-level, and it knows when the Mac is idle (and waiting for the user input), and allows the host to intercept this and yield execution. I made that work in the browser-based version by using Atomics to wait until either there was user input or a screen refresh was required, which dropped CPU utilization significantly. A previous blog post has more details, including the hoops required to get it working in Safari (which are thankfully not required with Safari 15.2).

The bulk of the remaining time was spent updating the screen, so I made some optimizations there to do less per-pixel manipulation, avoid some copies altogether, and not send the screen contents when they haven’t changed since the last frame.

The outcome of all this is that the emulator idles at ~13% of the CPU, which makes it much less disruptive to be left in the background.

Odds and Ends

There were a bunch more polish changes to improve the experience: making it responsive to bigger and smaller screens, handling touch events so that it’s usable on an iPad (though double-taps are still tricky), fixing the scaling to preserve crispness, handling other color modes, better keyboard mapping, and much more.

There is a ton more work to be done, but I figured MARCHintosh was as good a time at any to take a break and share this with the world. Enjoy!

Update: See also the discussion on Ars Technica and Hacker News (take 2). There is also a follow-up blog post with some post-launch details.

35 Comments

Awesome project. I look forward to trying it out. 🤓
Beautifully done! It's taken me back to my days on Photoshop 1.0 before layers even existed. Thank you.
unreal.. this is so good.. thank you
nice - now I wish I had kept my CodeWarrior disks, the stack of inside Macintosh books and developer CDs, MPW was a dog on my LCII, might actually be useful in the emulator... I much preferred CW to ThinkC. Would be easy to spend hours faffing around with this rather than getting some work done.
Any suggestions for opening a .dsk file in the emulator?
OMG! What an amazing project!

I copied over a BinHex install set of the last major System 7 MPW app I wrote. Unpacking it with Stuffit really brought back some memories! Sadly, it shows error -192 when I try to run it. The app includes some libraries compiled with DEC Fortran for MPW, so I'm not entirely surprised.

I still have sources for some Think C apps from 1987; gonna try to compile them next.

Thanks for this project!
Great work. File conversion just got much easier.

Is it possible to add old coding projects to your environment and launch them? I wrote some abstract art screen savers in the '90s - one was golden flying toaster-worthy. I wrote stand-alone players to customize and run them, not to mention a few other miscellaneous packages.

Can the emulator handle 8 bit CLUT animation and 24bit gamma table animation?

On a separate note, the old Macintosh had a great 2D graphics toolbox for bitmaps with features that still are hard to match today, including addover penmodes and copybits operations clipped with complex regions. Has anybody ever ported Quickdraw or, better yet, Quickdraw GX into an open source package accessible in a modern development environment? That would be sweet enough to send me into a digital diabetic coma.

(.leftbracket.)wriddick(.at.)usa(.dot.)net(.rightbracket.)
I was trying to run this in emulation but I wasn't able to open dmg files. Is there a way to do this yet?
Are you sure you've got the right ROM? I've tried both of your in-browser emulators & they are incredibly unstable in every browser I've tried (except Chrome, I won't use that).
Amazing Project! Can't seem to get sound from running this on an iPad.
Like sitting in front of my PowerBook 140 again — only with color!
Tried playing Marathon but limited to using keyboard controls because the mouse speed is insane. I chose the slowest mouse speed in OS7 but it was still too fast.
Also, I can't figure how to quit the game. ESC doesn't work nor does [modifier] Q.
(Do I have to get to a pattern buffer first? It's been so long I can't remember.)

If anyone has solutions to either of those issues please let me know. Otherwise, I'll have to fire up my (working) PowerMac 8500 or 9600 to play.
Tried running both Ssystem7 & System8 on a Samsung Chromebook Plus (using Chrome) & it gets stuck at the "loading files 4/4" stage...any suggestions ?

2 typos you may wish to fix from your Stickies:

a modern web browser
the mid-1990s was like
@Saru: If you go full-screen you should be able to use Command-Q to exit Marathon games (at least in Chrome, no other browser appears to support the keyboard lock API: https://caniuse.com/mdn-api_keyboard_lock)
** Tried playing Marathon but limited to using keyboard controls because the mouse speed is insane. I chose the slowest mouse speed in OS7 but it was still too fast.
Also, I can't figure how to quit the game. ESC doesn't work nor does [modifier] Q.
(Do I have to get to a pattern buffer first? It's been so long I can't remember.)

** @Saru: If you go full-screen you should be able to use Command-Q to exit Marathon games (at least in Chrome, no other browser appears to support the keyboard lock API: https://caniuse.com/mdn-api_keyboard_lock)

Being full screen (clicking the Apple logo on the monitor bezel) and Command-Q worked! (At least in Chrome.) Thanks.

This is really quite an impressive feat. I'm guessing the Japanese Language Kit and a lot of my old Japanese software probably won't run. Still, it will be fun to try.
Awesome project, congratulations!

I'd be very happy to see my old classic game 3Tris / 3Wiz in the games collection, feel free to add it if you wish. Here is the original '90s link to the dowloadable hqx file:

http://esurfers.com/francesco/gadgets/download/wizcol.hqx

(Color version)
i think the desktop file needs rebuilding on macos8.app, imported .sit files and the sample movies wont play by double-click
@Anonymous: That’s a known issue (https://github.com/mihaip/infinite-mac/issues/4). In the meantime you can rebuild the desktop manually by holding down command and option during the startup sequence (you can restart the Mac from the Special menu too).
The obvious game to try with this is the awesome Royal Flush pinball emulator.
https://gp.home.xs4all.nl/Royal%20Flush/DownloadRF.html
But it doesn't work :(
I lied. It works great!!! So cool.
May I suggest including Royal Flush in the games folder. I can't image that Gerard would object.
Never mind...Royal Flush works, but it's unacceptably slow. Note that it works fine on Basilisk. I'm on an M1 Macbook Pro. Pinball games aside, this is still an amazing piece of work! (sorry I've been so chatty - I'll stop now).
Wow.... Good job!!!
Very much loving this project.
2 suggested updates:
a) add support for 1024x768, as when I was supporting graphic designers in the 1980's and 1990's, everyone wanted as big a screen as they could afford/
b) please replace Strategic Conquest Plus 2.x with Strategic Conquest 3.x (available in online repositories). It's an improved version of the game with better AI.
Wow. Thanks for memories
launching hypercard again literally made me cry
/_/
@jsepeta: Filed https://github.com/mihaip/infinite-mac/issues/46 about allowing the resolution to be changed. Added Strategic Conquest 3.0 with https://github.com/mihaip/infinite-mac/commit/b648ad0acb1b9cea33cd8239fdb61560147905fe, it should be live with the next deploy (in a couple of days most likely).
@Francesco: Finally got around to adding 3Wiz: https://github.com/mihaip/infinite-mac/commit/865432175bd02d97023570e9e53e1ce4f07d53db
Thank you Mihai Parparita
Hi - This is an amazing piece of work! Is there a way to save projects and updates through a restart? I would love to be able to play my old favorite game, Scarab Of Ra, and access some of the DocMaker articles I wrote when I was a columnist for “About This Particular Macintosh.” I tried a few preference settings changes to see what happened, but after closing and restarting they were gone. Thanks!
@Anonymous: The contents of the "Saved" folder in "The Outside World" drive should be saved (it works for me in Chrome on a desktop Mac, though I have not tested it everywhere).
Thanks for the reply. The Saved folder does work for me. I was wondering about putting apps in the Applications Folder, but maybe I should just leave them in the saved folder? And also any docs I create? I think I can live with that. Should the Saved Folder survive a host computer restart? Thanks again. Your efforts are remarkable! Ed
If I can install the games Burning Monkey Puzzle Lab, Tetris, and Factory in this my wife will be ecstatic.

Post a Comment