genart
Craft long-form generative art for onchain platforms — Art Blocks, 256ART, Verse, Highlight, Plottables, bootloader.art, or a self-hosted drop — and for screen diffusion channels such as Artpoint. Covers hash-seeded determinism, resolution-agnostic rendering, features and rarity
Install
npx skills add https://github.com/camilleroux/genart-skill/tree/main/skills/genart
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install camilleroux-genart-skill@llmmart
git clone https://github.com/camilleroux/genart-skill.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole camilleroux/genart-skill collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Generative art
Help design, write and check long-form generative artworks — pieces where one algorithm produces a whole edition, each output derived from a hash the blockchain hands you at mint time.
Default practices, and when to break them
These are defaults that work most of the time, not rules. Every one of them has legitimate counter-examples, and those are often the interesting pieces. State the default, then state what breaking it costs.
| Default | Breaking it is legitimate when… |
|---|---|
| All variation comes from the hash | The piece deliberately reads live onchain state (owner, block data). It is then no longer reproducible from the hash alone — own that choice and document it |
| One seeded PRNG, fixed consumption order | Almost never. But named sub-streams let you add a draw without shifting the whole edition — that is how you get flexibility without breaking the property |
| No network, no CDN, no system fonts | Some platforms have an explicit external-asset mode, under their own rules |
| No wall clock | The piece is about time passing, or is genuinely interactive. The preview render must stay stable regardless |
| Sizes relative to the canvas dimension | The piece is deliberately adaptive and reveals more detail with more room. A choice, not an accident — then know what size it will be captured at |
| Runs on current browsers and devices — Chrome, Firefox, Safari, mobile included | A piece built on a bleeding-edge API (WebGPU…) or needing real GPU power is a choice: state the requirement, keep the preview/capture path rendering everywhere, and remember collectors open links on phones |
| Emit the platform's render-done signal | Never. A blank thumbnail is a failure with no upside |
| Features computed before render, from the seed only | Where the platform computes features outside a browser, it is required. Elsewhere it is a convenience |
| Debug code is stripped from the submitted build | Where code is stored unminified and readable onchain, formatting is part of the work |
| Never claim "it's deterministic" without testing it, and without stating the scope | Never |
The firm part: breaking a default must be the artist's explicit choice, never an accident. Point out the deviation, explain what it costs, then do what the artist asked. Do not refuse and do not lecture.
Ethics
- Never reproduce a named living artist's signature work. Techniques are shared heritage; a body of work is not. Offered "make me a
- Credit the algorithm, the shader, the palette you borrowed.
- Check that a library's licence survives being written onchain forever.
- Be straight about what AI did: writing the code and generating the image are different claims.
Full version: references/ethics.md.
Platforms: read the docs, every time
Platform APIs change and the fiches in references/platforms/ deliberately hold
no versions, no numbers and no field names — only the stable mental model, the
canonical URLs and the questions to ask.
So the sequence is always: open the fiche → fetch the URLs it lists → then write code. Never write platform-specific code from memory, and never from the fiche alone. If a doc is unreachable, say so and flag what could not be confirmed.
| Target | Fiche |
|---|---|
| Art Blocks (incl. Engine / Flex) | references/platforms/artblocks.md |
| 256ART | references/platforms/256art.md |
| Verse | references/platforms/verse.md |
| Highlight | references/platforms/highlight.md |
| Plottables (pen plotter, AB Engine) | references/platforms/plottables.md |
| bootloader.art — ask which bootloader | references/platforms/bootloader.md |
| Self-hosted / no platform | references/platforms/self-hosted.md |
| Artpoint — art on screen, you deliver a video, not code | references/platforms/artpoint.md |
| Undecided between two | references/platforms/comparison.md |
Where to look
| When the question is about… | Read |
|---|---|
| Seeding from a hash, PRNG choice, sub-streams, distributions, things that silently break reproducibility | references/determinism.md |
| Output that changes with canvas size, stroke weights, noise frequency, element density, print or plotter output | references/resolution.md |
| Designing traits, rarity tables, weights, distribution that came out wrong | references/features.md |
| Originality, attribution, licences, disclosing AI use | references/ethics.md |
| Debug panel, param tweaking, keyboard shortcuts, PNG and video export, contact sheets | references/tooling.md |
| "Is my sketch actually deterministic?", pre-mint checking, what a test can and cannot prove | references/verification.md |
| Techniques, tutorials, libraries, inspiration, what other artists use | Fetch https://github.com/camilleroux/awesome-generative-art — a maintained list; do not paraphrase it from memory |
Runnable scripts
Two scripts ship with the plugin and run in place — never copy them into a
project. They need Playwright installed in the artist's project and print the
install command if it is missing. Contract and details: references/verification.md.
| Command | Does |
|---|---|
node "$CLAUDE_PLUGIN_ROOT/scripts/check.mjs" <dir> |
Determinism: repeatability, distinctness, global-state contamination, feature stability |
node "$CLAUDE_PLUGIN_ROOT/scripts/render.mjs" <dir> --hash 0x… |
One PNG — render it, then look at it with Read before judging any visual change |
node "$CLAUDE_PLUGIN_ROOT/scripts/render.mjs" <dir> --grid N |
Contact sheet, hash + features under each tile |
node "$CLAUDE_PLUGIN_ROOT/scripts/render.mjs" <dir> --census N --edition-size E |
Real feature distribution vs the rarity table |
node "$CLAUDE_PLUGIN_ROOT/scripts/render.mjs" <dir> --batch N --size 2400 |
N individual full-res PNGs, hash-named — portfolio/print export |
When asked to change how a piece looks, close the loop yourself: edit → render one hash → Read the PNG → judge → adjust. Do not describe a visual change you have not looked at.
Workflow
- Target first. Which platform? If undecided,
references/platforms/comparison.md. Once decided, open its fiche and fetch its docs before writing anything. - Decide the rarity table before coding. It is much harder to retrofit.
- Build. Defaults above; deviations discussed, not silently taken.
- Look at many outputs, not one. A single good render proves nothing —
render.mjs --gridexists for this. - Check before minting —
check.mjs, thenreferences/verification.mdfor what the green result does and does not prove.
Files (genart-skill)
-
references
-
platforms
-
256art.md 2 KB
<!-- Verified: 2026-08-28 --> # 256ART **In one line** — Ethereum, fully onchain: code, traits and metadata all served from the chain. No IPFS, no server. ## Mental model - **Two deliverables**: the artwork code and a weighted trait table (write the table first — `../features.md`). - Libraries come **only from an onchain filesystem**; the live list is in the Artist Portal, never a CDN. - A **platform-provided PRNG** whose use is expected. - The input object mixes **mint-fixed** values (hash, token id) with **live chain state** (owner, balances, block data) — optional fields may be absent: degrade, never throw. - Preview waits for an **explicit signal** and times out without it — the top cause of blank thumbnails. **The decision this platform forces**: using live-state fields makes the work non-reproducible from the hash alone and unverifiable by any determinism test. Legitimate and deliberately exposed — but a decision to take up front and state in the description, not to stumble into because a field was there. ## Docs — start at `https://docs.256.art/artist-documentation/` | Page | Answers | |---|---| | `artist-documentation/generative-art-template/` | File structure, input object, provided PRNG, trait table, sizing, render signal | | `artist-documentation/testing-your-project/` | Pre-deployment checklist, common rejections | | Artist Portal | The **live** library list — the portal is current, docs may not be | Community template: `https://github.com/Martibis/256ART-generative-art-template` ## Check before you code Input object fields (which fixed, which live) · provided PRNG's API · onchain library list and versions · trait table format and its summation rule · exact render-done signal · whether minification is expected. ## Traps by design Reading a live field without realising it · throwing on a missing optional field · trait weights failing the summation rule (usually a rounding error) · assuming a library exists because npm has it. -
artblocks.md 2.4 KB
<!-- Verified: 2026-08-28 --> # Art Blocks **In one line** — Ethereum; the curated reference platform. Your script is stored onchain and executed by an onchain generator that assembles the page. ## Mental model - **One JS file, one library max**, from a **closed registry** of pinned versions, injected by the generator — a `<script src>` you write will never run. - Hash + token id arrive in an **injected global**; the token id encodes project and invocation number. - Features are set **synchronously on a global** and must be computable **outside a browser** (no canvas) — traits decided while drawing cannot onboard. - **Bytes cost gas**: deployment cost is proportional to script size; the practical ceiling is far below any technical limit. - Preview: a configured delay, or an explicit ready signal with a placement requirement that catches people out — check it. - **Engine / Flex** = same protocol, different curation; Flex adds external assets (changes the "no network" default). Prohibition (Base), Bright Moments and Plottables (own sheet: `plottables.md`) are Engine deployments — this contract applies there too. ## Docs — start at `https://docs.artblocks.io/` | Page | Answers | |---|---| | `creator-onboarding/artists/1-building-your-project/` | The core contract: injected globals, libraries, size, features, preview | | `creator-onboarding/artists/2-staging-and-testing/` · `3-mainnet-launch/` · `faq/` | Staging, launch, edge cases | | `creator-onboarding/artists/flex-assets/` | Engine/Flex external assets | | `protocol/on-chain-generator/` · `protocol/postparams/` | Page assembly; post-mint params | | `developer/mcp-server/quick-start/` · `developer/mcp-server/capabilities/` | Art Blocks' own MCP server — connect it and ask it what it exposes, rather than reading its surface off a page | Community starter (unofficial): `https://github.com/ArtBlocks/artblocks-starter-template` ## Check before you code Injected global's name and fields · invocation derivation · approved libraries and versions **today** · features API and accepted types · preview mechanism and where the opt-in must live · size guidance and cost model · expected testing (how many hashes, which browsers). ## Traps by design Traits computed during render (fails headless) · a second library (there is none) · bundling instead of declaring a library · assuming last year's approved version · forgetting the generator injects its own page styles. -
artpoint.md 3.5 KB
<!-- Verified: 2026-09-04 --> # Artpoint **In one line** — a Paris digital-art agency, not a mint platform: you deliver a finished video file and it plays on client screens (offices, hotels, retail) on a monthly rotation. No chain, no hash, no code of yours runs on their side. ## Mental model - **You ship a cut, not a generator.** The edition logic stays yours: pick the seeds, render, hand over files. `../determinism.md` still pays — it is what lets you re-render a master a year later, at a bigger size or a different ratio. - **Put the recipe in the filename.** It is the only metadata that survives someone else's asset library — no sidecar, no README, no email thread. Piece, hash, sketch commit, size, fps, frames: `dunes_0x8f3a91c2_a41b9de_3840x2160_30fps_1800f.mp4`. - **The output device is a screen in a room where people work.** Same inversion as the plotter in `plottables.md`: ambient, muted, glanced at rather than looked at. Slow beats eventful, legible at distance and off-axis, contrast that survives a bright lobby. A screen nobody can look away from is a screen the client turns off. - **Time becomes an input.** A still becomes video by animating the system, or by travelling across seeds. Drive it from a frame counter, never the wall clock — `../tooling.md` §Video. - **A loop's seam gets many chances.** A one-minute piece replays hundreds of times a day, and a seam that passes once may not pass the fortieth (`../tooling.md` §Perfect loops). A visible cut is a legitimate choice; the seam nobody chose is not. - **One master by default — ask what they do to it themselves.** Rescaling and rotating is routine, reframing is not something to assume. A 9:16 that is not a cropped 16:9 is a second *render*, not a second export: deliver both when the composition demands it, and keep the framing yours. - **They need words too.** Works are shown with curatorial texts on a companion page: supply the description, or the key facts and let them write it. ## Delivery spec Artpoint publishes no artist specification. This is what the team asked for in 2026-09 — **reconfirm with your contact**; it is private and it will drift. | | | |---|---| | Container | `.mp4`, H.264 preferred | | Resolution | UHD, Full HD floor | | Ratio | 16:9 and/or 9:16 | | Duration | 3–5 min if it does not loop, around 1 min if it does | | Frame rate | 30 fps | | Weight | 1.5 GB per file max, 30 MB/s max | ## Docs `https://www.artpoint.fr` — the offer, the catalogue, the artist sign-up form. No artist documentation, no public spec: **your contact is the spec** — write the answers down and date them. ## Check before you render Container, resolution, ratio and weight caps · whether it must loop, which decides the duration · sound, or is every screen muted · which orientations are in the client's rooms · what they do to the file themselves: rescale, rotate, reframe · how long it stays up and how often it repeats in a day · the licence granted, its duration and territory, and the AI-training exclusion (`../ethics.md`) · what the curatorial text needs from you. ## Traps by design Screen-recording the sketch (dropped frames, variable timestep, nothing reproducible) · a loop that accumulates (`x += v`) or damps, and so never closes · exporting at the preview size instead of re-rendering at UHD (`../resolution.md`) · a 16:9 cropped to 9:16 by someone who did not compose it · a delivered master nothing in its filename can regenerate · sound designed in, played on a muted screen · a rhythm that reads well in a 30-second review and is unbearable on day nine. -
bootloader.md 2.5 KB
<!-- Verified: 2026-08-28 --> # bootloader.art **Ask which bootloader first** — the platform is a family of runtimes with incompatible constraints. Never "bootloader" in the singular. | | `svg-js` | `p5-js` | `generic-web` | |---|---|---|---| | What | inline JS → SVG | p5 sketches | HTML/CSS/JS projects | | Storage | **onchain** | IPFS | IPFS | | Deliver | code in a wrapper | a sketch | a zip + manifest | | Libraries | **none** — SVG DOM | p5, provided | anything in the zip | | Mint-time params | no | no | **yes**, typed schema | **In one line** — Tezos, by ObjktLabs. Open, experimental, non-curated, code visible next to the work by design. ## Mental model Common: seeded PRNG + edition number handed to your code; generators **versionable** (bug fixes after launch); collectors can regenerate keeping their seed; a detectable preview/cover mode. - **`svg-js`** — SVG DOM only, billed per byte onchain, node count is the budget. Stored **unminified**: your formatting is part of the work's visible provenance — the one place where "strip and minify" is wrong (`../tooling.md`). - **`p5-js`** — p5 provided and **already seeded**, inverting the usual bridge-your-own-PRNG advice. Check what that means for `random()`/`noise()` before wiring anything. - **`generic-web`** — zip + manifest declaring capture and animation; the only place in this corpus where **mint-time parameters** exist (typed schema, UI widgets). ## Docs `https://bootloader.art/` → `/resources`, `/help`, and one page per runtime (`/bootloaders/svg-js`, `/bootloaders/p5-js`, `/bootloaders/generic-web`). **The site rejects plain fetches** (403 / empty SPA shell) — open in a browser. Runtime source of truth: `https://github.com/objkt-com/bootloader-monorepo` (newer runtimes may live on a feature branch). Third-party sandbox: `https://github.com/Tezumie/bootloader-sandbox`. ## Check before you code Which runtime and version · injected object's fields (PRNG, seed, edition, preview flag, root element) · features mechanism if any · render-done signal · `svg-js`: current cost per byte · `generic-web`: manifest options (capture, animation, params) · dev harness params (seed, edition, capture). ## Traps by design Assuming a canvas in `svg-js` · porting canvas work to `svg-js` (vector cost scales with nodes, not resolution) · minifying `svg-js` code · double-seeding p5 · referencing outside the zip in `generic-web` · treating these docs as settled — youngest platform here, the sheet most likely to be stale: read the site and the monorepo. -
comparison.md 3.1 KB
<!-- Verified: 2026-08-28 --> # Choosing a platform Qualitative on purpose — nothing here to go stale. Once chosen: open the sheet, fetch its docs. | | Chain | Storage | You deliver | Hash reaches you via | Libraries | Mint-time params | Minify? | |---|---|---|---|---|---|---|---| | **Art Blocks** | Ethereum | onchain | one script | injected global | closed registry, one of them | post-mint params exist | yes, bytes cost gas | | **256ART** | Ethereum | onchain | code + trait table | injected object, plus live chain state | onchain filesystem only | no | yes | | **Verse** | Ethereum | IPFS | a web page | URL parameter | free | no | optional | | **bootloader `svg-js`** | Tezos | onchain | inline JS → SVG | injected object | **none** | no | **no — formatting is the work** | | **bootloader `p5-js`** | Tezos | IPFS | a p5 sketch | injected object, p5 pre-seeded | p5, provided | no | optional | | **bootloader `generic-web`** | Tezos | IPFS | a zip + manifest | injected object | free, inside the zip | **yes, typed schema** | optional | | **Highlight** | EVM multi-chain | IPFS | a web page + platform script | platform script, pre-seeded | free | no | optional | | **Plottables** (AB Engine) | Ethereum | onchain | one script (Art Blocks contract) | injected global | closed registry | no | yes | | **Self-hosted** | — | your choice | a web page | your choice | free | roll your own | optional | The table is shaped for mint platforms. **Artpoint** (`artpoint.md`) is a different animal — a screen-diffusion agency: no chain, no hash, no code of yours running anywhere. You deliver a finished video file. ## Choosing by what constrains you - **Smallest possible code, maximum permanence** → Art Blocks or 256ART. Both put bytes on Ethereum and both will make you fight for kilobytes. - **Vector work, plotter output, code as part of the piece** → bootloader `svg-js`. No canvas, no libraries, and the source is read alongside the work. - **Heavy 3D, large assets, a real bundle** → Verse, bootloader `generic-web`, or self-hosted. Onchain platforms are the wrong shape for this. - **The collector should choose something at mint** → bootloader `generic-web`. - **The piece reads live chain state on purpose** → 256ART, which exposes it deliberately. Read the trade-off in that sheet before committing. - **p5, quickly, with the seeding already handled** → bootloader `p5-js`, or Highlight, whose platform script hands you seeded draws. - **Self-serve on an EVM chain, no application** → Highlight. - **Pen plotter as the real output device** → Plottables (and read the plotter section of `../tooling.md`). - **No platform, full control** → `self-hosted.md`. - **The deliverable is a video on someone's screen, not a token** → `artpoint.md`. ## Porting between platforms Four things change: how the hash arrives (isolate it in one function on day one and porting is an afternoon) · how features are declared · how the render-done signal is sent · **which libraries are allowed** — the fatal one: a piece built on a library the target does not offer is a rewrite, not a port. -
highlight.md 1.7 KB
<!-- Verified: 2026-08-28 --> # Highlight **In one line** — EVM multi-chain, self-serve, no application: a web project bundled with a required platform script, wired to the mint by the platform. ## Mental model - The **platform script (`hl-gen.js`) is the whole interface**: seeded randomness, token data, trait declaration, preview capture. Your code talks to it, never to the chain. - The **seed derives from the mint transaction + token** — random functions arrive pre-seeded. Keeping your own PRNG (for portability) is fine, but seed it from the script's data and **never consume from both streams**. - **Mint context is exposed** (minter, chain data) — the 256ART trade-off in milder form: deliberate use is legitimate, accidental use is the bug. - Curated vs open flows assign hashes differently — check which applies. ## Docs `https://github.com/highlightxyz/generative-art` — the canonical repo: the script, README, FAQ, examples. **Read the script's source; it is short and it is the contract** — prefer it over any summary, including this one. `https://support.highlight.xyz` — creator knowledge base (generative flow, upload, testing). ## Check before you code What the script exposes (random, token data, mint context, traits, capture) · seed derivation and the curated-flow difference · bundle contents and entry point · trait typing and when traits are read · capture trigger and size · available chains. ## Traps by design Consuming from your PRNG *and* the platform's (two streams, order bugs) · reading mint context unintentionally · testing only one of curated/open · forgetting the platform script in the bundle (local stub works, upload fails). -
plottables.md 1.6 KB
<!-- Verified: 2026-08-28 --> # Plottables **In one line** — Ethereum, Art Blocks Engine, pen-plotter generative art: the token is a generator whose outputs are meant to be physically drawn. ## Mental model - **The contract is Art Blocks** — read `artblocks.md` and follow its sequence; this sheet only adds what plotting changes. - **The real output device holds a pen.** Screen rendering is a preview, which inverts several defaults: strokes only (no fills, opacity, blending); **line density is the whole game** (too few wastes the ink's presence, too many drowns the paper — tuned by plotting physical prototypes, early and often); color = choice of pens per layer, not RGB; paper has a real ratio and a size in millimetres. - Expose vector output a plotter toolchain can consume — the SVG section of `../tooling.md` (real units, one path per stroke, no fills). Generate the SVG and the preview **from the same stroke list**, or they will disagree. - The token usually carries the plotting privilege: the generator is the artwork, a plot is an instantiation. ## Docs `https://plottables.io` (platform, releases, applying) · `https://github.com/plottables` (Engine templates, plotting tools) · everything in `artblocks.md`. ## Check before you code The `artblocks.md` checklist, plus: expected vector format and export path · standard paper sizes and ratios · how the plotting privilege is granted · line-count / plot-time expectations. ## Traps by design Designing on screen, discovering on paper (opacity stacking and dense fills turn to mud) · fills or raster effects anywhere in the pipeline · vector export bolted on after the canvas render. -
self-hosted.md 2.3 KB
<!-- Verified: 2026-08-28 --> # Self-hosted / no platform No platform contract to satisfy — so this is the one prescriptive sheet: a minimal contract worth adopting anyway, because porting, the verification scripts and long-term survival all need the same three things: an external seed, declared features, a done signal. ## The minimal contract **Seed and size in, from the URL** (fallback random in dev, written back into the URL so every output is a shareable, reproducible link): ```js const q = new URLSearchParams(location.search); const hash = q.get("hash") ?? "0x" + crypto.randomUUID().replace(/-/g, "").repeat(2); const W = +(q.get("width") ?? 800); // tools pass a size the same way — const H = +(q.get("height") ?? 800); // honour it, then freeze it (resolution.md) ``` (That `crypto` call is the one legitimate unseeded random in a generative piece: choosing which piece to look at, never how to draw it.) **Features out** — one flat object on a global, from the hash alone, before rendering. **Done signal** — set once the frame is genuinely complete: ```js window.rendered = canvas; document.dispatchEvent(new CustomEvent("genart:done")); ``` **One render entry point** — `window.render = (hash) => { … }`. Costs nothing; enables re-rolling without reload and the A-B-A contamination test in `../verification.md`, and forces the render to be a pure function of the hash — the property you wanted anyway. **Self-contained** — one entry HTML, everything inlined (fonts base64, shaders as strings), no CDN, no fetch. Test with the network disabled: that is the actual requirement. ## Concepts you will meet, named neutrally - **Mint-time parameters** — the collector picks values within declared ranges before minting; the piece takes two inputs. Implemented today by bootloader `generic-web` (`bootloader.md`). - **Preview trigger** — the piece declares "capture this frame" instead of the tooling guessing after a delay; roll your own with the done signal. ## Hosting and posterity IPFS with a pinning service you actually pay for, or Arweave. A personal domain is mortal. Document yourself what nobody else will: seed format, feature weights used, capture size/ratio, target browser, the artwork's licence and the licences of everything bundled (`../ethics.md`). -
verse.md 1.5 KB
<!-- Verified: 2026-08-28 --> # Verse **In one line** — Ethereum; HTML+JS deployed to IPFS, hash generated at sale time and handed to the page. ## Mental model - **The hash arrives through the URL**, encoded, with edition info — the only platform here using a query param rather than an injected global. First thing to handle when porting in or out: a missed param silently falls back to a random seed and everything looks fine locally. - You ship a **web page** (entry HTML + assets) to IPFS — self-containment still applies: what is not in the bundle will not exist in ten years. - Features on a global object; preview captured automatically after sale but steerable (aspect ratio, explicit trigger, supplied image); animation accepted. - The artist owns the contract. ## Docs — start at `https://docs.verse.works/` Paths have **no `/docs/` prefix** (a stale prefixed link 404s and looks like the page is gone): `/projects/generative-verse/` (the generative JS model), `/guides/new-project/`, `/for-developers/`, `/contract/basics/`, `/sales-mechanics/…` (edition types change the data your page receives). ## Check before you code URL parameter name and decoded content · features object name · capture API and aspect ratio control · expected IPFS bundle structure · which sales mechanics affect the payload. ## Traps by design Porting from an injected-global platform and missing the URL param · a CDN reference left in the bundle · stale `/docs/`-prefixed links.
-
-
determinism.md 4.4 KB
<!-- Verified: 2026-08-28 --> # Determinism The contract: the same hash rebuilds the same artwork, on any machine, forever. `Math.random`, `Date.now`, `performance.now`, `crypto.getRandomValues` and anything derived from them break it — the piece renders differently on every load and no thumbnail matches the live view. ## Hash → seed → PRNG Fold **all 32 bytes** of the hash in (first-8-chars-only causes collisions across a large edition). Use `sfc32`: fast, integer-only, identical on every engine. ```js function seedFromHash(hash) { const hex = hash.replace(/^0x/i, "").padStart(64, "0").slice(-64); const s = new Uint32Array(4); for (let i = 0; i < 8; i++) { const w = parseInt(hex.slice(i * 8, i * 8 + 8), 16) >>> 0; s[i % 4] = (Math.imul(s[i % 4] ^ w, 0x9e3779b1) + i) >>> 0; // mix, do not fold } if (!(s[0] | s[1] | s[2] | s[3])) s[3] = 1; // sfc32 must not start all-zero return s; } function sfc32([a, b, c, d]) { return function () { a |= 0; b |= 0; c |= 0; d |= 0; const 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; }; } const rnd = sfc32(seedFromHash(hash)); for (let i = 0; i < 12; i++) rnd(); // warm-up: undiluted seeds correlate first draws ``` `Math.imul`, not plain XOR: XOR-folding cancels on patterned hashes (`a3f1a3f1…` → all-zero state → every such hash gives the same artwork). Test harnesses use exactly such hashes. ## Named sub-streams — set up on day one **Draw order is part of the artwork**: one extra `rnd()` call shifts every later draw and changes the whole edition. Sub-streams isolate domains so a change in one leaves the others untouched. ```js function fnv1a(str) { let h = 0x811c9dc5; for (let i = 0; i < str.length; i++) { h ^= str.charCodeAt(i); h = Math.imul(h, 0x01000193); } return h >>> 0; } function stream(hash, label) { const s = seedFromHash(hash); const k = fnv1a(label); const mixed = [0, 1, 2, 3].map((i) => (s[i] ^ Math.imul(k + i, 0x9e3779b1)) >>> 0); if (!(mixed[0] | mixed[1] | mixed[2] | mixed[3])) mixed[3] = 1; const r = sfc32(mixed); for (let i = 0; i < 12; i++) r(); return r; } const palette = stream(hash, "palette"); const layout = stream(hash, "layout"); ``` ## Distributions ```js const int = (r, a, b) => a + Math.floor(r() * (b - a + 1)); const pick = (r, xs) => xs[Math.floor(r() * xs.length)]; const chance = (r, p) => r() < p; const gaussian = (r) => Math.sqrt(-2 * Math.log(1 - r())) * Math.cos(2 * Math.PI * r()); const weighted = (r, entries) => { // [[value, weight], ...] let total = 0; for (const [, w] of entries) total += w; let x = r() * total; for (const [v, w] of entries) { x -= w; if (x < 0) return v; } return entries[entries.length - 1][0]; }; const shuffle = (r, xs) => { // Fisher-Yates, in place for (let i = xs.length - 1; i > 0; i--) { const j = Math.floor(r() * (i + 1)); [xs[i], xs[j]] = [xs[j], xs[i]]; } return xs; }; ``` `gaussian` consumes **two** draws; any helper with a data-dependent draw count (rejection sampling) belongs in its own sub-stream. Uniform reads lifeless: skew with `r() ** 2` (bias small), `1 - (1 - r()) ** 2` (bias large), or mixed gaussians — see [Piter Pasma on distributions](https://piterpasma.nl/articles/probability). ## Silent breakers - `arr.sort()` without a comparator (lexicographic); a comparator calling the PRNG (engine-dependent result). - `for...in`, `Object.keys()` on mixed integer/string keys; `Set`/`Map` iteration when insertion is conditional. - Async completion order affecting drawing (image decodes, workers, `Promise.all`). - Reading canvas/viewport size as a *composition* input (scaling only — see `resolution.md`). - Module-level mutable state surviving between renders — invisible on a single run; `verification.md` has the `[A,B,A]` test for it. ## Floats `Math.sin/cos/pow/exp/log` are not bit-identical across JS engines. Harmless in geometry; forbidden in two places: - **inside the PRNG** — `fract(sin(x)*43758.5453)`-style generators diverge across engines and GPUs; integer arithmetic only (in GLSL use an integer hash like PCG); - **in feature computation** — a one-ULP difference at a threshold flips a trait and the metadata lies forever. Decide traits from PRNG output with generous bin margins (`features.md`). -
ethics.md 2.1 KB
<!-- Verified: 2026-08-28 --> # Ethics The one normative sheet — these are lines, not defaults. ## Originality Techniques are shared heritage: flow fields, Truchet tiles, differential growth, Poisson-disc, reaction-diffusion, circle packing, noise — published, owned by no one, free to use. A named artist's signature body of work — its palette, gesture, recognisable resolution of a technique — is theirs. **Asked to reproduce a named artist's work**: decline in one sentence, offer the underlying technique, move on. No lecture, no repetition. > "I won't rebuild <artist>'s work — that's their piece. The technique under it > is <X>; here's how it works. Where do you want to take it?" The honest "in the style of": name the influence, make something the influence would not have made. ## Attribution Credit, in the project description or a comment: the algorithm's source (paper, post, person), shaders and snippets (Shadertoy, iq, lygia, Stack Overflow), palettes taken from a named collection or work, fonts and their licences. A credit line costs nothing; its absence turns a technique into a taking. ## Licences Onchain/IPFS deployment is permanent distribution — check before bundling: - MIT/BSD/CC0 safe; Apache-2.0 requires keeping the notice in the bundle. - **GPL/AGPL in an immutable artefact is a real problem** — you cannot later comply or unpublish. Avoid onchain. - Fonts: embedding is often exactly what their licence restricts. - Non-commercial clauses are incompatible with work that will be sold. Decide the artwork's own licence (CC0, named licence, all rights reserved) before the mint, not after. ## Being straight about AI Distinct claims — do not blur them: AI **wrote the code** (normal; the algorithm and aesthetic decisions remain the artist's) ≠ AI **generated the image** ≠ AI **produced the concept**. Some platforms have explicit policies; collectors are entitled to know what they are buying. Never claim onchain randomness you did not use, determinism you have not tested (`verification.md`), or rarity you did not measure (`features.md`). Never train on or generate from a living artist's work without their agreement. -
features.md 2.3 KB
<!-- Verified: 2026-08-28 --> # Features and rarity Traits are how a collector reads an edition. They describe the work, they do not configure it: 4–8 axes nameable after five seconds of looking, values as words (`Density: Sparse|Balanced|Dense`), numbers only for the genuinely countable. ## Table first, then code Retrofitting rarity shifts the draw stream and changes the whole edition; writing the table first costs nothing — and it is also what you hand the platform and what you compare the measured distribution against. ```js const PALETTES = [["Ember", 45], ["Ash", 30], ["Verdant", 18], ["Aurora", 7]]; const palette = weighted(rndPalette, PALETTES); ``` ## Pure function of the hash, computed before render ```js function features(hash) { const r = stream(hash, "features"); return { Palette: weighted(r, PALETTES) /* … */ }; } ``` Some platforms compute features headless, with no canvas — traits decided while drawing fail there, and a feature derived from the render can disagree with the render forever. Keep the object flat: strings, numbers, booleans; no arrays, `NaN`, `undefined` or raw floats. ## Binning continuous values ```js const d = rnd(); const Density = d < 0.3 ? "Sparse" : d < 0.75 ? "Balanced" : "Dense"; ``` Never bin on `Math.sin/pow/exp` output — engines differ in the last bits and a boundary value flips the trait per browser (`determinism.md`). ## What shows up only after the mint Measure over thousands of seeds (`render.mjs --census`, see `verification.md`): - **A value that never appears** — usually an off-by-one threshold. - **Expected count < 1 over the real edition** — a 1/2000 trait in an edition of 500 will likely never mint. For a true "1 of 1", place it by invocation number, not probability. - **A value covering ≥80%** — carries no information; rebalance or drop. - **Duplicate full tuples** — traits too coarse for the edition size. Then look at pieces, not just counts — a well-distributed trait can still be invisible. ## Honesty Publish the weights you actually used; report realised distribution, not intended; say when a trait is placed rather than drawn. ## Platform specifics Declaration APIs, accepted types and weight formats differ and change — never from memory, always the fiche in `platforms/` then its docs. -
resolution.md 2.6 KB
<!-- Verified: 2026-08-28 --> # Rendering at any size The 300px thumbnail, the fullscreen live view and the 4000px print must be the same artwork — not the same pixels. ## Draw in a normalised space Scale into the canvas once; everything downstream is then correct by construction (a stray hard-coded pixel value becomes impossible): ```js const S = Math.min(canvas.width, canvas.height); ctx.translate((canvas.width - S) / 2, (canvas.height - S) / 2); ctx.scale(S, S); // now draw in 0..1 ctx.lineWidth = 0.004; // 0.4% of the short side ``` ## Freeze the size at load ```js canvas.width = W; canvas.height = H; // fixed, from platform or ?width=/?height= canvas.style.width = "100%"; canvas.style.height = "100%"; canvas.style.objectFit = "contain"; // CSS letterboxes; the buffer never changes ``` Resizing a live canvas destroys accumulated state with no correct resume — on window resize, re-render from the seed. Same for `devicePixelRatio`: pick it once (1 for capture, device value for live), never re-read mid-render. ## Things that must scale and get forgotten Stroke widths · shadow/glow blur radii · corner radii · font sizes · dash patterns · particle sizes · margins · displacement amplitudes · any "skip if smaller than N pixels" threshold. The tell: a raw number in a drawing call that is not a fraction of the normalised space. ## Noise Sample in normalised space — `noise(x * 4, y * 4)` gives 4 features across the piece at any size; `noise(px * 0.01)` gains detail as resolution grows (a legitimate choice only if chosen). ## Element density — the real trap 5 000 dots fill a 400px canvas and vanish at 4000px. Three defensible choices; pick one **on purpose** and write it down: 1. **Fixed count** — identical composition, larger reads sparser. The default of most long-form work. 2. **Fixed count, element size scaled** — perceived density preserved; right when the elements are the subject. 3. **Count scaled with area** — the piece reveals more when larger; then the thumbnail is a different artwork, so know which size gets captured. ## Text and fonts Font metrics vary across platforms: never derive layout from `measureText`. Structural text → embed the font inline and await `document.fonts.ready`; decorative text → prefer paths. ## Check Render one hash at 400/1000/2000/4000px (plus portrait/landscape if not square); look for thinning strokes, drifting margins, shifted composition. Then a dozen hashes — some layouts break only for particular seeds. Print needs 300 dpi survival; plotter output: see the SVG section of `tooling.md`. -
tooling.md 3.9 KB
<!-- Verified: 2026-08-28 --> # Working tools: debug GUI, shortcuts, export ## Keyboard shortcuts | Key | Does | |---|---| | `R` | new random hash, re-render | | `←` `→` | previous / next seed | | `C` | copy hash to clipboard | | `S` / `⇧S` | save PNG at display size / at high resolution | | `V` | start / stop video capture | | `G` | toggle debug panel | | `F` | fullscreen | | `1`–`4` | preset sizes (400/1000/2000/4000) | Two rules that matter more than the bindings: guard against typing into fields (`if (e.target.matches("input, textarea")) return`), and **put the current hash in the URL** on every re-roll — every interesting output becomes a shareable, reproducible link. ## Debug panel Plain DOM, no dependency. Show: current hash (with an input field — "output #412 looks wrong" needs its hash pasted), computed features live (a broken rarity table shows up as twenty identical re-rolls long before any script says so), render time, and the params being tuned. **Strip it from the submitted build**: behind a build flag, loaded via dynamic `import()`. Exception — bootloader `svg-js` stores code unminified and readable, so leftover debug code is not expensive there, it is *visible* (`platforms/bootloader.md`). ## PNG export — re-render, never upscale ```js function savePNG(scale = 1) { const c = document.createElement("canvas"); c.width = baseWidth * scale; c.height = baseHeight * scale; render(c, hash); // same seed, bigger canvas c.toBlob((b) => { const a = document.createElement("a"); a.href = URL.createObjectURL(b); a.download = `${projectName}_${hash.slice(0, 10)}.png`; // hash in the filename a.click(); URL.revokeObjectURL(a.href); }); } ``` WebGL needs `preserveDrawingBuffer: true` to be readable; prefer `toBlob` over `toDataURL` for large renders (string limits). ## Video - **`MediaRecorder`** → WebM, a dozen lines; right for real-time/interactive pieces; drops frames when rendering is slow. - **Frame sequence + `ffmpeg`** — advance a frame counter, render, save, assemble offline; the only correct option when frames exceed their budget or the piece is counter-driven (which, per `determinism.md`, it should be). Drive animation from a frame counter, not `performance.now()` — that is what makes an export reproducible. ## Perfect loops Everything periodic driven by one normalised phase, never elapsed time: ```js const t = frame / TOTAL_FRAMES; // 0..1, exact const y = Math.sin(2 * Math.PI * (t + phase)) * amp; ``` `sin(2π·(k·t + phase))` with integer `k` closes at `TOTAL_FRAMES`. What never loops: accumulation (`x += v`), damping, wall-clock seeding. Know which frame the platform captures — a loop's worst frame should not be its public face. ## SVG export — print and plotter Build a real SVG document; `toDataURL` gives pixels, which a plotter cannot use. Either draw to SVG natively, or keep a parallel stroke list and serialise it: ```js const NS = "http://www.w3.org/2000/svg"; const svg = document.createElementNS(NS, "svg"); svg.setAttribute("viewBox", "0 0 210 297"); // A4, in mm svg.setAttribute("width", "210mm"); svg.setAttribute("height", "297mm"); // … append <path>/<circle>/<line> while drawing … const blob = new Blob([new XMLSerializer().serializeToString(svg)], { type: "image/svg+xml" }); ``` Plotter rules: strokes only (no fills, no opacity — a pen has neither), one `<path>` per continuous pen-down movement, real units (mm), stroke width = the physical pen width, and sort/merge paths to reduce pen travel on large plots. ## Contact sheets The most useful tuning tool for an edition — shipped ready to run: ``` node "$CLAUDE_PLUGIN_ROOT/scripts/render.mjs" . --grid 50 # one sheet node "$CLAUDE_PLUGIN_ROOT/scripts/render.mjs" . --batch 50 --size 2400 # individual PNGs ``` Contract in `verification.md`. -
verification.md 4.3 KB
<!-- Verified: 2026-08-28 --> # Checking a piece before minting Two rules up front: a test proves **same-machine reproducibility only** (last section says what is out of reach), and never write "it's deterministic" without saying across what — machine, browser, GPU mode. ## Read it first — five minutes, no tooling Search the source **and the production bundle** (a dependency can bring `Math.random` in — a graphics library whose seed you never set is the classic case) for: `Math.random` · `Date` · `performance.now` · `crypto.getRandomValues` · `fetch` · `XMLHttpRequest` · `<script src=` · `@font-face` with a remote `url(` · `navigator.` · `Intl` · `toLocaleString` · `.sort(` without a comparator · `for...in` · `window.innerWidth` outside the sizing code A legitimate hit (dev-only hash generator) deserves a comment saying why. ## The scripts Run from the plugin, never copied; Playwright lives in the artist's project (they print the install command if missing): ``` node "$CLAUDE_PLUGIN_ROOT/scripts/check.mjs" <dir> [--size 600] [--runs 3] [--hashes 0x…,0x…] node "$CLAUDE_PLUGIN_ROOT/scripts/render.mjs" <dir> --hash 0x… | --grid N | --census N [--edition-size E] ``` Contract assumed (defined in `platforms/self-hosted.md`; any platform sketch satisfies it with a few lines of dev shim): hash from `?hash=`, size from `?width=`/`?height=`, `window.rendered` = finished canvas, `window.render(hash)` exposed (required for census and the A-B-A test), optionally `window.$features`. ## check.mjs — four tests, and why each exists - **Repeatability** — same hash, fresh contexts → identical pixels. Catches unseeded randomness and time dependence. - **Distinctness** — different hashes → different renders. Caught a real bug in `determinism.md`'s own `seedFromHash` (XOR-folding collapsed patterned hashes) while repeatability stayed green. - **Global state (A, B, A in one page)** — render A, B, A *without reloading*; the two A's must match. Catches module-level caches, pools, unreset accumulators — invisible to fresh-page repeatability, and platforms do render twice in one page. - **Feature stability** — same hash → same features, or the mint metadata can disagree with the artwork forever. ## render.mjs - `--hash` → one PNG. The visual loop: edit, render, **look**, adjust. - `--grid N` → contact sheet, hash + features under each tile; failed seeds marked red. - `--census N --edition-size E` → measured feature distribution with the `features.md` warnings (≥80% values, expected-count-<1, duplicate tuples). A sample estimates — a declared-but-unseen value may still be a threshold bug. - `--batch N --size 2400 [--out dir] [--hashes 0x…,0x…]` → N individual full-resolution PNGs, one file per seed, hash in the filename. ## The pinned environment, and why - `deviceScaleFactor: 1` — retina doubles the buffer, nothing compares. - `--force-color-profile=srgb` — otherwise output depends on the monitor profile. - `--disable-lcd-text`, `--font-render-hinting=none` — subpixel AA is machine-specific. - `await document.fonts.ready` — or you hash a half-drawn frame. - **Read the canvas, never `page.screenshot()`** — screenshots go through the compositor and CSS. - **WebGL needs `preserveDrawingBuffer: true`** or you hash a blank image. ## What none of this can tell you - **Cross-machine determinism in WebGL/WebGPU.** Shader compilers, float precision, rasterisation and MSAA differ per GPU; two correct machines produce different pixels. Only a software renderer (`--use-gl=swiftshader`) gives machine-comparable, committable hashes — never compare across GPU modes. For 3D the honest contract is *same composition, same features, differences below perception* — not same bytes. - **Cross-engine transcendentals** — invisible in geometry, decisive at feature boundaries (`determinism.md`). - **Other browsers and devices** — run repeatability on WebKit/Firefox to catch crashes and missing APIs (never compare their pixels to Chromium's), and open the piece on a phone at least once: collectors do, and mobile GPUs are where precision and memory limits bite first. - **Ten-year survival** — untestable; fewer dependencies and no experimental APIs is the whole strategy. - **Whether the rarity, the piece, or the originality is any good** — a contact sheet and `ethics.md`.
-
-
SKILL.md 7.6 KB
--- name: genart description: > Craft long-form generative art for onchain platforms — Art Blocks, 256ART, Verse, Highlight, Plottables, bootloader.art, or a self-hosted drop — and for screen diffusion channels such as Artpoint. Covers hash-seeded determinism, resolution-agnostic rendering, features and rarity design, preview capture signals, debug GUIs and image/video export shortcuts, verification of a sketch before minting, and the ethics of the field. Use when the user mentions generative art, gen art, creative coding, long-form, onchain art, a seeded sketch, a PRNG or deterministic randomness, traits, rarity, features, a mint, a plotter or SVG output, art on screen or delivering a piece as a video, or pastes platform APIs such as tokenData, $features, inputData, $bootloader, BTLDR, hl-gen, or a base64 payload query param. user-invokable: true argument-hint: "[platform|check|render] [path]" license: MIT metadata: author: Camille Roux category: generative-art --- # Generative art Help design, write and check long-form generative artworks — pieces where one algorithm produces a whole edition, each output derived from a hash the blockchain hands you at mint time. ## Default practices, and when to break them These are defaults that work most of the time, not rules. Every one of them has legitimate counter-examples, and those are often the interesting pieces. State the default, then state what breaking it costs. | Default | Breaking it is legitimate when… | |---|---| | All variation comes from the hash | The piece deliberately reads live onchain state (owner, block data). It is then no longer reproducible from the hash alone — own that choice and document it | | One seeded PRNG, fixed consumption order | Almost never. But *named sub-streams* let you add a draw without shifting the whole edition — that is how you get flexibility without breaking the property | | No network, no CDN, no system fonts | Some platforms have an explicit external-asset mode, under their own rules | | No wall clock | The piece is about time passing, or is genuinely interactive. The preview render must stay stable regardless | | Sizes relative to the canvas dimension | The piece is deliberately adaptive and reveals more detail with more room. A choice, not an accident — then know what size it will be captured at | | Runs on current browsers and devices — Chrome, Firefox, Safari, mobile included | A piece built on a bleeding-edge API (WebGPU…) or needing real GPU power is a choice: state the requirement, keep the preview/capture path rendering everywhere, and remember collectors open links on phones | | Emit the platform's render-done signal | Never. A blank thumbnail is a failure with no upside | | Features computed before render, from the seed only | Where the platform computes features outside a browser, it is required. Elsewhere it is a convenience | | Debug code is stripped from the submitted build | Where code is stored unminified and readable onchain, formatting is part of the work | | Never claim "it's deterministic" without testing it, and without stating the scope | Never | **The firm part:** breaking a default must be the artist's explicit choice, never an accident. Point out the deviation, explain what it costs, then do what the artist asked. Do not refuse and do not lecture. ## Ethics - Never reproduce a named living artist's signature work. Techniques are shared heritage; a body of work is not. Offered "make me a <artist>", decline that framing and offer the underlying technique instead. - Credit the algorithm, the shader, the palette you borrowed. - Check that a library's licence survives being written onchain forever. - Be straight about what AI did: writing the code and generating the image are different claims. Full version: `references/ethics.md`. ## Platforms: read the docs, every time Platform APIs change and the fiches in `references/platforms/` deliberately hold no versions, no numbers and no field names — only the stable mental model, the canonical URLs and the questions to ask. So the sequence is always: **open the fiche → fetch the URLs it lists → then write code.** Never write platform-specific code from memory, and never from the fiche alone. If a doc is unreachable, say so and flag what could not be confirmed. | Target | Fiche | |---|---| | Art Blocks (incl. Engine / Flex) | `references/platforms/artblocks.md` | | 256ART | `references/platforms/256art.md` | | Verse | `references/platforms/verse.md` | | Highlight | `references/platforms/highlight.md` | | Plottables (pen plotter, AB Engine) | `references/platforms/plottables.md` | | bootloader.art — ask *which* bootloader | `references/platforms/bootloader.md` | | Self-hosted / no platform | `references/platforms/self-hosted.md` | | Artpoint — art on screen, you deliver a video, not code | `references/platforms/artpoint.md` | | Undecided between two | `references/platforms/comparison.md` | ## Where to look | When the question is about… | Read | |---|---| | Seeding from a hash, PRNG choice, sub-streams, distributions, things that silently break reproducibility | `references/determinism.md` | | Output that changes with canvas size, stroke weights, noise frequency, element density, print or plotter output | `references/resolution.md` | | Designing traits, rarity tables, weights, distribution that came out wrong | `references/features.md` | | Originality, attribution, licences, disclosing AI use | `references/ethics.md` | | Debug panel, param tweaking, keyboard shortcuts, PNG and video export, contact sheets | `references/tooling.md` | | "Is my sketch actually deterministic?", pre-mint checking, what a test can and cannot prove | `references/verification.md` | | Techniques, tutorials, libraries, inspiration, what other artists use | Fetch https://github.com/camilleroux/awesome-generative-art — a maintained list; do not paraphrase it from memory | ## Runnable scripts Two scripts ship with the plugin and run **in place** — never copy them into a project. They need Playwright installed in the artist's project and print the install command if it is missing. Contract and details: `references/verification.md`. | Command | Does | |---|---| | `node "$CLAUDE_PLUGIN_ROOT/scripts/check.mjs" <dir>` | Determinism: repeatability, distinctness, global-state contamination, feature stability | | `node "$CLAUDE_PLUGIN_ROOT/scripts/render.mjs" <dir> --hash 0x…` | One PNG — render it, then **look at it** with Read before judging any visual change | | `node "$CLAUDE_PLUGIN_ROOT/scripts/render.mjs" <dir> --grid N` | Contact sheet, hash + features under each tile | | `node "$CLAUDE_PLUGIN_ROOT/scripts/render.mjs" <dir> --census N --edition-size E` | Real feature distribution vs the rarity table | | `node "$CLAUDE_PLUGIN_ROOT/scripts/render.mjs" <dir> --batch N --size 2400` | N individual full-res PNGs, hash-named — portfolio/print export | When asked to change how a piece looks, close the loop yourself: edit → render one hash → Read the PNG → judge → adjust. Do not describe a visual change you have not looked at. ## Workflow 1. **Target first.** Which platform? If undecided, `references/platforms/comparison.md`. Once decided, open its fiche and fetch its docs before writing anything. 2. **Decide the rarity table before coding.** It is much harder to retrofit. 3. **Build.** Defaults above; deviations discussed, not silently taken. 4. **Look at many outputs, not one.** A single good render proves nothing — `render.mjs --grid` exists for this. 5. **Check before minting** — `check.mjs`, then `references/verification.md` for what the green result does and does not prove.
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.