A foggy grey stone mountain rising above dark grass terraces under a pale blue sky

4085 BWebGPU2026

Minecraft 4k

Notch's 2009 Minecraft 4k, remade as one HTML page that uses 4085 of 4096 bytes.

The whole game, byte by byte

Each square is one byte of index.html, read live from the page you play.

4085 of 4096 bytes

decoder
-
compressed game
-
free
-

Then and now

Notch's entry for the Java 4K contest, released in December 2009, drew a small world with a CPU raycaster. This one hands both the world and the rays to the GPU.

2009 Java 4KThis
Size2581 B Java applet4085 B HTML
World64 x 64 x 631024 x 1024 x 128 (128 MiB)
RendererCPU raycaster at about 214 x 120WebGPU compute shader at full window resolution
GenerationCPU at loadcompute shader at load
Placeablegrass16: 3 plants, 10 blocks, 3 ores
Lightingface shadingambient occlusion, sun shadows, day and night

Controls

Click the page first to lock the pointer.

W A S D
Walk. Physical keys, so Z Q S D on AZERTY.
Left Shift
Sprint
Space
Jump, or swim up
Mouse
Look
Left click
Break
Right click
Place
1 - 9 0
Select a block: grass, dirt, stone, sand, log, leaves, planks, bricks, snow, glass
Wheel
Cycle every placeable block, plants and ores included

How it fits

The page is a decoder and a deflate stream. The decoder is all the page can afford, so everything else is squeezed before it gets there.

  1. Source

    TypeScript for the game and WGSL for the shader. World constants are shared by name.

  2. Shrink

    An in-house minifier rewrites the AST and picks every name by deflate size, the shader's names too.

  3. Deflate

    An in-house encoder searches the bit stream for one without the 10 bytes the parser or l9 would change, against zopfli and libdeflate.

  4. Page

    charset=l9 reads each byte as one character. The stream sits raw in <canvas><xmp>.

  5. Boot

    The browser inflates it with DecompressionStream('deflate-raw') and evals the result.

<meta charset=l9><body onload='new Response(new Response(Uint8Array.from(c.textContent,c=>c.charCodeAt())).body.pipeThrough(new DecompressionStream("deflate-raw"))).text().then(eval)'><canvas id=c><xmp>

Then the raw deflate stream. No decompressor, no zlib header.

Requirements

Needs WebGPU with immediates, a keyboard and a mouse. Today that means Chrome 150+ on Windows.

BrowserRunsWhy
Chrome 150+ on Windowsyestested on Chrome 155
Edge 150+, Chrome on Linux, ChromeOSshoulduntested, same engine
Chrome on macOSnothe canvas can't be rgba8unorm there yet
Firefox 158nono immediates, tested: everything else works
Safari 26nono immediates
Phones, tabletsnothe controls need a keyboard and a mouse

Immediates are the tiny per-dispatch uniforms set with setImmediates and read as var<immediate> in WGSL. They save the bytes of a uniform buffer.