Claude Code drawing a happy face in MacPaint using Xcode UI automation testing
Claude Code learning to draw a happy face using Xcode UI automation testing.

Porting MacPaint to Swift with Claude Code

· Matt Baker

The Computer History Museum released MacPaint's original source code years ago — a mix of Pascal and 68k assembly. I've watched Claude Code ship features at big tech companies for months now. Could it tackle porting an entire application from 1984?

So over a few weekends, I was able to do it, and more. I didn't write a single line of code. I didn't read a single line of what was written. I didn't want to.

The main prompt

I began by discussing my idea with Google Gemini — something I do regularly at work. Assembly to convert, ROM calls to handle, file I/O and printing to address. What else? Gemini came back with the full scope: QuickDraw reimplementation, event model translation, resource fork handling. I pasted that plan into Claude Code, pointed the "superpowers" planning mode at it, and told it to start.

Twenty-five minutes

hover a commit to explore

After a few clarifying questions — should it reimplement QuickDraw or build drawing primitives from scratch, which language to target — Claude Code spun up subagents and went to work. I skimmed its implementation plan. The task breakdown looked reasonable. Twenty-five minutes later, it told me to build and run.

Twenty-five minutes of automated work — a white rectangle in a window

A blank screen. (sigh)

This isn't going to work. But I had a hundred bucks of Claude Code capacity left in the month. Might as well see how far it gets.

First pixels

I told Claude Code the screen was blank. It started debugging — working through the rendering pipeline, checking initialization order, examining the coordinate system. I didn't suggest where to look. I described what I saw: a white rectangle.

Three minutes later, I rebuilt and re-ran.

Three minutes after reporting a blank screen — desktop pattern, fill patterns, no tool icons. Half-formed but unmistakably MacPaint.

The desktop pattern appeared. Fill patterns rendered in their little squares, sitting exactly where they belong. No tool icons or menus yet, but the layout was right. The proportions were right. Half-formed, but unmistakable.

Claude Code wrote its own MFS parser

Claude Code explained why: those icons live in the resource fork of the original MacPaint binary, not in the source code. It then gave me two options: find the original resources, or recreate them from an emulator screenshot.

I said "find them" and went to make coffee.

When I came back, Claude Code had gone online, found a MacPaint disk image, and tried to read it. The image was formatted with MFS — Macintosh File System, Apple's original 1984 filesystem that predates HFS. Nothing modern reads MFS.

So Claude Code wrote an MFS parser from scratch. Figured out the directory format. Decoded the resource fork's internal map. Extracted every icon and dropped them into the project.

MacPaint tool palette with icons extracted from original 1984 disk image by Claude Code — pencil, paintbrush, spray can, line tool, and shape tools visible

I told it to find the icons. It found a disk image it couldn't read, wrote a parser for a 42-year-old filesystem, and extracted what it needed. No questions asked. That's the moment I stopped wondering how far this would go and decided to finish it.

Every menu, every tool, every pixel

The working relationship with Claude Code settled into a loop. I described what was wrong. "This menu doesn't track right." "The cursor is split in half." "The fill bleeds through." Fixed. Every time.

Window chrome came first — title bar with horizontal stripes, close box in the upper left, document title centered in Chicago 12pt.

MacPaint window chrome ported by Claude Code — title bar with horizontal stripes, close box, document title

Then I added fullscreen mode. I tolde Claude to fill space with checkerboard.

MacPaint fullscreen mode — integer-scaled pixels with checkerboard background

Native macOS menus worked but looked wrong — the empty space at the top broke the illusion. So Claude Code rendered the menu bar as a bitmap inside the 512x342 framebuffer, matching the original pixel-for-pixel.

MacPaint Goodies dropdown menu with Grid, FatBits, Show Page, Edit Pattern, Brush Shape, Brush Mirrors

Every drawing tool works - fill patterns, fonts, etc. The lasso took some time — Atkinson's original implementation is 68000 assembly doing bit manipulation that had to survive translation to C on a completely different processor architecture. FatBits,

Every menu item works except Print Catalog, which read a 400K floppy to generate thumbnail pages. I left it there for posterity.

Beyond the original

I updated copy and paste to work with modern Macs. Paste a photo from Safari and MacPaint dithers it to black and white automatically. Copy a selection and paste it into Notes or Messages as a standard image.

The import dialog gives more control: a navigator pane shows the full picture, a detail pane shows dithered pixels at 1:1 scale. Three dithering modes — Atkinson, ordered, and threshold. Drag and zoom before committing.

MacPaint import dialog — navigator pane with full image, detail pane showing Atkinson-dithered result at 1:1 pixel scale

Printing routes through macOS natively — 72 DPI, black and white, to whatever printer is available.

MacPaint on iPad

A child drawing stick figures in MacPaint running on iPad

The iPad port took thirty minutes to first run. The C core compiled to ARM unchanged. SwiftUI handled the rest.

After that I added the older style menus. MacPaint's original interaction — press, hold, drag through items, release to select — doesn't translate to touch. Clause handled it. Do I know how? Should I care? As a life long software engineer and Apple enthusiest, I feel like I should. But I was just happy it made a stable, usable version of the first app I loved.