# Writing the artwork

The viewer executes a JavaScript **function body** for each artwork instance. Do not include HTML, an import statement, a top-level module export, or your own `new p5(...)` wrapper in a p5 script.

## Contexts and renderers

- `p5js`: the supplied `p` is the p5 instance. Use `p.setup`, `p.draw`, `p.keyTyped` and other instance methods.
- `threejs`: the supplied `t` is your instance context; the current viewer supplies `THREE`. Create the scene and renderer and attach its canvas to `props.container`.
- `vanillajs`: the supplied `v` is your instance context. Create your own canvas and attach it to `props.container`.
- `glsl`: the supplied `g` is your instance context. Provide JavaScript that creates WebGL and compiles shaders; raw GLSL alone is not a complete posted script.

Do not assume the same runtime version will exist forever. Preserve the library versions used to validate your release and test the downloadable viewer. GPU output and font rasterization can differ between devices even when the inputs and trait choices agree.

## Edition inputs

Implement `context.myCustomRedrawAccordingToNewPropsHandler = props => { ... }`, substituting `p`, `t`, `v` or `g` for `context`. The callback receives:

- `transactionHash`: the full `0x`-prefixed hash of the **edition's mint transaction**, not the code-post transaction.
- `projectId`: the art project ID.
- `editionId`: the edition's number within that project, starting at 1 for a real mint.
- `tokenId`: the ERC-721 token number across the art platform. This differs from `editionId`.
- `square`: the layout divisor; a single selected edition uses 1. Numeric fields may arrive as strings.
- `container`: supplied to the three.js, vanilla and GLSL wrapper for your renderer's DOM output. p5's wrapper manages its own container.

The current generic callback does **not** supply a block hash, `prevrandao`, mint timestamp, VRF result, or an authenticated `seed` field. Do not invent those inputs. Extra project-specific data requires an explicitly supported integration.

## Initialization and redraw

For p5, create a small canvas in `setup` and use the props callback to configure the edition. Guard against missing props. Create edition-specific state when its identity changes, and handle size changes separately. Avoid registering a new keyboard listener or starting another animation loop every time props arrive.

`p.updateWithProps` is bridged to the older props callback in the current viewer, but the callback above is the portable authoring convention for this client. For a non-p5 renderer, put cleanup work on `context.cleanup`; keep requestAnimationFrame IDs in `context._animationId` and dispose GPU resources.

A useful sizing pattern is `Math.min(window.innerWidth, window.innerHeight) * 0.8 / Number(props.square)`. Use fixed logical coordinates for the composition and scale to the canvas. Changing phone orientation should not choose new traits.

Each grid cell is a separate artwork. Keep state local to the posted function body or its supplied context. Avoid assignments to `window`, replacing browser globals, or mounting unrelated elements into the surrounding app.

## Traits and metadata

Report a flat object after the edition's traits are ready:

```javascript
console.log('metadata: ', {
  transactionHash: props.transactionHash,
  Palette: 'Midnight',
  Structure: 'Orbit'
});
```

Keep `transactionHash` as the original mint transaction. Do not replace it with your derived seed. Prefer stable strings or finite numbers for traits; do not put a canvas, a cyclic object, current frame, or fluctuating performance measurements in metadata.

The current viewer groups reported traits by transaction hash. Multiple editions minted in one transaction can have distinct art when the seed includes edition ID, but that grouping can conflate their statistics. This is a viewer limitation to account for before designing a batch release.

## Deterministic identity and time

Use a local seeded stream for persistent composition and traits. `Math.random()` and `crypto.getRandomValues()` are suitable for generating preview inputs, not the permanent identity of a rendered edition. Animation may evolve over frames or time if that is the artwork's design; document its clock, initial state and replay expectations.

See the [working starter](/docs/examples), [controls](/docs/controls) and [seed guidance](/docs/randomness).

Source basis: `P5Wrapper`, `ThreeWrapper`, `Grid`, `WizardProvider` and `BlockchainProvider` in `clients/art/src/`.
