Size Coding
The art of compression
Size coding emerged from demo scene competitions with strict byte limits, pushing programmers to achieve impossible effects in 64KB, 4KB, or even 256 bytes.
How much can you fit in 64 kilobytes? What about 4 kilobytes? 256 bytes? The demo scene answered these questions with productions that seemed to defy physics. Size coding — creating complex audiovisual productions under strict byte limits — became an art form. The techniques developed fed into game compression, procedural generation, and efficient programming practices.
Fast facts
- Origin: Demo scene size competitions (late 1980s).
- Categories: 64K, 4K, 1K, 256 bytes, smaller.
- Techniques: Procedural generation, compression, mathematical synthesis.
- Influence: Game engines, web development, embedded systems, JavaScript code-golf.
Size categories
| Category | Limit | Challenge |
|---|---|---|
| Demo | Unlimited | Full artistic expression |
| 64K intro | 65,536 bytes | Procedural textures, music, scenes |
| 4K intro | 4,096 bytes | Extreme compression, single-pass synth |
| 1K intro | 1,024 bytes | Minimalist effects, raymarchers |
| 256 byte | 256 bytes | Pure code golf, single effect |
| 32 / 64 byte | 32 / 64 bytes | Single instruction-level idea |
Legendary productions
| Production | Size | Achievement |
|---|---|---|
| .kkrieger | 96 KB | Complete first-person shooter (Farbrausch, 2004) |
| Elevated | 4 KB | Mountain landscape flythrough (Rgba/TBC, Breakpoint 2009) |
| fr-08: .the .product | 64 KB | Full 3D engine with procedural textures and music (Farbrausch, 2000) |
| Memories | 4 KB | Atari 8-bit raymarched scene |
| A Mind Is Born | 256 bytes | Full C64 production with music and visuals (Linus Åkesson, 2017) |
Techniques
Procedural generation
Replace stored content with code that generates it. A single trig formula computes a 256×256 cloud texture; a noise function fills a height-field; an L-system grows a tree. The 4 KB demos Elevated and Cdak generate entire mountain landscapes from one ray-march loop and a couple of sine-based noise functions.
Mathematical textures
// 4-byte expression generating a checker pattern
((x ^ y) & 8) ? 1 : 0
Plasma, marble, wood, fire, water — all common demo textures fall out of two or three sines plus an absolute value. The “shader” is shorter than the texture file would be.
Executable compression
The compiled binary is fed to a compressor that emits an unpack stub plus compressed data. Standard tools:
| Compressor | Platform | Typical use |
|---|---|---|
| Crinkler | Windows x86 | 4 KB / 64 KB intros — context-mixing arithmetic coder |
| Aplib | Cross-platform | LZSS variant, fast decompression |
| Pucrunch | C64 / 6502 | Pasi Ojala’s 1998 hybrid LZ77 and RLE; compresses on a PC, decompresses on the C64 with no extra memory |
| Exomizer | C64 / 6502 | Among the tightest packers in Codebase64’s 2016 benchmark (45% of input, level with Subsizer), at the cost of slow depacking |
| kkrunchy | Windows x86 | Used by Farbrausch (.kkrieger and friends) |
| upx | Cross-platform | Generic; used for normal binaries, not size-tight intros |
A 4 KB intro typically runs the binary through multiple passes: dead-code elimination, custom linker scripts, then crinkler. Five hundred bytes of output for ten kilobytes of source is normal.
Synthesised audio
Stored samples are too big. Instead: single-pass softsynth. A short oscillator routine generates each note, instrument envelopes are computed live, the song is a list of (note, length, instrument) tuples. Tools like 4klang (Windows 4 KB) and Clinkster (small-intro audio) ship with whole synths in under 1 KB. On retro platforms the SID and AY chips already are softsynths in hardware — the song data dominates rather than the audio code.
Imports / API minimisation
On Windows, calling kernel32.dll requires loading the function pointer table. A 4 KB intro skips the standard linker and walks the PE export tables itself, costing ~30 bytes of stub vs ~200 bytes of normal import handling. On retro platforms the analogue is direct hardware access instead of going through KERNAL/BIOS routines.
Code golf
Single-effect 256-byte productions are mostly hand-crafted assembly where every byte is justified. Common tricks:
- Reuse a single arithmetic register for both pixel coordinate and frame counter
- Self-modifying code so two pieces of logic share one instruction
- Use
RST 0/JMP $0000style for hardware initialisation by re-entering the boot loader - Pack constants into instruction operands that double as data
On the Commodore 64
The C64 answer to the question is documented in the authors’ own words. In summer 2001 an 8-bit minigame contest drew “thirty entries for the C64, Spectrum, Amstrad, and Atari 8-bit” in 512-byte and 2K classes, and C=Hacking issue 21 (February 2002) printed six postmortems under the title “The Art of the Minigame”. Its introduction sets out the discipline: “Whereas many of us are used to optimizing programs for cycle-efficiency, optimizing programs for byte-efficiency turns out to be very different”, and the framework is to “put stuff in zero-page, take advantage of default zero-page values, take advantage of the kernal, always know the register values, and reuse as much code as possible”. Zero-page instructions are two bytes instead of three; a zero-page location that the machine already leaves at zero saves the four bytes of LDA #0 / STA; and Mark Seelye’s “injections” put an STX zp into an earlier routine where X is known to be zero, instead of clearing the location where it is used.
| Author | Entry | Size | What the postmortem records |
|---|---|---|---|
| David Holz (White Flame) | Codebreaker | 415 bytes | A Mastermind game; random numbers come from reading $D012 and looping that many times before reading again; “the smallest entry” |
| Stephen L. Judd | Tetrattack! | 512 bytes | “A 3D engine in 512 bytes”: a line routine, a rotation/projection routine and object bookkeeping. One line routine serves both axes by swapping INX/INY, and the load address is chosen so the end address is $10F0, two vertex coordinates the code needs anyway |
| Per Olofsson (MagerValp) | MagerTris | 2K | A 512-byte Tetris that would not fit became a 2K entry: a 600-byte engine plus a title screen, tile set and disk high-score list, packed with pucrunch from “6500 bytes uncompressed”. The custom font is stored EOR’d against the ROM font so it compresses better |
| Mark Seelye (Burning Horizon) | Tinyrinth | 509 bytes | A maze generator and game; “I ended up getting the code down to like 475 bytes, but the version I turned in was 509 bytes” |
Olofsson’s note on when to pack is the useful rule of thumb: “at around 700 bytes or so compression starts to make sense – pucrunch breaks even around there somewhere”. Pucrunch itself is Pasi Ojala’s 1998 design, described in C=Hacking 17 as a “Hybrid LZ77 and RLE compressor” whose decompressor “uses no extra memory”; Ojala tried a Huffman back end and dropped it when “the gain was reduced to only about 3% after accounting the extra code and the Huffman tree”. Codebase64’s 2016 benchmark, run on the files of the demo Pearl for Pigs, shows the later generation — Exomizer, Subsizer, Doynamite, ByteBoozer, Bitfire — packing to 45–48% of input against pucrunch’s 54%, with depacking speed the trade: ByteBoozer 2.0 unpacks the first test file in 22 frames, Exomizer in 58 to 79.
Modern applications
Size-coding discipline appears in:
- Web performance — minified bundles, tree-shaking, server push budgets
- Embedded systems — fitting firmware in a few KB of flash
- Game asset compression — Steam / store-page download budgets
- Demakes and fan projects — fitting a modern game’s idea on retro hardware
- JavaScript 1k / 13k contests — js13kgames, Code Golf, 1k JS contest