
Every pixel is a palette index
Phosphor is our indexed-color 2D engine, written in Swift and aimed at iPhone and iPad. It draws each frame as a small grid of palette indices, the way consoles and home computers of the 1980s and 1990s did, and can show it through a tube you can tune.
v0.9.3 · in development
Indices, not colors
Each pixel is one byte: an index into a palette of up to 256 colors. A palette groups its colors into ramps, runs from dark to light for one material, and shading is arithmetic on the index. Two steps lighter is two places along the ramp.
A hit flash, a fade or fog is arithmetic on indices, so it costs work in proportion to the palette, not to the number of pixels, and a new color scheme is new data with no new art. The GPU looks each index up in the palette as it draws the frame.

A fixed grid, scaled by whole numbers
A game says how much of the screen it needs, such as a number of rows along the short side. The engine picks the largest whole-number scale that shows it, the other side takes what the screen gives, and any leftover becomes a border, so every grid pixel is the same size. On a large iPhone held upright, a 220 by 478 grid fills the screen at exactly six times.
Rotation redraws a shape onto the grid instead of turning a texture, so a rotated sprite is still made of whole pixels and never blurs.
A CRT you can tune
Phosphor can show its grid through a model of a cathode-ray tube. Each effect has its own switch and settings, a game can give players a settings screen for them, and with every effect off a frame takes one pass on the GPU. Drag the split on each picture to compare it with the plain grid.


Scan lines
A dark line along the last physical row of each grid row.


Aperture grille
Red, green and blue stripes, one physical column each. Best seen at full size.


Curvature
Barrel distortion, as if the picture sat on the curved front of a tube.


Corner pin
Moves each corner on its own. Shown here as keystone, with the top edge narrowed.


Color fringing
Red pulls outward from the center and blue inward, more so toward the edges, the way a tube’s three beams drift apart away from the middle.


Glow
A blurred copy added back on top. It has no brightness threshold, so every lit pixel glows.


Persistence
Lit pixels fade over a set half-life in milliseconds, the same on a 60 Hz and a 120 Hz screen. Here on the shooting star, the embers and the barrel.


Burn-in
The ghost a tired screen keeps of whatever it showed longest. Here, a title card held for six seconds before the cut.


Vignette
Darkening toward the corners. The center stays exact.
Effects that move
Three effects read best moving, so they are shown as clips.
LOOP 0:08Persistence
Trails fade at the same rate whatever the screen’s refresh rate.
LOOP 0:08Flicker
An occasional dip in brightness every few seconds, like a tube on unsteady power.
LOOP 0:08The lean
The rows near one edge slide sideways, and the lean wanders. It reads best with the tired tube below, beside flicker and burn-in.
A tube in good order, and a tired one
Two presets combine the effects. One is a shadow-mask tube in good order: scan lines, the grille, curvature, fringing, glow, trails and vignette. The tired tube adds burn-in, flicker and the lean, for a set twenty years on and never once serviced.




LOOP 0:08
LOOP 0:08Physics that settles, and breaks
Phosphor’s 2D rigid-body physics is deterministic. Stacks settle and go to sleep together, a body rolling over ground made of tiles doesn’t catch on the seams between them, and joints can be given a load at which they break.
LOOP 0:08


Cloth that blows, tears and falls
Version 0.9.3 added cloth for flags and wall hangings. A sheet is pinned to anchors that follow bodies, and gusting wind pushes it. It tears where it is stretched too far, a hit cuts away the cloth under it, and a pin can tear loose or be let go. Each cell is drawn as two triangles, shaded on a palette ramp by how squarely it faces the light.
LOOP 0:08
LOOP 0:05
LOOP 0:08
LOOP 0:08Cloth reads the bodies’ positions and writes nothing back, so a world steps to the same bytes with cloth on or off, and a test checks that. On an iPhone 16 Pro Max, a 192-cell cloth takes about 0.13 ms to step and draw, under 1% of a frame at 60 Hz.
- Seed and inputsConnected to the next step by: run
- Simulationfixed 60 HzConnected to the next step by: every time
- Same framesbyte for byte
Same seed, same frames
The simulation steps at a fixed 60 Hz with no variable time step, and its core uses its own sine, cosine and arctangent instead of the platform’s math library, so it can’t drift between machines. The same seed and inputs give byte-identical frames.
That makes a recorded input trace replay a session exactly, and a packed session can be replayed on another machine, which names the first step where the two disagree. Hundreds of tests cover the modules, and determinism is tested directly: a replay run without a window or GPU must reproduce the saved frame hashes.
Wipes the old hardware could do
Title cards come with eight wipes: cut, fade, blinds, iris, curtain, dissolve, an interlaced scan, and collapse, which shrinks the picture the way a tube did when the power went off. Each is something the hardware Phosphor imitates could actually do. The fade walks every pixel down its own ramp.
LOOP 0:03
LOOP 0:03
LOOP 0:03
LOOP 0:07
Tuning on the phone
Every value a game wants to tune is registered once, and the tuning screen is generated from that list. It runs in a browser through a debug server, or on the phone itself, where it stays usable with hundreds of dials.
Phosphor’s other modules include a tracker-style synthesizer, a 5 by 7 bitmap font with an immediate-mode interface, and tile grids with shadowcast line of sight and A* and flow-field pathfinding. Each module is imported on its own, so a game that doesn’t need physics never links a solver.

Playing the same song on period hardware
Phosphor’s synthesizer can play a game’s songs and effects on the voices of a period machine, which the engine calls an era. The music, instruments and effects stay the same, and the era changes how they are heard. The next release has seven: the NES, the PC speaker, the Tandy 1000 and the SNES play the music on the machine’s own sound hardware, and the Mac Plus, the Mac II and the Sound Blaster Pro play it through the machine’s digital audio output.









The recording, unprocessed.

The figure shows a track from Wreckwing. The recording spreads its energy across the whole band, and each bright vertical stripe is a drum hit. The NES era plays the track on a model of the console’s sound chip, the 2A03: two pulse channels, a triangle and a noise channel, with the console’s output filters on the mix. A part is heard only while it holds a channel of its kind, pitch snaps to the nearest value the chip could play, and volume to its 16 levels.
The evenly spaced horizontal lines are the harmonics of the pulse and triangle waves, and the vertical stripes are drums on the noise channel. The wavy lines are the lead’s vibrato, which moves in steps because the engine writes the chip’s registers once per video frame, 60.1 times a second, as NES sound drivers did. A sound effect takes a channel from the music until it ends, and changing era mid-song fades one machine out and the next in without a click.
Made for our own games
Phosphor is private. We built it for our own games and don’t offer it to other developers. We pulled it out of an earlier physics game of ours, and like the rest of our work it is built almost entirely with Claude Code. One Thousand Ruins, for iPhone and iPad, is the first game built on it, and Bit Pixel Horde is likely to be next.
It is at version 0.9.3, from October 21, 2026, and its interfaces still change between versions. So far it has run on macOS, the iOS Simulator and one iPhone. Version 0.9.2 added Game Center and a one-time unlock through in-app purchase, which still have to be checked on a device against the App Store. Haptics are still to come.