RIGGLES CREATOR GUIDE

# Creator guide: the Riggles Auction House and the onchain launchpad

This is the reference for artists and developers making art for both Riggles
products. Sections 1 to 10 cover the Auction House, where the tokens of your
collection are sold at auction. Sections 11 to 14 cover the onchain launchpad,
where you deploy a collection that collectors mint from: a layered collection
built from trait images, or a code art collection.

Sections 1 to 10 describe exactly what the loader runs, what it gives your
code, what it takes away, and what happens between the studio and onchain.
Every claim there matches the source of loader 2.2, the loader new art runs on
(`shared/launchpad/art/loader-v2.2-source.mjs`). Where
the loader spec (`LOADER-V2-SPEC.md`) says something the source does not do, this
guide follows the source, and the difference is listed at the end of that document
for the team, not here. Sections 11 to 14 match the code the Studio checks and
packs a collection with (`shared/nft-launchpad/art/` for layered art,
`shared/nft-launchpad/code/` for code art) and the contracts it deploys.

## 1. What the platform runs

You choose your art's format in the Studio's Art editor: **p5**, **Canvas**,
**SVG** or **PNG**. p5 is Format A below and runs on the pinned p5. Canvas and
SVG are Format B: plain JavaScript with no library, which draws on a canvas or
builds an svg element (the loader is not given p5 for them, so a `setup()`
function there is never called). PNG is not code: a series of images you
upload, one per token (section 10). Every token's attributes start with its format.

**Format A** is an ordinary p5 sketch in global mode, the kind the p5 web editor
and OpenProcessing run: a `setup()` function, an optional `draw()`, and whatever
p5 event functions you want. **Format B** is a self-contained Canvas2D script,
in the Art Blocks style: it reads `tokenData.hash`, creates its own canvas, runs
its own `requestAnimationFrame` loop, and attaches its own input listeners. The
platform's own demo piece, `constellation.js`, is Format B.

Detection happens after your script loads. If `window.setup` is a function, the
loader treats your piece as Format A and constructs a `p5()` instance itself,
which is what binds `setup`, `draw`, and the p5 event functions to the running
sketch. If there is no `window.setup`, the loader assumes Format B and only
observes: it does not construct anything, it waits for your code to make its own
canvas and start its own loop. If, after the poster budget of frames has run,
there is neither a `setup` function nor any `<canvas>` element in the document,
the loader fails at phase `load` with:

```
Riggles art needs a p5 setup() function, a canvas or an svg in the document.
```

p5 is pinned at version 1.11.11. That exact build is bundled with every
collection and its hash is committed in the manifest as `runtimeHash`. You
cannot ask for a different p5 version or for p5 2.x.

Every token gets exactly one seed: a bytes32 value, `riggles.seed`, unique to
that token. All the randomness your code uses, deliberately or through p5's own
`random()`/`noise()`, is derived from that one seed. A token's seed is committed
by the first bid it receives, under the Riggles Auction House's Riggle Auction
format: a 15 second opening window, where each of the next three bids extends
the clock by 5 seconds, and then the clock runs to zero. Before that first bid,
the token has no committed seed; the studio gives you a scratch seed to iterate
against instead.

## 2. Format A: an ordinary p5 sketch

A minimal Format A piece:

```js
function setup() {
  createCanvas(1000, 1000);
  background(riggles.random() * 255);
}
function draw() {
  circle(mouseX, mouseY, 10 + 5 * sin(frameCount / 10));
}
function mousePressed() { background(0); }
```

Rules the loader enforces:

- `setup()` is required. `draw()` is optional; without one, your piece is static
  and the loader never enters a live animation loop for it (see `static.js`).
- `createCanvas(w, h)` decides the collection's size. There is no width or
  height field to fill in anywhere: the loader reads your canvas's actual size
  right after `setup` runs and reports it back, and registration writes those
  numbers into the manifest. Anything in the manifest's width and height fields
  before that is informational only. It is never enforced against the canvas you
  actually create.
