Example: On-Chain Tetris (rich media)
A complete, playable Tetris — the game code and the p5.js library live on-chain (content-addressed), and the play runs client-side over an on-chain poster floor. It's a real example of ChainRender's rich-media capability, and an honest one about where the trust boundary sits.
What "on-chain" means here (honest)
ChainRender is deliberately declarative and analyzable — there is no arbitrary-code escape hatch, which is what makes trust-grading possible. A real-time arcade game (60fps, keyboard input, mutable state) inherently runs client-side. So Tetris is built as a rich-media enhancement over an on-chain floor:
- On-chain: the p5.js library (in the
DependencyRegistry, by content hash) + the Tetris sketch (as aSourceStoreentry) + the seed. Nothing is fetched from a CDN or server at play time in production. - Client-side: the actual play (the game loop, input, rendering) runs in the viewer, over an on-chain poster floor that shows "Tetris" on any client, even without p5.
- Grade ⚙️ (enhancement over a Grade-A floor) — not "fully on-chain trustless." The code is on-chain; the play is a client enhancement. That distinction is stated honestly, per ChainRender's core value that marketing never exceeds what the code guarantees.
If you wanted a trustless Tetris (Grade A), that's a different design: move-by-move, each move a transaction, the board validated in a contract — turn-based, not arcade. This example is the playable one.
Determinism from the seed
The piece sequence derives from the on-chain seed (a bytes32) via a seeded PRNG — so the same seed always
produces the same game. Two players with the same seed get the same pieces (useful for fair competitions or
verifiable runs).
How it's wired
It's a standard ChainRender p5 scene binding:
import { encodeSceneBinding } from "@chainrender/client";
const tetris = encodeSceneBinding({
kind: "p5",
libHash: "0x…", // DependencyRegistry content hash of the on-chain p5 library
seed: "0x…", // bytes32 — the deterministic piece sequence
entry: "tetris", // SourceStore id of the on-chain sketch (or inlined source)
});
// bind it to an image node with a poster floor; the viewer loads the lib + sketch and runs it, fail-closed.
The viewer reads the binding, loads the library and sketch from chain, and runs the sketch sandboxed over the poster floor — failing closed to the poster if p5 can't run.
Controls
← → move · ↑ rotate · ↓ soft drop · space hard drop · P pause · R restart.
Why this is a good example
It demonstrates the honest rich-media pattern end to end: real, fun, interactive content, stored on-chain, presented through ChainRender, with the trust boundary stated plainly (⚙️ enhancement over an on-chain floor). It's the template for any p5/three interactive piece — a generative-art minter, a physics toy, a data visualization you can drag.