p5loom

Controls

A spec can declare controls: a slider, a toggle or a select. The library draws the inputs next to the picture and redraws when one changes. Inputs are ordinary labelled form controls, so they work with a keyboard, and every change is announced to a screen reader once the reader stops moving.

Try itMove the slider, switch the horizon. The link updates.
A sunrise with a seed, a star count, a star colour, a theme and a horizon toggle.
Declare a controlThree types
{
  "version": 1,
  "seed": 7,
  "canvas": { "width": 640, "height": 400 },
  "controls": [
    { "id": "dots", "type": "slider", "label": "Stars", "min": 10, "max": 600, "step": 10, "value": 200 },
    { "id": "horizon", "type": "toggle", "label": "Show the horizon", "value": true },
    { "id": "tint", "type": "select", "label": "Star colour", "options": ["accent", "muted", "fg"], "value": "muted" }
  ],
  "elements": [
    { "type": "random-dots", "count": { "control": "dots" }, "fill": { "control": "tint" }, "radius": [0.6, 1.8] },
    { "type": "line", "points": [[0, 0.8], [1, 0.8]], "stroke": "accent", "weight": 1.5, "when": "horizon" }
  ]
}
TypeFieldsValue
slidermin, max, step, valueA number from min to max, snapped to the step.
togglevaluetrue or false.
selectoptions (2 to 12 strings), valueOne of the options.

Every control has an id (lowercase letters, digits and hyphens), a label of up to 80 characters, and a type from slider, toggle, select. A spec can hold up to 12.

Bind a field{ "control": "id" }

A field follows a control by naming it. The control's whole range must fit the field, so a coordinate can only follow a slider that runs inside 0 to 1. The validator says so when it does not.

FieldFollows
x, y, w, h, ra slider inside 0 to 1 (r on a scatter is pixels, up to 100)
weighta slider inside 0 to 64
counta whole-number slider with a minimum of 1
seeda whole-number slider from 0 to 4294967295
fill, strokea select whose options are all theme tokens
themea select whose options are light and dark
when on an elementa toggle: the element is hidden while it is off
Share the stateA link that reproduces this picture

Pass a shareKey to mount (or a share-key attribute) and the values that differ from the defaults are written to the address bar as one parameter, p5loom-<key>. Opening that link draws the same picture. A hand-edited value is checked against the control's range and step, so a link can never set a value the spec would refuse.

From codemount returns a handle
import { mount } from "p5loom";

const handle = mount(element, spec, {
  p5,
  shareKey: "revenue",                       // optional: put the values in the address bar
  onChange: (values, id) => save(values),    // optional
});
handle.set("dots", 400);                     // moves the slider, redraws, announces, updates the link
handle.values();                             // { dots: 400, horizon: true, tint: "muted" }
handle.plan();                               // the drawing plan for the current values
handle.destroy();

Each change also dispatches a p5loom-change event on the picture, with the values as its detail.

Keep it reproducibleValues are data too

The picture is a function of the spec and the control values, and nothing else. Values are snapped to the step and clamped to the range. Two readers with the same spec and values see the same picture, and an AI client can reproduce any state a person left the page in.