- Every p5 event callback works as it does in the p5 editor: `mousePressed`,
  `mouseMoved`, `mouseDragged`, `keyPressed`, `keyTyped`, `touchStarted`,
  `touchMoved`, `touchEnded`, and the rest. `windowResized` does not fire. Your
  frame is a fixed size; nothing resizes it, so there is nothing to respond to.
- `loop()`, `noLoop()`, `redraw()`, and `frameRate()` behave exactly as they do
  in p5. The loader never overrides or fights p5's own timing. A piece that
  calls `noLoop()` in `setup` and then `redraw()` from a `mousePressed` handler
  works the same way it would in the p5 editor (`nolooped.js`).
- Async `setup()` is not supported. p5 1.x does not await it either, and the
  loader calls your `setup` synchronously and moves on the moment it returns.
  If your `setup` starts an async operation, the poster is captured before that
  operation resolves.
- Traits are declared in your code with `riggles.pick` and `riggles.range`.
  Section 6 covers exactly how.

## 3. Format B: a canvas script

A Format B script gets no help from p5 and builds everything itself. The
platform hands it `window.tokenData`:

```js
var tokenData = window.tokenData || { hash: '0x' + '00'.repeat(32), tokenId: '0' };
```

`tokenData.hash` is the token's seed as lowercase hex, and `tokenData.tokenId`
is the decimal token id as a string (`'0'` in the studio, since no real token
exists yet there). Your script makes its own canvas, appends it to
`document.body`, and drives it with your own loop:

```js
const canvas = document.createElement('canvas');
canvas.width = 1000;
canvas.height = 1000;
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');

function tick() {
  draw(ctx);
  requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
```

You also wire your own input listeners on your own canvas or on `window`, the
way the `constellation.js` demo does for pointer, wheel, touch and keyboard
events. The loader does not attach anything for you in Format B; it only
watches the `requestAnimationFrame` queue you feed and the canvas you create.

`riggles.poster` (true only during the poster phase) and `riggles.posterReady()`
exist so a Format B script can tell the loader "the poster is on the canvas
now, capture it," instead of waiting for a fixed frame count. Setting
`window.__RENDER_DONE__ = true` does exactly the same thing, for compatibility
with pieces ported from other Art Blocks-style harnesses that already set that
flag when they finish their intro. The loader checks both `riggles.posterReady()`
and `window.__RENDER_DONE__ === true` on every logical frame during the poster
loop and stops at whichever fires first.

## 4. The seed and randomness

`riggles.seed` is the token's seed, lowercase hex, frozen before your code ever
runs. `riggles.random()` is a full xoshiro256** generator seeded from that value
under the domain `render`, the same generator v1 art used. Call it as many
times as you like; each call advances the stream.

If you write Format A, p5's own `randomSeed()` and `noiseSeed()` are also called
for you, once, before your `setup` runs, seeded from the same `render` domain.
That means p5's own `random()` and `noise()` are deterministic for a given
token too, without you calling `randomSeed` yourself. They are a separate
stream from `riggles.random()`, seeded from the same value but advancing
independently, so calling one does not change what the other returns next.

`Math.random()` is also replaced, seeded from a different domain (`setup`), so
it is deterministic as well rather than throwing or returning platform noise.
A sketch that calls `Math.random()` inside `draw` gets the same sequence on
every run of the same seed (`seeded.js` exercises exactly this).

None of this reseeds per frame. The seed sets your sketch's initial
conditions once, before `setup`, and your sketch evolves from there exactly the
way an ordinary p5 sketch with a fixed seed would. An earlier loader reseeded
the random stream on every drawn frame, which meant an animated piece could
never accumulate genuine randomness over time; v2 does not do that.

The practical result: the same seed always produces the same poster, pixel for
pixel, whether it runs in the studio, on the collection page, or on the
server's renderer. A different seed produces a different poster.

In the studio, before a token's seed is committed onchain, you get three tools
for testing this: **Run**, which re-executes your current code against the
seed already in hand and reproduces the same result; **New variation**, which
draws a fresh platform seed and shows you a different one; and a seed field,
where you can paste a specific seed, including a deployed token's, to reproduce
it exactly. This replay only exists for testing before mainnet. Once a token's
first bid commits its seed onchain, there is nothing left to replay: that token
renders once, deterministically, forever.

