1. Building Your Project

Technical requirements for Art Blocks generative art scripts — tokenData, PRNG, window.$features, and conversion guide.

This page covers the technical requirements for building a generative art script for Art Blocks. Your script will be stored on-chain and run in the Art Blocks Generator — a browser-based environment that injects tokenData and loads your dependency library.


Quick Start: MCP Scaffold

The fastest way to get started is with the Art Blocks MCP Server:

"Scaffold a p5.js Art Blocks project with window.$features for Palette and Density traits"

The scaffold_artblocks_project tool generates a complete index.html + art script with the PRNG, canvas setup, and feature stubs already wired up. Connect the MCP server in under 5 minutes — see the Quick Start.


What the Generator Provides

When your script runs, the Art Blocks Generator has already:

  1. Loaded your dependency library (p5.js, Three.js, etc.) as a <script> tag
  2. Injected a tokenData global with the token's hash and other metadata
  3. Applied CSS for full-viewport canvas rendering

Your script runs after all of this setup is complete.


tokenData

let tokenData = {
  hash: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
  tokenId: "42000001"
}

tokenData.hash is a 32-byte hex string — the unique, on-chain random seed for this token. This is your only randomness source.

tokenData.tokenId encodes both the project and invocation:

const projectId = Math.floor(parseInt(tokenData.tokenId) / 1_000_000)
const invocation = parseInt(tokenData.tokenId) % 1_000_000

Deterministic Randomness (PRNG)

Never use Math.random() or Date.now() as a randomness source. These are non-deterministic — the same token would produce different outputs on different loads, which breaks the fundamental guarantee of generative art on Art Blocks.

Instead, seed a pseudo-random number generator (PRNG) from tokenData.hash. The recommended implementation is the sfc32 algorithm:

// Seeded PRNG from tokenData.hash
function sfc32(a, b, c, d) {
  return function () {
    a |= 0; b |= 0; c |= 0; d |= 0;
    let t = (((a + b) | 0) + d) | 0;
    d = (d + 1) | 0;
    a = b ^ (b >>> 9);
    b = (c + (c << 3)) | 0;
    c = (c << 21) | (c >>> 11);
    c = (c + t) | 0;
    return (t >>> 0) / 4294967296;
  };
}

function hashToSeeds(hash) {
  const hex = hash.slice(2); // remove "0x"
  return [
    parseInt(hex.slice(0, 8), 16),
    parseInt(hex.slice(8, 16), 16),
    parseInt(hex.slice(16, 24), 16),
    parseInt(hex.slice(24, 32), 16),
  ];
}

const [s1, s2, s3, s4] = hashToSeeds(tokenData.hash);
const rand = sfc32(s1, s2, s3, s4);

// Usage:
// rand()           → float in [0, 1)
// rand() * (b - a) + a  → float in [a, b)
// Math.floor(rand() * n) → integer in [0, n)

Alternatively, use the full Random class that ships in the MCP scaffold, which adds convenient helper methods:

class Random {
  constructor() {
    const hex = tokenData.hash.slice(2);
    const seeds = [
      parseInt(hex.slice(0, 8), 16),
      parseInt(hex.slice(8, 16), 16),
      parseInt(hex.slice(16, 24), 16),
      parseInt(hex.slice(24, 32), 16),
    ];
    this._state = seeds;
  }
  _next() {
    let [a, b, c, d] = this._state;
    a |= 0; b |= 0; c |= 0; d |= 0;
    let t = (((a + b) | 0) + d) | 0;
    d = (d + 1) | 0;
    a = b ^ (b >>> 9);
    b = (c + (c << 3)) | 0;
    c = (c << 21) | (c >>> 11);
    c = (c + t) | 0;
    this._state = [a, b, c, d];
    return (t >>> 0) / 4294967296;
  }
  random_dec()             { return this._next(); }
  random_num(a, b)         { return a + this._next() * (b - a); }
  random_int(a, b)         { return Math.floor(a + this._next() * (b - a + 1)); }
  random_bool(p)           { return this._next() < p; }
  random_choice(arr)       { return arr[Math.floor(this._next() * arr.length)]; }
}

const R = new Random();

Token Features with window.$features

Token features (traits) are defined by assigning an object to window.$features in your art script. Art Blocks reads this value to index token traits.

