Silas Beckett, On-Chain Critic & Market Columnist
June 29, 2026 · 10 min read
Run on-chain code of generative NFTs locally to test permanence
Floor prices swing, Discord mods whisper "LFG," and collectors mint six-figure jpegs into wallets they barely control — yet almost nobody stops to verify whether the art they own can actually exist without someone else's server running it.

Your NFT Is Not On-Chain. Here's How to Prove It Is.
Here's the signal hiding inside all that noise: roughly 90% of NFT collections rely on external hosting — IPFS, Arweave, or worse, a plain AWS bucket — for their visual assets. On-chain generative art, the kind pioneered by Art Blocks in 2020, is the exception. The code that renders the artwork lives inside the Ethereum contract itself. But "lives inside" is a claim, not a fact. Until you extract that code, run it locally, and reproduce the exact output tied to your token ID, you're taking the platform's word for it. And in this market, taking anyone's word for it is capitulation dressed up as trust.
You didn't buy a JPEG. You bought a promise of permanence. The only way to honor that promise is to break it open yourself.
This is the practical, no-BS guide to doing exactly that — decoding the on-chain data, setting up a local rendering environment, and proving your generative NFT will survive the collapse of every intermediary between you and the blockchain.
Decoding the tokenURI: Where the Raw Code Actually Lives
Every ERC-721 contract exposes a function called tokenURI. When you call it with your token ID, it returns one of two things: either a URL pointing to an external JSON file, or a base64-encoded string containing the entire metadata blob, including the artwork's source code.
For on-chain projects — think Art Blocks' Chromie Squiggles, Fidenza, or newer generators like QQL — that returned string is a goldmine. It's a data:application/json;base64,... payload. Decode the base64 portion and you get a JSON object. Inside it, look for fields like animation_url or image. For generative art, animation_url typically holds an HTML document or a reference to the script that drives the visual output.
Here's what the extraction process looks like in practice:
- Navigate to the contract on Etherscan and locate the "Read Contract" tab.
- Call
tokenURIwith your specific token ID. - Copy the returned base64 string and decode it using any standard decoder — browser console, CyberChef, or a simple Python one-liner.
- Parse the resulting JSON. For on-chain generative pieces, the
animation_urlfield will often start withdata:text/html;base64,...— that's your rendering engine, living right there on-chain.
The critical nuance: some Art Blocks contracts store the generative script in a dedicated Script field accessible directly from the contract's read functions, bypassing the tokenURI entirely. This is cleaner, more transparent, and exactly the kind of provenance signal that separates museum-grade on-chain art from "trust me bro" metadata hosted on someone's VPS.
A subtle but important point: the tokenURI return value itself can be static or dynamic. Some contracts return a pointer to an immutable JSON blob baked into the contract bytecode at deployment time. Others compute the URI on the fly, pulling from storage variables that were set during the mint transaction. Both approaches preserve the code — but the static variant is inherently more auditable, because what you see on Etherscan is the same raw bytes that were deployed, with no runtime logic layered on top.
If you can't extract the code from the contract without touching a centralized server, you don't own on-chain art. You own a receipt.
The PRNG Seed: Why Your Hash Is Your Artwork
Generative code alone doesn't produce your NFT. It produces every NFT in the collection — thousands of possible outputs, all from the same algorithm. What makes your piece unique is the pseudo-random number generator seed, often stored as a hash associated with your token ID at mint time.
This seed is the DNA. The generative code is the organism. Same code, different seed, completely different visual output. When you run Art Blocks code locally, you need to feed it the exact seed tied to your token ID, or you'll get a beautiful, valid rendering — of someone else's piece.
Retrieving the seed depends on the contract architecture:
| Collection Type | Seed Storage | How to Retrieve |
|---|---|---|
| Art Blocks (legacy) | Hash generated at mint, stored in contract state | Call tokenIdToHash(tokenId) on the contract |
| Art Blocks (engine) | Token-specific hash in dedicated hash storage | Same read function, different contract address |
| Custom on-chain generators | Varies — often in token metadata or a seed field | Decode tokenURI or check contract's custom read methods |
The hash itself is generated from a combination of the minter's address, the block hash at the time of minting, and a nonce — making it effectively unpredictable at the moment of mint, but fully reproducible afterward since all inputs are permanently recorded on-chain. This is what guarantees deterministic output: the same hash fed into the same code will always produce the same visual. Not "probably the same." Exactly the same, pixel for pixel, frame for frame.
Once you have the hash and the extracted source code, you have everything needed to reproduce the artwork with mathematical certainty. No OpenSea, no Art Blocks frontend, no cloud rendering — just your machine, your code, your seed.
Setting Up a Local Environment: p5.js, HTML Boilerplate, and the Render Loop
Most on-chain generative art is written in JavaScript, overwhelmingly using the p5.js library — a creative coding framework that handles canvas drawing, color math, and animation loops. When you extract the script from the tokenURI's animation_url field, you'll typically find a self-contained HTML document with embedded <script> tags.
If the HTML is complete, you can save it as a .html file and open it directly in a browser. That's the simplest path, and for well-constructed Art Blocks pieces, it just works. Your browser becomes the rendering engine. No build tools, no npm, no node.
But reality is messier. Here's what you'll actually encounter:
- Embedded p5.js: The code includes p5.js inline or references a CDN URL. If it's a CDN link, the page will render only while you're online, which defeats the purpose. Fix: download p5.js locally and replace the
<script src="...">tag with a path to your local copy. - Instance mode: Art Blocks scripts typically use p5.js "instance mode" — the sketch is wrapped in a
new p5(sketch, containerElement)call rather than relying on global functions. This matters because it means the code is modular and can be injected into any HTML container. - External font or asset dependencies: Some pieces load Google Fonts or external assets. If the CDN goes down, the rendering degrades. For true permanence verification, download the font files and embed them as base64 in the CSS.
- WebGL renderers: A subset of generative pieces — particularly those exploring 3D forms or shader-based effects — rely on
p5.js's WebGL mode or raw Three.js. These introduce GPU-dependent rendering paths that can produce subtly different outputs across hardware. Permanence testing on these pieces should include at least two machines with different GPU vendors.
The verification workflow is straightforward:
1. Decode the base64 HTML from animation_url.
2. Save it locally as piece_[tokenId].html.
3. Replace any external CDN references (p5.js, fonts) with local copies.
4. Open in a browser. Compare the output against the known rendering on Art Blocks' site or a trusted indexer.
5. Toggle the hash in the code to confirm that a different seed produces a different output — proving the seed actually drives the PRNG and the rendering is deterministic.
If the output matches, the on-chain claim holds. If it doesn't, you've just discovered a permanence failure — and that's more valuable than any Discord verification.
External Library Risk: The Dependency Time Bomb
Here's the uncomfortable reality that most collectors and even some developers gloss over: on-chain code is only as permanent as its dependencies.
A generative art script that references p5.js version 1.4.0 via a CDN URL is not immutable. It's a ticking clock. If that CDN changes its URL structure, if the library authors push a breaking update that gets cached at the same URL, or if the CDN simply disappears — the rendering changes or breaks entirely. Your "on-chain" art just became a broken promise.
This isn't theoretical. Libraries evolve. APIs deprecate. WebGL implementations shift across browser versions. The code on the blockchain is frozen, but the ecosystem around it is not.
The mitigation is ownership of the full dependency tree:
- Self-host p5.js: Download the exact version used at mint time. Store it locally or pin it to IPFS under your own control.
- Freeze font dependencies: If the code loads
InterorSpace Monofrom Google Fonts, grab the.woff2files and base64-encode them into the stylesheet. - Pin shader libraries: Pieces using GLSL shaders sometimes pull utility functions from external repos. Clone the specific commit referenced by the artist.
- Test across browsers: A piece that renders perfectly in Chrome 120 might behave differently in Firefox 115 due to canvas API quirks. Cross-browser testing is part of permanence validation, not an afterthought.
The principle is the same one that governs any serious audit of digital assets: the code is the claim, and the dependencies are the reserves backing that claim. If you haven't verified both, you're operating on trust — and trust is the one resource that deprecates faster than any JavaScript library.
The code on the blockchain is frozen. Everything around it is not. Own your dependencies or accept that your "permanence" has an expiration date.
Beyond the Platform: Why Local Execution Is the Only Honest Audit
Art Blocks has done more for on-chain permanence than any other project in the space. Their contract architecture, their commitment to storing scripts on Ethereum, their public documentation of the hash mechanism — it's genuinely good engineering. But engineering isn't immunity.
Platforms sunset. Frontends get redesigned. API endpoints change. The Art Blocks website you visit today to view your Fidenza is a convenience layer, not a guarantee. If Art Blocks disappeared tomorrow — not a prediction, just a stress test — your on-chain generative art should survive, pixel for pixel, because the code and the seed are on Ethereum.
Local execution is the audit that proves this. It's the difference between believing your art is permanent and knowing it. And in a market where cultural premium is built almost entirely on provenance claims, knowing is everything.
The process isn't glamorous. There's no Discord role for "I verified my hash renders correctly in a local HTML file." No Twitter thread goes viral for decoding a base64 string. But this is what on-chain maximalism actually looks like — not buying the narrative, but running the code.
There's also a second-order benefit that rarely gets discussed: when you run the code locally, you start to understand it. You see how the artist structured the randomness, what parameters the seed influences, how color palettes shift across different hashes. This isn't just verification — it's intimacy with the work. The collectors who do this work carry a fundamentally different relationship to their pieces than those who never look past the OpenSea thumbnail. They know which functions control density, which branches produce the rarest forms, which noise algorithms create the textures they love. That knowledge compounds.
The floor price reflects consensus. The local render reflects truth. Consensus can capitulate overnight. Truth doesn't.
If you hold generative art and you've never pulled the source from Etherscan, never decoded the tokenURI, never fed the hash into a local p5.js sketch and watched it render — you're holding on faith. Faith is expensive in this market. Verification is free. Run the code.