## 5. The poster

The poster is the still image the platform captures from your sketch: the
picture marketplaces, thumbnails, and the collection grid show. Every run,
whether in the studio, on the collection page, or on the server, produces a
poster the same way and the same deterministic value for a given seed.

The poster is taken on a logical clock, not the wall clock. The loader replaces
`requestAnimationFrame`, `performance.now()`, and `Date.now()` with a clock that
advances exactly 1000/60 milliseconds per frame and runs your queued animation
callbacks one logical frame at a time, with no input events delivered. It does
this for `posterFrame` frames, or until you call `riggles.posterReady()` (or
set `window.__RENDER_DONE__ = true`), whichever happens first.

`posterFrame` defaults to `0`. For a static piece, or one whose first frame is
already the finished image, the default is correct: the poster is captured
after `setup` and the first `draw`, if present, at logical time zero. Calling
`riggles.posterReady()` in setup captures that completed canvas immediately. If your piece has an intro animation, a settle-in
period, or otherwise needs several frames before it looks right, set
`posterFrame` higher in your draft, or call `riggles.posterReady()` (Format B)
the moment your piece settles rather than waiting on a fixed count. The
platform's demo piece needs one of the two: its intro takes 240 logical frames
to finish placing stars before the scene is worth capturing.

After the poster is reported, the loader hands your sketch the real clock back:
real `requestAnimationFrame` timing, real `performance.now()`, real `Date`, and
real mouse, keyboard, and touch events. `riggles.phase` flips from `'poster'`
to `'live'` at that moment. A piece that wants to stay completely still after
its poster simply has no `draw()`, or calls `noLoop()` in `setup`; nothing forces
a live phase on a static piece.

## 6. Traits

Traits are declared inside your code with two helpers on the `riggles` global.
Each one returns this token's value, and your sketch draws with that value. An
ordinary p5 sketch with two traits:

```js
let palette, stars;

function setup() {
  createCanvas(1000, 1000);
  noStroke();
  noLoop();
  palette = riggles.pick('Palette', { Dusk: 3, Ember: 1, Sea: 2 });
  stars = riggles.range('Stars', 12, 128);
}

function draw() {
  background({ Dusk: '#1b1f3a', Ember: '#3a1414', Sea: '#0f2e33' }[palette]);
  fill(255, 230, 160);
  for (let i = 0; i < stars; i++) {
    circle(random(width), random(height), random(2, 8));
  }
}
```

**`riggles.pick(name, { option: weight, ... })`** returns the name of one
option, drawn by weight. Above, Dusk comes up about half the time (3 of 6),
Sea a third and Ember a sixth. Weights are whole numbers from 0 to
4,294,967,295, and an option with weight 0 never comes up. The options can
also be given as pairs, `[['Dusk', 3], ['Ember', 1]]`, or a `Map`; a plain
object lists number-like keys (`{ 3: 1, 1: 1 }`) first, in ascending order,
whatever order you wrote them in.

**`riggles.range(name, min, max)`** returns a whole number from `min` to `max`,
both included.

How the values are drawn, and what that means for your code:

- A trait's value comes only from the token's seed and the trait's name. The
  token's metadata is computed onchain from the same seed with the same math on
  every read, so the attributes always name exactly what your art drew.
- Neither helper touches `riggles.random()`, p5's `random()` or `noise()`, or
  `Math.random()`. Adding, removing or reordering traits never moves the rest
  of your art.
- Declare each trait the same way on every seed, before the poster is taken:
  at the top of your script, in `setup`, or in your first `draw`. At
  registration the studio runs your code on many seeds, collects every pick and
  range with its options and odds, and checks they are the same on every seed.
  That list is stored onchain with your art, so you never write a separate
  trait list. A trait declared on only some seeds, or with options that change
  from seed to seed, cannot be stored.
- Asking again with the same arguments returns the same value. The same name
  with different options or bounds is an error.
- A trait that depends on what the drawing did, such as how many lines
  connected or which stars ended up alone, cannot be an onchain trait: the
  metadata only has the seed. Keep facts like that on the canvas.
