DOOMon Solana
The whole 1993 game, byte for byte, written into 588 Solana transactions. No server holds it. Your browser pulls it off the ledger and runs it. Right here.
Arrows move · Ctrl fires · Space opens doors · Alt + arrows strafe · Enter picks · Esc menu
One transaction. The Slayer and the riff.
A single Solana transaction of 3,994 bytes carries a picture of the Doom Slayer and the first four seconds of At Doom's Gate. Your browser reads it off the chain, splits the bytes and plays them.
Doomguy contract
Not a link to DOOM. DOOM.
Most "on-chain" games store a URL. This stores the game. Every byte of the engine and the shareware levels sits inside the data field of a Solana transaction, forever, on every full node in the world.
Index
11 transactions
60 signatures each
Game data
577 transactions
3,895 bytes each
4,096
bytes per transaction since Solana's v1 format went live on mainnet on September 15, 2026. Before that: 1,232. DOOM would have needed 1,850 transactions. Now it needs 588.
127.34 SOL
invested to put DOOM on chain. Storage in transaction data has no rent. It never expires.
0 servers
between you and the game. The page asks an RPC node for the transactions, checks the SHA-256 against the root, inflates the gzip, and boots the WebAssembly. Even the boot script comes from the chain.
Verify it yourself in 20 lines
// node ≥ 20 — reads the root tx, then every chunk, and rebuilds doom.wasm.gz
const RPC = 'https://api.mainnet-beta.solana.com';
const ROOT = '2qQDvkVaxbF84o25N1gH1o87T19ZNiD6wbHKhQwYDQmBi9qoaCVMpcFL4ey7m3i9Xm9PcdE5HhFmeSxZusGevPsc';
const b58 = s => { const A='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; let n=0n; for (const c of s) n=n*58n+BigInt(A.indexOf(c)); const out=[]; while(n>0n){out.push(Number(n&255n)); n>>=8n;} for (const c of s){ if(c!=='1')break; out.push(0);} return Uint8Array.from(out.reverse()); };
const tx = async sig => b58((await (await fetch(RPC,{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({jsonrpc:'2.0',id:1,method:'getTransaction',params:[sig,{maxSupportedTransactionVersion:1}]})})).json()).result.transaction.message.instructions[0].data);
const root = JSON.parse(new TextDecoder().decode((await tx(ROOT)).subarray(5)));
const asset = root.assets.find(a => a.name === 'doom.wasm.gz');
const out = new Uint8Array(asset.size);
for (const pageSig of asset.pages) {
const p = await tx(pageSig); const n = new DataView(p.buffer).getUint32(11, true);
for (let k = 0; k < n; k++) {
const sig = /* base58 of */ p.subarray(15 + k*64, 15 + (k+1)*64);
const c = await tx(encodeBase58(sig)); const i = new DataView(c.buffer).getUint32(7, true);
out.set(c.subarray(11), i * asset.chunkSize);
}
}
// sha256(out) === asset.sha256 → gunzip(out) is DOOM