# Start with a working sketch

Use these for a **new project**. Changing an existing artwork's seed recipe changes its rendered editions, so this is not an instruction to patch released artwork.

## A complete p5 starter

[Download the complete script](/docs/examples/p5-starter.js), paste it into the wizard's script editor, choose `p5js`, and use Preview in UI. The file includes the seed helper; it has no package imports or remote asset requirements.

It demonstrates a stable composition, a separate noise seed, fixed logical coordinates, resize handling, flat metadata, an Animate toggle and an explicit PNG export. The complete script is also included in the [LLM reference](/llms-full.txt).

```javascript
// Immutables.art edition seed convention v1 — for NEW projects.
// Deterministic mixing, not cryptographic randomness or bot prevention.
// Keep props.transactionHash unchanged for metadata and provenance.
function immutablesSeed(props, stream = 'art') {
  const hash = String(props.transactionHash || '').toLowerCase();
  if (!/^0x[0-9a-f]{64}$/.test(hash)) throw new Error('A full mint transaction hash is required');
  const decimal = (value, name, allowZero) => {
    if (typeof value === 'number' && !Number.isSafeInteger(value)) throw new Error(name + ' must be a safe integer or decimal string');
    const text = String(value);
    if (!/^\d+$/.test(text)) throw new Error(name + ' is required');
    const normalized = text.replace(/^0+(?=\d)/, '');
    if (!allowZero && normalized === '0') throw new Error(name + ' must be positive');
    return normalized;
  };
  const input = ['immutables.art/edition-seed/v1', hash,
    decimal(props.projectId, 'projectId', true), decimal(props.editionId, 'editionId', false), stream].join('|');
  let hex = '0x';
  for (let word = 0; word < 8; word++) {
    let state = (2166136261 ^ Math.imul(word + 1, 0x9e3779b9)) >>> 0;
    for (let i = 0; i < input.length; i++) state = Math.imul(state ^ input.charCodeAt(i), 16777619) >>> 0;
    state = Math.imul(state ^ (state >>> 16), 0x85ebca6b);
    state = Math.imul(state ^ (state >>> 13), 0xc2b2ae35);
    hex += ((state ^ (state >>> 16)) >>> 0).toString(16).padStart(8, '0');
  }
  return hex;
}

// A local drawing stream. Do not replace Math.random globally.
function immutablesRandom(props, stream = 'art') {
  let state = parseInt(immutablesSeed(props, stream).slice(2, 10), 16) >>> 0;
  return () => {
    state = (state + 0x6d2b79f5) >>> 0;
    let value = Math.imul(state ^ (state >>> 15), state | 1);
    value ^= value + Math.imul(value ^ (value >>> 7), value | 61);
    return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
  };
}

// Include edition-seed.js above this body. MIT; see examples/LICENSE.txt.
let edition = null;
let composition = [];
let motion = true;
let frame = 0;

p.setup = () => { p.createCanvas(1, 1); p.noLoop(); };

function fitCanvas() {
  if (!edition) return;
  const size = Math.max(1, Math.floor(Math.min(window.innerWidth, window.innerHeight) * 0.8 / Number(edition.square)));
  p.resizeCanvas(size, size);
}

p.myCustomRedrawAccordingToNewPropsHandler = props => {
  if (!props || !props.transactionHash || !props.editionId || !props.square) return;
  const identity = immutablesSeed(props);
  if (!edition || edition.identity !== identity) {
    p.randomSeed(parseInt(identity.slice(2, 10), 16));
    p.noiseSeed(parseInt(immutablesSeed(props, 'noise').slice(2, 10), 16));
    composition = Array.from({ length: 18 }, () => ({ x: p.random(40, 360), y: p.random(40, 360), radius: p.random(8, 35) }));
    frame = 0;
    console.log('metadata: ', { transactionHash: props.transactionHash, Structure: 'Orbits', Circles: composition.length });
  }
  edition = { ...props, identity };
  fitCanvas();
  motion ? p.loop() : p.redraw();
};

p.draw = () => {
  if (!edition) return;
  p.background(0);
  p.push();
  p.scale(p.width / 400);
  p.noFill();
  p.stroke(255);
  p.strokeWeight(1.5);
  for (const circle of composition) {
    p.circle(circle.x, circle.y, circle.radius * 2 + Math.sin(frame / 45 + circle.x) * 6);
  }
  p.pop();
  if (motion) frame++;
};
p.windowResized = () => { fitCanvas(); if (!motion) p.redraw(); };
function toggleMotion() { motion = !motion; motion ? p.loop() : p.noLoop(); }
p.keyTyped = () => { if (p.key === 'A' || p.key === 'a') toggleMotion(); };
p.immutablesControls = {
  version: 1,
  actions: [
    { id: 'motion', label: 'Animate', type: 'toggle', key: 'A' },
    { id: 'png', label: 'Save PNG', type: 'export' }
  ],
  getState: () => ({ values: { motion } }),
  onAction: id => { if (id === 'png' && edition) p.saveCanvas('orbits-' + edition.editionId, 'png'); }
};

```

## Seed helper by itself

[Download edition-seed.js](/docs/examples/edition-seed.js) if you already have a drawing. Copy its functions into the posted source; do not add a runtime fetch dependency on the documentation website.

Use `immutablesRandom(props, 'composition')` for a local `random()` function in vanilla or three.js artwork. For p5, initialize `p.randomSeed` from `immutablesSeed(props)` and use a separate domain for noise. See [seed limitations](/docs/randomness).

## Built-in wizard studies

The wizard contains four renderer boilerplates and 16 studies across p5.js, three.js, vanilla canvas and GLSL. Their current seed paths mix the full transaction hash and full project/edition IDs. The particle example uses a local seeded stream rather than `Math.random()` for its persistent structure.

Examples based on existing projects are **adapted studies**. Their seed behavior is intentionally different from the historical releases. Preserve credits and check the applicable license before reusing artwork code. The generic starter is available under the [MIT license](/docs/examples/LICENSE.txt).

The wizard only replaces the current editor after the example-load confirmation. Saved drafts and already-posted scripts are not automatically migrated. To adopt a new recipe in your own unreleased draft, explicitly replace its seed initialization and retest the work.

## What a good preview demonstrates

Compare different hashes, then reuse one hash with different edition IDs. Reload a fixed input, resize the viewport, and compare both traits and a fixed animation moment. Preview in a grid and as a selected edition. Test native mobile typing, exports and control styling in the full UI.