- Limits: at most 16 traits; each name and option is 1 to 64 bytes of text with
  no control characters; a pick has 1 to 32 options and at least one weight
  above 0; a range stays within plus or minus 9,007,199,254,740,991. `Format` is
  taken: every token's attributes start with its format.
- A pick's value is text in the metadata and a range's is a number.
- A piece with no picks or ranges has no traits. That is normal, not an error.

`riggleTraits()`, `calculateFeatures()` and a global `traits` object are not
read. A piece ported from a platform that uses them declares its traits with
`riggles.pick` and `riggles.range` instead.

## 7. What is denied, and why

The frame your art runs in is a sandboxed, network-free document. Some of this
is enforced by the loader with a readable error; some of it is enforced by the
browser sandbox itself, silently, with no message from the platform.

- **Network.** `fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource`, `Worker`,
  and `SharedWorker` are all replaced with a function that throws:

  ```
  Network and worker APIs are unavailable in Riggles art.
  ```

  The frame's Content Security Policy already blocks these at the browser
  level (`connect-src 'none'`, `worker-src 'none'`); the guard exists so you
  get a legible error instead of a silent network failure.
- **Workers.** Covered by the same guard and the same CSP directive as network
  access, above.
- **Storage.** The frame is served from a `data:` origin inside a sandboxed
  iframe with no `allow-same-origin`, which makes its origin opaque. Storage
  APIs (`localStorage`, `sessionStorage`, `indexedDB`, cookies) are unreachable
  from an opaque origin as a property of the browser sandbox itself, not a
  guard the loader installs. There is no Riggles-specific error text for this;
  the browser throws its own security error if you touch these APIs.
- **Wallet.** `window.ethereum` and `window.crypto` are both explicitly set to
  `undefined` before your code runs. There is no wallet to connect to and no
  crypto API to read from inside the frame; referencing a property on either
  fails the way referencing a property on `undefined` always does.
- **Async setup.** Not a guard, a limitation: the loader does not await
  whatever your `setup()` returns, so an async `setup` will have its poster
  captured before any awaited work finishes. Do your setup work synchronously.
- **External assets.** The frame's script sources are restricted to `data:`
  and `blob:` (the loader's own blob URLs for the pinned p5 runtime and your
  source), so a `<script src="https://...">` tag or a remote font, image, or
  audio file cannot load. There is no fetch fallback for these either, per
  the network guard above.
- **HTML documents.** You submit one JavaScript source file, not an HTML page.
  The document around your script (the `<canvas>` styling, the payload, the
  runtime script tag) is built entirely by the platform. You cannot add your
  own `<head>` markup, stylesheets, or additional script tags; anything you
  need done to the page has to happen from inside your one JavaScript file.
- **Three.js and other runtimes.** The platform loads exactly one runtime for
  you: the pinned p5 1.11.11 build, and only for Format A. It does not fetch or
  bundle Three.js, matter.js, or anything else, and since network access is
  denied you cannot load one from a CDN yourself. A Format B script can use any
  browser API a canvas element gives it, including WebGL, but the entire
  runtime you want has to be part of the single JavaScript file you submit;
  the platform will not fetch it for you.

## 8. Errors you will see

Every run reports its status through one of five phases, in order:

1. `load`. Your script failed to parse, or after the poster budget ran out
   there was still no `setup()` function and no canvas in the document.
2. `setup`. Your `setup()` function itself threw.
3. `poster`. Something failed while the loader was driving the poster's
   animation frames.
4. `traits`. The declared traits exceeded the platform's size budget. A
   `riggles.pick` or `riggles.range` call with bad arguments throws where you
   called it, so it reports in the phase your code was in.
5. `live`. An uncaught error in `draw()` or in an event handler, after the
   poster has already been reported successfully.

Every error carries a line and column number where the browser can supply one:
a syntax error reports the exact line of your source, and a throw inside
`draw` or an event handler reports the exact line that threw, because your
script is loaded from its own blob URL and the loader maps stack traces back
to it. A live-phase error never tears down the canvas; whatever was drawn last
stays on screen.

Every error message is capped at 2048 characters. In the studio, these
messages surface inline under the code editor next to the line they came
from. On the deployed collection page, the surrounding page may show its own
"art failed" state instead of exposing the raw message.

