Not a link to the game. The game.
A whole game,
held inside a chain.
Every byte of the engine and its data is deployed as contract code on an EVM chain. No server holds it. Your browser reads it out of chain state, checks it against a commitment the chain itself can verify, and runs it.
Pull it off the chain
Nothing here is cached and nothing is served from this page. Every byte below arrives from an RPC node and is checked before it is run.
waiting.
The chain checks its own copy
This is the part an equivalent on Solana cannot do. There, a program cannot read transaction data — the bytes sit in the ledger and an RPC hands them back, so the verification lives in the page and you are trusting a webpage again.
Here the bytes are contract code. The ROM contract reads its
own chunks with EXTCODECOPY, hashes them, and walks the merkle
tree to its committed root. Ask it about any chunk and the answer comes from
the EVM, not from JavaScript.
verify(index, proof) — evaluated on chain.
What the loader refuses
- An unsealed ROM. Before sealing, the owner can still append chunks and set any root, so matching the root would prove only that the publisher was self-consistent when you looked.
- A wrong, swapped, missing or duplicated chunk. The tree is rebuilt from what actually arrived and compared with the commitment.
- A payload that inflates cleanly but is the wrong game. Both the compressed and the decompressed forms are committed to, because a valid gzip stream is not evidence of anything.
- A malformed bundle. The root proves the bytes were published; it says nothing about whether they are well formed.
How a game fits in a chain
-
Bundle
The engine and its data files go into one container, then gzip once over the whole thing.
-
Chunk
Cut into 24,575-byte pieces — EIP-170 caps deployed code at 24,576 and one byte is the STOP prefix that keeps the data from being executable.
-
Commit
A keccak256 merkle tree over the chunk hashes. keccak rather than SHA-256 because the EVM computes it natively, so a contract can check a proof for a few thousand gas.
-
Deploy
Each chunk becomes its own contract. The ROM records the pointers, then seals — one way, so the root becomes a commitment rather than a label.
-
Read
The browser fetches each pointer's code, rebuilds the tree, compares the root, inflates with the platform's own gzip, and compiles the WebAssembly — in that order. Never compile first.
What it costs
| Payload | Size | As calldata | As contract code | Contracts |
|---|
Calldata is the direct equivalent of writing bytes into transactions: permanent and retrievable, but invisible to contracts. Contract code costs more and is readable by the chain. Mainnet figures include the EIP-7623 data floor, which raises a data-heavy transaction to 40 gas per byte rather than 16 — costing this workload at 16 understates mainnet by two and a half times. Blobs are excluded on purpose: clients drop them after about eighteen days, so they are not storage.
Honest status
Working
- Packer — bundle, gzip, chunk, keccak merkle
- ROM contract — code storage and on-chain proof verification
- Publisher — plan, deploy, verify each chunk, seal
- Raw EIP-1559 signing, no wallet library
- Loader and boot path, browser and Node
- DEPTH — an original raycaster, engine and all, on chain
- 207 tests, including a full publish-and-run against a real EVM
Not yet
- A second ROM, to prove the reader is not special-cased to one
- Sound — the engine is silent by choice, not by limitation
- More levels than the one
What ships here is DEPTH, written from scratch for this: the raycasting renderer, the wall textures, the level and the creatures are all defined in code, and there are no asset files at all — the textures are arithmetic and the map is a string literal. That was the point. Bytes on a chain are permanent and cannot be taken down, so a payload has to be one nobody can later object to, and the surest way to get one is to write it.