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:
- James Friend’s in-browser ports of the Basilisk II and PCE.js emulators
- The Internet Archive’s Mac Emulation, which is partly based on James’s PCE.js work
- OldWeb.Today, which extends James’s Basilisk II work to support a working networking stack
- RetroWeb Vintage Computer Musem, also based on James’s PCE.js work
- The Macintosh Repository is experimenting with in-browser emulation via either Basilisk II or vMac
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:
- A bare OS image is parsed using machfs (which can read and write the HFS disk format)
- 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 - Software that’s available as Stuffit archives or similar is decompressed with the
unar
andlsar
utilities from XADMaster and copied into the image (the Macintosh Garden is a good source for these archives). - 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, and another describing the implementation of networking.
66 Comments
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!
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.)
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.
a modern web browser
the mid-1990s was like
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.
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)
https://gp.home.xs4all.nl/Royal%20Flush/DownloadRF.html
But it doesn't work :(
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.
On the topic, as I said I own a copy of 4.0.1 from the Before Time. Is it not available as part of the games on Infinite Mac because you don't have a copy, or are there ownership issues with loading anything beyond 3.0? If it's just a matter of getting hold of the game, I'd be happy to share the .sit file. Thanks, this is a very interesting project!
I'm noticing that I can't toggle volume slider away from 0 on either macos8.app or macos9.app. Seems consistent across browsers and devices (tried on my 2020 intel MBP using Chrome, Firefox and Safari and my 2021 ipad pro on Chrome). Is this related to https://github.com/mihaip/infinite-mac/issues/67?
@John T: I appreciate the interest in a more premium offering, but it's not something that I will be able to offer. If you're looking for persistence and more control, running Basilisk II or SheepShaver locally is going to be your best option.
(“Import” floppy icon)
As for making the screen more phone-friendly, I'm not sure there's much that can be done -- the OS was designed for large screens, and is not responsive.
Can you make Mac os 9.1 and 9.2?
As soon as possible please!
As for sound/speech synthesis, you'll have better luck in System 7 (https://system7.app). Sound is still not working in Mac OS 8 or Mac OS 9: https://github.com/mihaip/infinite-mac/issues/67
Post a Comment