## 9. Testing your piece

Before you deploy, the studio's collection preview is the QA step. It renders
a sample of the supply you have configured, one hundred seeds or a chosen
percentage, as a grid of poster thumbnails, using the exact loader bytes that
will run once the collection is live. The preview is not a simulation of the
collection; it is the collection, seen early.

Every trait_type your piece reports becomes a filter over that grid, with a
count for each value, so you can see your rarity distribution and spot
outliers before a single token is minted. What you see in the trait filters is
exactly what the deployed metadata will carry, because it is what
`riggles.pick` and `riggles.range` returned for each sampled seed, the same
values the metadata computes onchain.

Use the grid to check for things a single-seed Run cannot show you: does any
seed produce a broken or empty canvas, does the palette collapse to one look
across most seeds, are the trait counts what you expected, does a rare
combination look the way you intended it to. Click any cell to open the full
render and confirm it at full size before you commit to deploying.

## 10. From studio to onchain

Registration is what turns a draft into a deployable collection: it takes the
canvas size the loader read back after your `setup()` ran, along with the
`posterFrame` you set, and writes both into the manifest that the rest of the
platform trusts. REGISTER ART first runs your code on 32 seeds and collects the
trait list (section 6), then stores your code compressed with gzip, that list and
your project description (up to 1,000 bytes) together as one onchain version.
Changing the code, the format, the poster frame or the description means
registering again.

Where the art lives in each token depends on its size after compression. Canvas
and SVG art stored in 40,000 bytes or less is carried inside every token, so the
token holds the whole piece. Larger Canvas or SVG art, and all p5 art, gets a
small viewer page that reads the art (and p5) from the chain when the token is
opened. Nothing is refused for size up to the 1 MiB source limit; the Studio
shows which one your piece gets after it runs.

A PNG collection is a series of 1 of 1s: you upload every image before you
register, and token 1 shows image 1, token 2 shows image 2, and so on. The
supply is the number of images. Choose the files together (they join in
file-name order) and arrange the order with UP, DOWN and REMOVE; the same image
twice is refused. Each image is stored onchain exactly as uploaded (a color
profile is removed first so every browser shows the same colors), up to 1 MiB.
An image of 40,000 bytes or less is itself its token's image; a larger one is
shown by a copy Riggles serves, and the PNG stays onchain. Keep the tab open
from choosing the images to registering them: the images go onchain from the
page that read them. From there, deploying is a single wallet transaction: the
factory deploys your collection's payout splitter, the collection contract
itself, and the Riggle Auction house for it, all together, signed once from
your wallet. Every deployed collection renders through the same shared v2
renderer this guide describes, running the exact loader bytes your preview
ran. The platform does not ask you for a static image or a metadata file
separately: it publishes the poster image for each token by actually running
your code against that token's committed seed, the same way the studio's
preview did, and the attributes are computed onchain from that seed and the
trait list stored with your art.

## 11. Collections on the onchain launchpad

A launchpad collection is a contract you own that collectors mint from, in the
mint phases and at the prices you set. In Art and metadata you choose where its
art lives:

- **Hosted metadata.** Your own artwork and metadata, at an address you host.
  Nothing in this guide is needed for it.
- **Onchain artwork from layers.** Trait images you upload, stored onchain and
  layered into each token's image by the collection's renderer. Section 12.
- **Generative onchain code.** One JavaScript file, stored onchain and run for
  each token from that token's own hash. Section 13.

Every collection, whatever its art, deploys to a testnet first and reaches
mainnet only after review, the way Auction House collections do. Section 14
walks through it.

## 12. Layered collections

### Preparing layers

Choose **Onchain artwork from layers** in Art and metadata. There are two ways
to bring your art in.

**Build from traits** opens the Trait editor. Add a layer, name it, then add
its trait images, 1 to 255 at a time. **Add layers from a folder** takes a
folder of layer folders and adds one layer per folder. A file named
`Blue#20.png` becomes the trait Blue at weight 20; a file name without `#` and
a whole number keeps its whole name, at weight 1.

**Import prepared folder** takes a collection you have already generated, in
this shape:

