Architecture
How MacSurf fits together: rendering pipeline, networking, memory model, and the libraries underneath.
A Modern Web Platform for Classic Mac OS 9
Version: 2.0.5
Status: Shipping — native rendering pipeline, live on real hardware
Author: MPLS LLC
This page used to be a pre-1.0 design doc for a proxy / server-side-rendering / per-site-template architecture. All of that was abandoned. Everything now runs on the Mac: the HTML parser, the CSS cascade, layout, painting, TLS, and a modern JavaScript engine. There is no proxy on the path and no server-side rendering anywhere. This page describes what actually ships. For property-by-property CSS detail see CSS status; for the release-by-release story see History.
1. What MacSurf is
MacSurf is a web browser for Classic Mac OS 9 on PowerPC — a Carbon CFM application built with CodeWarrior 8 that runs a full rendering engine on 1997-2002 hardware. Not a thin client, not a screenshot streamer, not a remote renderer: the Mac does the work. It parses the HTML, cascades the CSS, lays out the page, paints it with QuickDraw, handles its own TLS, and runs the page's JavaScript.
The same Carbon binary also runs on Mac OS X 10.0 through 10.4 (maintainer-verified 2026-07-21 — full HTTPS render of 68kmla.org on 10.3, no regression on OS 9). One artifact, both platforms: no Mach-O target, no second build, no Classic environment. That work is in the 3.0 development line, ahead of the current tagged release.
2. The rendering pipeline
MacSurf is a Carbon/PowerPC port of NetSurf's engine. All five NetSurf core libraries are ported and C89-clean — libwapcaplet, libparserutils (15 files), libhubbub (30), libdom (95), and libcss (303) — 443 source files, roughly 125,000 lines of C, every one of them compiled by CodeWarrior 8 in strict C89 mode. The pipeline as it actually runs:
URL entered
→ macos9_http_fetcher (HTTP/1.1 over Open Transport)
macos9_tls_fetcher (HTTPS natively via macTLS — no proxy)
→ fetch messages (FETCH_HEADER, FETCH_DATA, FETCH_FINISHED)
→ llcache / hlcache
→ html_init content handler (libhubbub parser)
→ DOM tree (libdom)
→ CSS cascade (libcss, native var() resolution)
→ NetSurf layout engine (flex, grid, tables, floats)
→ macQJS (page JavaScript runs here, mutating the DOM)
→ CONTENT_MSG_REDRAW
→ macos9 plotters (QuickDraw: rects, gradients, round-rects, text-shadow, images)
→ Window update
Modern CSS that renders correctly includes Grid (V2, with placement and alignment),
Flexbox, gradients, custom properties (var() resolved at cascade time),
border-radius, text-shadow and opacity via QuickDraw stipple,
background-clip, logical properties, and font-family alias dispatch. CSS
feature work is always done in libcss and the layout engine — never
preprocessed or offloaded. Current gaps and the ranked queue live in
CSS status and Issues.
2.1 JavaScript — macQJS
The JavaScript engine is macQJS, a port of
QuickJS that runs ES2023
on the Mac. It is linked into the base build (gated by WITH_QUICKJS,
on by default) and evaluates page scripts directly — there is no transpilation step
and no server-side JS execution.
macQJS replaced Duktape, the ES5.1 engine MacSurf shipped through the 1.5 line. Duktape was removed from the tree entirely at the 1.68 "macQJS" release, and the custom ES6→ES5 transpiler that had propped it up was retired with it — QuickJS runs modern JavaScript directly, so neither was needed. See History for that migration and Story for the original ES5 milestone that preceded it.
What that buys, measured on hardware 2026-07-25: a full modern WordPress stack —
hackaday.com's front page, an article page, and a cross-origin comment iframe —
executes with zero JavaScript exceptions. jQuery 3.7.1, slick, dotdotdot,
Typekit and the XenForo bundles all run. Alongside the engine sit the real browser
plumbing pieces script needs: a genuinely async XMLHttpRequest and
fetch() over Open Transport, real DOM mutation
(createTextNode, createDocumentFragment, innerHTML=),
a full RFC-6265 cookie jar, and per-host User-Agent selection.
Known gaps, all tracked: matchMedia answers false to every query,
MutationObserver / ResizeObserver / IntersectionObserver are stubs,
<script type="module"> is dropped, and innerHTML reads back
as textContent. Heavy SPA frameworks and very large DOM-mutation apps are
the open frontier. Nothing is offloaded — what runs, runs on the Mac, and gaps get
filled as we go.
3. Networking and TLS — macTLS
The browser fetches straight from origin. HTTP/1.1 goes over Open Transport with chunked transfer, keep-alive, redirect following and connection pooling. HTTPS goes through macTLS, a TLS stack built into the binary.
macTLS is hand-written TLS 1.3 (RFC 8446 — X25519 and multi-curve ECDHE, ChaCha20-Poly1305 and AES-128-GCM) with TLS 1.2 fallback, the full Mozilla CA bundle (121 anchors) baked in, and its own entropy source behind it. It is, as far as we know, the first TLS 1.3 implementation on Classic Mac OS (v1.3, 2026-05-29).
The Go proxy is retired. MacSurf originally shipped with a small Go binary on a VPS that stripped TLS on the Mac's behalf. Native macTLS replaced it around 2026-05-25 and it has not been on the request path since. The Mac opens its own origin connections — which is also what makes real logins work, since cookies and credentials live on the actual client where sites expect them.
4. The Mac side
Carbon requirements. MacSurf is a Carbon CFM app. A 'carb' resource
is mandatory on OS 9 — without it CarbonLib never loads as a dependency and Open
Transport calls crash inside an uninitialized client context. A 'plst'
resource gives the same binary a bundle identity under OS X 10.0-10.4; the two coexist,
so one artifact stays correct on both.
No threads. OS 9 is cooperatively multitasked, so the UI runs a
WaitNextEvent loop and Open Transport yields through a sync-idle notifier.
There are no preemptive threads anywhere in the browser.
Windows, not tabs. Multi-window is real — window list, File ▸ New Window, per-window event and reformat loops. Tabs are unimplemented: there is no tab code in the frontend at all.
Scrolling input. Scroll bar, arrow keys, Page Up/Down and Home/End all work. Carbon-native mouse-wheel events are architecturally unavailable — CarbonLib on OS 9 never back-ported the wheel event class, and asking for it destabilizes the dispatcher.
Memory. 128 MB of RAM is the practical minimum, 256 MB recommended, and 384 MB for the heaviest JavaScript sites. The application partition ships large (roughly 195 MB preferred / 164 MB minimum) because libcss and the DOM allocate heavily on real pages.
Bookmarks and history persist to flat files alongside the application, with standard Back/Forward navigation.
5. What this is not
MacSurf is not a remote-desktop or pixel-streaming client, it does not render or execute JavaScript server-side, and it does not depend on a proxy — those were all earlier design directions, and every one of them was abandoned once the native stack worked. It is not trying to make OS 9 a general-purpose computing platform. It is a browser for people who love classic Mac hardware and want to use it: a beige G3, MacSurf, and the web rendering on 1998 silicon.
For what's left to build, see Issues and History → What's Next.