← back to work

Case study · Atari Lynx

Wolfenstein 3D on the Atari Lynx

The entire 1994 Mac version — every level, boss, sprite and animation — squeezed into a 512 KB cartridge and rendered by a 16-bit sprite chip named Suzy. Four years of work.

Shareware release — out now

The shareware build (first three levels) is publicly released and available to download and run. The full version is still in progress.

Download on AtariAge ↗
Wolfenstein 3D running in a Lynx emulator with the custom profiler open
Wolfenstein 3D running in GearLynx, with the custom CPU / sprite-engine profiler attached.

Why Wolfenstein, and why the Lynx?

I took my first steps on the Lynx in 2017, after reading the Diary of an Atari Lynx Developer series. I'd always been curious about how the machine worked under the hood, and what its unrealised potential was. It got serious around 2021.

I've always been a huge id Software fan — they were my heroes at university. Every John Carmack .plan file, every interview, every Michael Abrash article, all of their source code: I lapped it up. But it was John Romero mentioning that id had themselves started an Atari Lynx version of Wolfenstein — and how cool they thought it would be on that platform — that made the idea take hold. After looking at other ports to see what had been done, I realised the Lynx could do a genuinely good job here.

What came across from the original

The Lynx version is built on the source of the 1994 Mac release — itself a derivative of the SNES and Jaguar versions. It contains all of the original gameplay data and logic, all sprites and animations, wall textures, every boss and all of the levels. The whole Mac version has been squeezed into a 512 KB cartridge. Some of the larger maps have reduced wall complexity to fit into memory, and in-game music was cut in favour of sound effects.

How the engine draws

The renderer is a heavily customised version of the original Mac one. The Mac uses a Binary Space Partitioning (BSP) tree to speed up visible-surface determination; the Lynx does too, with some extra culling on top.

Where the Lynx differs is how walls are actually drawn. The Mac draws walls with the CPU, column by column, pixel by pixel. The Lynx has a 16-bit sprite engine — a chip called Suzy — with some insanely powerful scaling features. So instead of the CPU drawing each wall pixel by pixel, the Lynx draws each wall section once with Suzy, then leverages her ability to stretch and tilt that section into the required 3D perspective.

That really is the secret sauce. Each wall segment is a single Suzy stretched sprite. On any other platform of the time, a wall segment is hundreds of thousands of CPU cycles of work. On the Lynx it's almost zero.
A 3D Wolfenstein scene built up one wall segment at a time
The whole 3D world in this scene is drawn with only 6 sprites — shown building up one segment at a time from a blank screen.

The main drawback: because Suzy can only perform these transformations across different scan-lines, the Lynx has to be held in portrait — TATE mode — for the effect to convince. (In id's own unreleased Lynx Wolfenstein, they made the same call.)

Diagram of Suzy's two scaling operations combining into a perspective warp
Suzy can stretch a sprite one row at a time, or shift its start position one row at a time. Combined, they produce a perspective warp in a texture essentially for free — as long as it's viewed on the side.
The parallelism trick

Suzy can also do multiplication and division in hardware — used constantly for perspective math. Crucially, the Lynx can run those operations simultaneously while the CPU does other work. Organising the code so CPU work and Suzy math overlap was the most time-consuming optimisation, and the most effective — effectively more work done per clock.

Getting the art onto the cart

Almost nothing transfers straight across — the Lynx is too different from a Mac or PC, so everything had to be converted. Graphics were the big job. The original ran in 256 colours; the Lynx shows 16 on screen at once. Picking a 16-colour palette that looked authentic, stayed readable in-game, and simply looked good took a long time.

Every in-game image was crushed to that palette, resized for the smaller screen, then given a manual per-pixel touch-up. At build time the images are packed into the Lynx's native compressed sprite format to save cartridge space.

Lynx enemy sprites next to the original assets
Enemy sprites, Lynx palette versus the originals.
Lynx wall textures next to the original assets
Wall textures, reduced to 16 colours.
In-game assetsUncompressed (3bpp)Packed on-cart
Sprites (170 frames)238 KB74 KB
Walls (35 textures)6.6 KB6.4 KB
Total≈ 245 KB≈ 81 KB

So 245 KB of art compresses into 81 KB — and of that, only about 5 KB ever lives in Lynx memory at once. In-memory sprites, enemies and walls alike, sit in a time-to-live (TTL) cache that's refreshed every frame. Sound is a similar story: samples were pulled from the DOS version and downsampled to 4-bit, 2000 Hz, one-second clips.

The toolchain

Development is Visual Studio Code with Neovim, plus a heavily modified GearLynx emulator I've extended with a profiling suite for CPU and sprite-engine timings. Everything else — map editors, sprite editors, cartridge packers — is custom Python.

Custom Wolfenstein 3D map editor
Custom Wolfenstein 3D map editor.
Custom Lynx sprite editor
The Lynx sprite editor.
Development environment in VS Code
Day-to-day: VS Code + Neovim.

Could the engine run other games?

Yes. All source and tools will be released once the full Wolfenstein 3D is complete — at which point anyone could swap the assets and build something new. A Doom conversion is possible too, likely based on the SNES version with its simplified geometry, but it would probably need at least a 1 MB cart. The real limit is the Lynx's tiny RAM: assets can't be read straight off the cartridge — everything, from lookup tables to enemy logic to graphics and sound, has to be copied into RAM first, so the cart behaves more like a disk drive than ROM.

In progress

Beyond finishing the full Wolfenstein 3D, current Lynx work is split across a port of the Amiga's Another World and the Atari ST version of Prince of Persia — plus a released Super Mario Bros. port and a Mortal Kombat II proof of concept. See all Lynx projects →

← Back to portfolio Get in touch