```
collection.json
tokens.json
art/<Layer>/<Trait>.png      (or .gif, .jpg, .jpeg, .svg)
```

`collection.json` holds `namePrefix` (each token's name is this followed by its
number, up to 64 bytes), `description` (up to 512 bytes), `startTokenId` (0 or
1), `supply`, `drawOrder` (the layer names, bottom to top), `artDir` (the art
folder, `art` above) and `tokens` (the token list's file, `tokens.json` above).
`attributeOrder` and `traitOrder` are optional (Order, below). `tokens.json`
lists every token once, with consecutive ids from `startTokenId`:

```
[{ "tokenId": 1, "traits": { "Background": "Dusk", "Hat": "Cap" } }, ...]
```

A layer a token leaves out has nothing drawn on it and no attribute in its
metadata. The supply and first token ID must match the collection's settings.
Every trait a token uses needs exactly one image file; art files no token uses
are not stored. A folder holds at most 3,830 files and 128 MiB, and files and
folders whose names start with a dot are skipped.

### The images

Each trait is one image: PNG, GIF, JPEG or passive SVG.

- **Size.** At most 24,575 bytes each, as stored onchain after the cleanup
  below, and 1 to 4,096 pixels on each side. A file over 1 MiB is refused
  before it is read.
- **PNG.** A still image: an animated PNG is refused, so use a GIF for motion.
  A color profile is removed, but only when the browser then shows exactly the
  same pixels; if it does not, export the image again without a profile.
- **JPEG.** Baseline or progressive. Its metadata (EXIF, XMP, color profiles,
  comments) is removed under the same pixel check, and a JPEG with an EXIF
  rotation is refused: save it upright. JPEG has no transparency, so it suits
  the bottom layer.
- **GIF.** Stored exactly as uploaded, every frame included, so a GIF trait
  animates.
- **SVG.** Plain shapes only: `svg`, `g`, `path`, `rect`, `circle`, `ellipse`,
  `line`, `polyline` and `polygon`, with their shape, fill, stroke, opacity and
  `transform` attributes. No text, `style`, `id` or `class`, no scripts,
  gradients, links or `url(...)` references, and no `<?xml ?>` line, doctype or
  comment, which many editors add on export. The root needs
  `xmlns="http://www.w3.org/2000/svg"` and a `width` and `height` or a
  `viewBox`. Convert text to paths. An SVG that does not fit is refused, never
  turned into pixels.

Every token is drawn as one 1,200 by 1,200 SVG. Each of its traits is a layer
of that square, scaled to fit it and centered on its own, with pixelated
scaling. Export every trait at the same width and height, transparent wherever
the layer has nothing, or the layers will not line up. Pixelated scaling keeps
pixel art sharp at any size.

The Studio checks every token before you can deploy:

- at most 15 layers, and at most 255 traits in each;
- the traits one token uses, together, at most 32,768 bytes;
- each token's SVG at most 45,568 bytes, and its whole metadata at most
  102,400 bytes;
- a supply of at most 100,000, every token a different combination of traits.

A layered collection also needs a **Hosted metadata fallback URL**, set in Art
and metadata: an address that serves each token's metadata, which the
collection uses until its renderer is connected.

### Naming traits

A token's metadata lists its traits with the layer name as the trait type and
the trait name as the value. These are the names collectors read.

Names are 1 to 64 bytes of text with no control characters. The Trait editor
also refuses slashes and trims spaces at either end. Layer names must differ
from each other, and so must the trait names inside one layer; two layers can
each have a trait with the same name.

Settle names before you deploy. Which trait each token has is fixed at
deployment. A trait's name and image can still be changed later in Management,
Onchain art, Replace a trait (the new image no larger than the one it
replaces), until you choose Lock trait replacement, which is permanent. The
Studio has no way to rename a layer after deployment.

### Weights and exact quantities

Each layer in the Trait editor sets how many tokens get each of its traits, in
one of two ways:

- **Weights to exact counts.** Each trait gets a whole-number weight from 1 to
  4,294,967,295. The weights become exact token counts that add up to the
  supply: each trait gets its share of the supply (its weight over the layer's
  total weight) rounded down, and the tokens left over go one each to the
  traits that rounding cut the most, the earlier trait first on a tie. With a
  supply of 1,000 and weights 3, 2 and 1, the counts are 500, 333 and 167. A
  weight is not a chance per token: the collection gets exactly these counts.
- **Exact quantities.** You enter each trait's count, from 0. A layer's counts
  must add up to exactly the supply; Layer total shows the sum as you type.

A trait whose count comes to 0 is left out of the collection, and the editor
names it before you generate. Switching a layer between the two keeps every
trait, and switching to exact quantities gives each trait at least one token
when the supply allows it. Randomize rarity gives a layer random counts with
every trait on at least one token. Sort by rarity lists its traits rarest
first.

**Linked traits.** A trait can link to one trait in another layer: every token
with it also gets the linked trait. Counts stay exact, so the linked trait
needs at least as many tokens.

**Generate collection** deals the counts out to the tokens from the Generation
seed: the same seed and settings always give the same collection, and New seed
gives another. Every token must be a different combination of traits, so no
trait can appear on more tokens than the other layers have combinations. When
the counts cannot make every token unique, the editor says so and keeps
nothing. The tokens come out in shuffled order. Check every token in
Collection preview, where Filter by trait shows the tokens with any trait you
select, with counts, then choose **Lock reviewed DNA**. Unlocking clears the
reviewed package.

### Order

Layers draw bottom to top: the first layer in the Trait editor's list is drawn
first, at the bottom, and the last is drawn on top. Drag a layer by its handle
to reorder it, or select it and use Move up to draw it one place higher and
Move down to draw it one place lower. A token's metadata lists its traits in
the same order.

In a prepared folder, `drawOrder` lists the layers bottom to top, and
`attributeOrder`, when present, sets the order the metadata lists them in.

Traits are stored in each layer in the order the Trait editor lists them. A
prepared folder stores them sorted by name, unless `collection.json` has a
`traitOrder` that lists each layer's traits in the order you want.

## 13. Code art collections

Choose **Generative onchain code** in Art and metadata, then **Open code
editor**. You write one JavaScript file: paste it in, or use Import .js file.
The Library setting decides which format it is:

- **None (raw canvas script)**: Format B (section 3). Your script makes its
  own canvas and draws on it, with no library loaded.
- **p5 1.11.11**: an ordinary p5 sketch, Format A (section 2), on the same
  pinned p5 the Auction House uses.

The art runs in the same sandboxed frame as Auction House art, with the same
`riggles` global: the same denied APIs (section 7), the same seeded randomness
(section 4), the same poster, set with the editor's Poster frame field
(section 5), and the same error phases (section 8). What differs is below.

### The seed

A collection token's hash is its seed. It is drawn in the transaction that
mints the token, from the collection's address, the token id, the wallet the
token is minted to, and that block's number, the previous block's hash and the
time, and it never changes after. Your code reads it as `riggles.seed` and as
`tokenData.hash`, and `tokenData.tokenId` is the token's real id. There is no
first bid: the mint is the moment the seed is fixed.

In the Code editor, Run, New seed and Run this seed are for previews only. A
preview hash is never saved or sent onchain; a minted token's hash always
comes from its own mint. The starter script declares two traits with
`riggles.pick` and `riggles.range`, and includes a `Random` class seeded from
`tokenData.hash` that draws the rest through `R`; Insert Random class adds the
class to a script without one. `riggles.random()` works as section 4
describes.

### Traits

Traits work exactly as section 6 describes: `riggles.pick` and `riggles.range`
in your own code, with the same rules and limits, `Format` included, and the
same value for the same hash on both products, so trait code written for one
works on the other. When you lock the reviewed script, the Studio runs it on 32
seeds, collects the trait list and stores it onchain with the script; you
never write a separate list. Each token's attributes are computed onchain from
its hash on every read. A collection token's attributes are your traits alone,
with no Format entry.

### Sizes

Collections differ from the Auction House here. The script is stored onchain
as written, in at most four chunks of 24,575 bytes: 98,300 bytes in all. The
editor takes up to 1 MiB while you work, but a script over 98,300 bytes cannot
be locked. The largest token's whole metadata must fit too: 349,560 characters
with no library, 940,000 with p5, with its traits counted at their longest
values. The editor shows both as you type, as Script and Largest token
metadata.

### The poster and token pictures

Every code art collection needs a poster: an HTTPS image URL, or an uploaded
PNG, GIF or SVG of at most 34,000 bytes. Where Riggles draws token pictures for
the network, each token's image is a picture drawn once on our server from that
token's own onchain art; elsewhere every token's image is the poster. Token
pictures are drawn without WebGL, so art that draws with WebGL keeps the poster
as every token's image. They are drawn with the server's own fonts, so art
should not depend on fonts installed on a viewer's computer.

On Ethereum, a large poster image together with a long token name and
description can make the renderer too large to deploy. The editor says so and
gives the longest poster that fits; an HTTPS poster URL avoids it.

### p5 collections and the fallback metadata

A p5 token's metadata carries p5 itself, so reading it through the collection
takes about 100M gas of call allowance, up to about 180M for the largest script
and poster. A reader allowed less, such as the common 50M default, gets the
collection's fallback metadata instead of the onchain artwork. The Studio fills
that fallback address for every code art collection from the locked script, so
there is nothing to type.

### Checking and locking

The Code editor checks the script as you type, with the same check the server
runs when the script is saved. A finding marked **Blocks locking** must be fixed
before the script can be locked; a **Warning** does not stop it. Collection
preview then runs the exact onchain document on a sample of hashes (100, 10% or
25% of the supply, or all of it). Open any sample to see it full size with its
seed and traits. Every trait becomes a filter over the samples, with a count for
each value, as in section 9. **Lock reviewed script** collects the traits (the
Studio shows its progress through the 32 seeds), lists them on the preview and
saves the script for deployment.

Once deployed, the script can be replaced from Management, Onchain art, and a
replacement carries its own trait list, so the traits always describe the
script that runs. Lock script replacement stops replacements for good.

## 14. From testnet to mainnet

Every collection deploys to a testnet first, and reaches mainnet only after
Riggles approves that testnet deployment, the same gate Auction House
collections pass. A Robinhood collection is tested on Robinhood Testnet and an
Ethereum collection on Ethereum Sepolia. Your wallet pays the gas for each
testnet transaction.

1. **Sign in.** Creating and managing a collection needs a wallet holding a
   riggle activated to Tier 1, the same sign-in the Studio uses. The collection
   page uses that same session, so there is no second signature.
2. **Launch a collection.** In the Studio, choose Create a project, then Launch
   a collection. It opens a new collection: set its name, symbol, maximum
   supply, network and royalties in Contract details. Your collections also
   show in the Studio's project list.
3. **Art.** In Art and metadata, prepare the art (section 12 or 13), check it
   in Collection preview and lock it.
4. **Deploy.** In Contracts, review the collection, then choose Deploy
   contract. Your wallet signs once to request the deployment, then sends one
   transaction to the Riggles collection factory, which builds the collection
   and makes your wallet its owner. Onchain art then takes further
   transactions from your wallet, each reviewed and simulated before your
   wallet opens: storing the art, deploying the renderer and connecting it to
   the collection. The screen counts the transactions left. **Verify onchain
   render** then checks what is onchain against your saved art. Mint phases
   stay closed until it passes.
5. **Mint to check it.** In Mint setup, add a phase, review it and send it. Open the mint
   page (Open mint page, on your collection) and mint a token, then look at its
   image and its metadata. For onchain art, **Verify first minted token**
   checks that token against your saved art; a code art collection asks for
   the mint's transaction hash first.
6. **Submit for review.** In your collection's Mainnet section, select **Request
   mainnet review**. The section reads WAITING FOR REVIEW. until Cap or Ross
   approve it (APPROVED) or decline it with a reason (DECLINED); an approval can
   later be REVOKED. **Withdraw request** takes a waiting request back. An onchain art collection can be
   submitted once its first minted token is verified. Only an approved
   deployment goes to mainnet, and mainnet deploys exactly its settings and
   art. To change them, deploy a new testnet version and request a new review.