Skip to content
REMIX OPENPublish with remix allowed and others can reuse your settings on their own picture →
Rasti
$RASTITBAOpen studio
Pre-launchThe studio works today. $RASTI is not launched yet; credits for it are planned, not live.How it works →
JSON Rasti Engine · format v1

Every render is one small object you can read, link and rebuild.

The studio, the share links, the gallery and the provenance record all speak the same preset format. Scripts can fetch the library as JSON, build presets in code and open them in the studio; nobody needs a key.

The pieces

Three pieces, all open, none of them holding a key for you:

Preset object

Algorithm, pixel size, tone, palette and screen settings in one plain JSON object.

Share codes

The canonical preset, base64url-encoded, in the studio address. Opens the same look anywhere.

Read API + contract

Keyless JSON endpoints on this site and the RastiProvenanceBook on Base.

First steps

Fetch the preset library, pick one, and open its studio link:

curl -s https://rasti.live/api/presets | jq '.presets[] | {name, studio}'

Or build a preset in code and turn it into a link:

const preset = { v: 1, algorithm: "bayer4", pixel: 3, contrast: 20, brightness: 0,
  threshold: 128, palette: ["#0a0a0a", "#f2c66d"], forcePalette: true,
  serpentine: true, dot: 8, angle: 45, invert: false };

// canonical JSON: keys sorted, no spaces
const canon = "{" + Object.keys(preset).sort()
  .map((k) => JSON.stringify(k) + ":" + JSON.stringify(preset[k])).join(",") + "}";
const code = btoa(canon).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
console.log("https://rasti.live/studio?p=" + code);

Preset format

Unknown keys are dropped and numbers are clamped to their range, so a preset from anywhere is always safe to open.

KeyType · rangeMeaning
v1Format version.
algorithmfloyd · atkinson · bayer2 · bayer4 · bayer8 · halftone · threshold · pixelateHow pixels are mapped to the palette.
pixel1–32Source pixels per output pixel.
contrast-100–100Tone contrast before mapping.
brightness-100–100Tone shift before mapping.
threshold0–255Cut point (two-colour Threshold) or light/dark bias.
palette2–16 × "#rrggbb"Allowed output colours, darkest first by convention.
forcePalettebooleanPixelate only: map to the palette instead of keeping source colours.
serpentinebooleanDiffusion scans alternate direction per row.
dot3–32Halftone cell size.
angle0–90Halftone screen angle in degrees.
invertbooleanSwap light and dark before mapping.

Share codes

A share code is the canonical JSON (sorted keys) in base64url without padding. The default preset becomes:

/studio?p=eyJhbGdvcml0aG0iOiJhdGtpbnNvbiIsImFuZ2xlIjo0NSwiYnJpZ2h0bmVzcyI6MCwiY29udHJhc3QiOjEyLCJkb3QiOjgsImZvcmNlUGFsZXR0ZSI6dHJ1ZSwiaW52ZXJ0IjpmYWxzZSwicGFsZXR0ZSI6WyIjMGIwYjBjIiwiI2YxZWZlNyJdLCJwaXhlbCI6Miwic2VycGVudGluZSI6dHJ1ZSwidGhyZXNob2xkIjoxMjgsInYiOjF9

Remix links add the parent: &parent=<gallery id> for a gallery work, and &ph=<file hash>&pc=<wallet> for an on-chain record. The settings hash in a provenance record is the SHA-256 of the same canonical JSON.

Endpoints

JSON over HTTPS on this site, no key, rate limited per network. A refused call answers { "error": "…" } plus a matching status code.

PathWhat you get
GET /api/presetsAlgorithms, palettes and the preset library, each preset with its share code and studio link.
GET /api/gallery?limit=60Published works, newest first: title, note, preset, remix flag, hashes, creator, preview. Hidden works are left out.
GET /api/chainLive Base facts: block, block time, gas price, ETH price, records in the book, token data once launched.
GET /api/nonce?address=0x…One-time nonce for a signed write (10 minutes).
POST /api/recordsSigned gallery writes: publish, remove, report. The wallet signs a plain-text message (personal_sign) that includes the SHA-256 of the exact payload.

Provenance contract

RastiProvenanceBook on Base (chain ID 8453): not deployed yet. No owner, no fee, no upgrade. Functions:

record(bytes32 artworkHash, bytes32 settingsHash, bytes32 parentHash,
       address parentCreator, uint8 license) returns (uint256 id)

recordOf(bytes32 artworkHash, address creator)
  -> (bool exists, bytes32 settingsHash, bytes32 parentHash, address parentCreator,
      uint64 recordedAt, uint64 blockNumber, uint8 license, uint256 id)
claimCount(bytes32 artworkHash) · claimantAt(bytes32 artworkHash, uint256 i)
entryCount() · entryAt(uint256 id) · creatorCount(address) · creatorEntryAt(address, uint256 i)

event ArtworkRecorded(uint256 indexed id, bytes32 indexed artworkHash, address indexed creator,
                      bytes32 settingsHash, bytes32 parentHash, address parentCreator, uint8 license)

Rules the contract enforces: a non-zero file hash; license 0 or 1; one record per wallet per file; a parent must exist under the named wallet and allow remixes; a work cannot be its own parent.

Hashing a file

The file hash is plain SHA-256 of the file bytes, so any tool gives the same answer as the studio. The verify page does it in your browser.

shasum -a 256 rasti-work.png          # macOS / Linux
// browser
const hash = "0x" + [...new Uint8Array(await crypto.subtle.digest("SHA-256", await file.arrayBuffer()))]
  .map((b) => b.toString(16).padStart(2, "0")).join("");

Limits

Gallery previews

WebP, at most 720 px a side and 132 KB.

Publishing

12 works per wallet per day, plus per-network limits.

Moderation

One counted report per wallet and per network per work, wallets with Base history only; hidden after 3.

Records

Gas only. Public and permanent once included in a block.