PATINA docs

Publish a render pack

A pack is a self contained folder that turns artifact state into a picture. Anyone can publish one, and nobody needs permission.

What you will know after this page
  • What goes in the folder, and why every font and asset ships inside it rather than being fetched.
  • Which five manifest fields are promises you can be held to, and why network_access is false in every honest pack.
  • The shape of the render function, and why it must fail loudly instead of drawing Raw for missing state.
  • The five kinds of sample a pack ships at a minimum, and which pair proves it tells the truth about a reset.
  • The eleven checks to run before publishing, from comparing hashes to contrast.

Layout

Seven entries, and everything the render reads while drawing is one of them.

my-pack/
  manifest.json      what this pack is and what it claims
  render.js          the pure render function
  tiers/             one asset set per tier, if the pack uses assets
  fonts/             any font the pack needs, bundled not linked
  samples/           rendered examples with their inputs
  README.md          how to use it, and the derivation the pack uses
  LICENSE

Everything the render needs ships inside the folder. A pack that fetches an asset from someone else's server stops working the day that server does, and it stops being verifiable long before that.

Manifest

The manifest is the pack's claim about itself, and the five fields below are the ones anyone can check against your samples. Write it once the render works, so it describes what the code does rather than what you meant. The eight tier names are spelled the way the tiers page spells them.

{
  "name": "my-pack",
  "author": "who made it",
  "license": "SPDX identifier",
  "protocol_id": "PTNA",
  "inputs": ["artifact_id", "founding", "status", "depth", "tier", "rings", "height"],
  "outputs": ["svg"],
  "tiers": ["Raw", "Sheen", "Cast", "Verdigris", "Umber", "Bronze", "Oxide", "Elder"],
  "deterministic": true,
  "network_access": false,
  "reduced_motion": "static output only",
  "derivation": "how per artifact variation is derived from artifact_id"
}
Fields that carry a promise.
FieldPromise
inputsThese are the only inputs the render reads. Listing an input you do not read is sloppy. Reading one you did not list is dishonest.
deterministicSame inputs produce identical output bytes
network_accessFalse, always. A render that needs the network is not a render pack.
derivationAnyone can reproduce your variant choices from the artifact id
tiersAll eight names, spelled as the protocol spells them

The render function

// Returns a string of SVG, or a buffer, depending on the declared output.
// No network, no clock, no randomness, no globals.
function render(state) {
  // state: { artifact_id, founding, status, depth, tier, rings, height }
  // ...
  return svg;
}
  • Take the whole state object and read only what you declared.
  • Do not mutate the input.
  • Handle every case: depth zero, Elder, relic, zero rings, many rings.
  • Fail loudly on missing fields rather than substituting a default. A render that quietly draws Raw when it was given nothing is a render that lies.

Samples are part of the pack

Ship at least these, each with the exact input JSON that produced it:

  1. One per tier, Raw through Elder.
  2. A founding artifact and a non founding one at the same tier.
  3. A relic.
  4. An artifact with zero rings and one with at least three.
  5. An artifact that is deep now, and one that was deep once and is Raw now.

The last pair is the important one. It shows whether the pack tells the truth about a reset.

Checks before you publish

Eleven checks, run against every sample rather than a favourite one. The first four are the determinism checks, and they are the reason anyone can trust a pack they did not write.

Run all of these. A pack that fails any of them is not ready.
CheckHow
DeterminismRender each sample twice, compare hashes
PortabilityRender on a second machine, compare hashes
OfflineDisconnect the network and render every sample
No external requestsGrep the source for URLs, and watch the network panel while rendering
Tier legibilityPut all eight side by side. Each must be tellable from its neighbours.
Relic legibilityA relic must not be mistakable for a live artifact
Height stampPresent and readable at the smallest size the pack supports
ContrastAny text passes AA against its background
Reduced motionAnimation is optional, and the static form carries the same facts
Text alternativeEvery meaningful element has one
SizeReasonable for a share card. State the output size in the README.

Publishing

  1. Put the folder in a public repository with its license.
  2. Include the manifest, the samples, and their inputs.
  3. State plainly that the pack is a view of the state, not the state, and that the protocol does not endorse it.
  4. If you change the pack, publish it as a separate pack rather than silently changing the pictures people already shared. A render is a claim about state at a height, and rewriting it later breaks that.
No version labels in the name

Name a revised pack for what changed, not with a number. The program has no version labels in names, folders, or URLs anywhere, and packs follow the same rule.

What a pack may never do

The full list is on Rules that keep renders honest. The short form: a pack may make an artifact beautiful, and may not make it look like something it is not.