// Compute your traits using the PRNG
const palette = R.random_choice(["Midnight", "Flame", "Ocean", "Forest"]);
const density = R.random_int(10, 90);
const animated = R.random_bool(0.3);

window.$features = {
  Palette: palette,
  Density: density,
  Animated: animated,
};

Rules:

  • Values must be deterministic: the same hash (and PostParam settings) must always produce the same features
  • For numeric traits, use consistent decimal precision

The legacy calculateFeatures() function approach is no longer documented here — see Legacy: Features Script if you have an existing project using that approach.


Resolution Agnosticism

Art Blocks tokens are rendered at different sizes — from thumbnails to large displays. Your script must be resolution agnostic: it should fill the viewport at any size, using relative coordinates rather than fixed pixel dimensions.

For p5.js:

function setup() {
  createCanvas(windowWidth, windowHeight);
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}

For vanilla JS / canvas:

const canvas = document.querySelector("canvas");
function resize() {
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  draw(); // redraw on resize
}
window.addEventListener("resize", resize);
resize();

Coordinate all drawing relative to canvas.width and canvas.height, not hardcoded values.


Script Requirements

Requirement Detail
Single script file One JavaScript file only
Single dependency One library (p5.js, Three.js, etc.) from the approved list
No CDN script tags The generator injects the library — do not add <script> tags
No Math.random() Use tokenData.hash-seeded PRNG for all randomness
No DOM setup No <canvas> creation — the generator provides a <canvas> element
Resolution agnostic Must render correctly at any viewport size
Deterministic Same hash → same output, always

Supported Libraries

All libraries must be from the Art Blocks Dependency Registry. Current supported libraries include:

  • p5.js — v1.0.0, v1.9.0, v1.11.11
  • Three.js — v0.124.0, v0.160.0, v0.167.0
  • regl — v2.1.0
  • Tone.js — v14.8.15
  • Paper.js — v0.12.15
  • Babylon.js — v5.0.0, v6.36.0
  • A-Frame — v1.2.0, v1.5.0
  • Vanilla JS — no dependency (canvas, WebGL, SVG)

Local Development

Set up a local index.html that mirrors the generator environment:

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.0/p5.min.js"></script>
  <script>
    let defined_hash = new URLSearchParams(window.location.search).get("hash");
    let tokenData = {
      tokenId: "0",
      hash: defined_hash || "0x" + Array.from({length: 64}, () =>
        "0123456789abcdef"[Math.floor(Math.random() * 16)]).join(""),
    };
  </script>
  <style>
    html { height: 100%; }
    body { min-height: 100%; margin: 0; padding: 0; }
    canvas { padding: 0; margin: auto; display: block; position: absolute;
             top: 0; bottom: 0; left: 0; right: 0; }
  </style>
</head>
<body>
  <canvas></canvas>
  <script src="sketch.js"></script>
</body>
</html>

Test with specific hashes by appending ?hash=0x... to the URL. Test with many different hashes to ensure variety and that no hash causes errors.


Conversion Guide: Adapting an Existing Sketch

If you have an existing p5.js or vanilla JS sketch that you want to convert to Art Blocks format:

  1. Replace Math.random() with calls to your sfc32 PRNG seeded from tokenData.hash
  2. Remove CDN <script> tags — the generator loads your library automatically
  3. Remove canvas creation code — for p5.js, remove createCanvas() calls or use windowWidth/windowHeight; for vanilla JS, select the existing <canvas> instead of creating one
  4. Make dimensions relative — replace hardcoded pixel values with percentages of width/height
  5. Add window.$features — compute and assign your token traits synchronously
  6. Verify determinism — reload the same ?hash= URL multiple times and confirm identical output

For a full step-by-step conversion walkthrough, use the MCP's scaffold_artblocks_project tool or read the Generator Specification resource available via the MCP server.


Testing Checklist

Before submitting for review:

  • Script runs without errors in Chrome, Firefox, and Safari
  • Same hash always produces identical output (deterministic)
  • Different hashes produce meaningfully different outputs
  • No hardcoded pixel dimensions — scales at all viewport sizes
  • No use of Math.random() or Date.now() for randomness
  • window.$features is assigned synchronously with correct values
  • Script is a single file with a single dependency (or no dependency)
  • No CDN <script> tags in the script
  • Tested across at least 20–40 different hashes
  • Script locks up or errors for no hash value (test edge cases)