Introduction
Loom is a runtime for interfaces that AI is allowed to change.
A page is not a file of components here. It is a tree — a validated data structure naming primitives you registered, with props those primitives declared. AI never writes that page. It proposes a delta: an ordered list of discrete operations against the tree you already have. A pure function called the Gate decides which of them are allowed, the runtime applies what survives, and the result is rendered.
Everything else on this site follows from those two sentences.
What the next hour looks like
The pages below, in that order, are the shortest way through this site. At the end of them somebody has asked your running page for a change, in a sentence, and it happened — and you can say exactly why it was allowed.
Installation
The package is in your project, and one call has proved it — you asked for the starter vocabulary and got it back, or got a sentence saying why not.
2 code blocks3 min to readyou will have typed
createStarterPrimitiveRegistryYour first tree
You have written a page down as data: a heading, a sentence, the box around them. It is JSON, so you can print it, store it and read it back.
4 code blocks3 min to readyou will have typed
createTreeRendering a tree
That data is on the screen, drawn by your own React components. Nothing about the page is generated code.
4 code blocks3 min to readyou will have typed
renderLoomTreeYour first change
Somebody has asked for a change in a sentence, and it happened. You watched the ask become a written-down list of operations, get an answer, and land on the page.
2 code blocks4 min to readyou will have typed
commitIntentPrimitives and the registry
A component you built is a word anybody can now use — the first one that was not handed to you. Everything a change is allowed to say comes off that list.
5 code blocks3 min to readyou will have typed
definePrimitiveWhat the Gate decides
A change has been refused, and you know why and could have decided otherwise. This is the step people skip and then discover on a Friday.
3 code blocks5 min to readyou will have typed
gatePolicySchema
The bargain
AI that emits UI code cannot be reviewed at a useful granularity. A diff of generated JSX tells you a file changed; it does not tell you that a checkout button was added to the sidebar, whether that was allowed, who asked for it, or how to put it back.
So Loom narrows what AI may produce, and asks you to accept a bounded vocabulary in return for four things a generated file cannot give you:
- Addressable. A change names a node. "Insert a button into the sidebar" is a value, not a description of a diff.
- Reviewable. The Gate is a pure function of the proposal and a policy. The same proposal under the same policy always gets the same answer.
- Attributable. Every change carries who asked, what interpreted it, and why.
- Reversible. An undo is another proposal — computed from what the change replaced, gated like any other.
The cost is real and worth saying plainly: AI can only say things your registry has words for. If no primitive is registered for a pricing table, nothing can propose one. That is the constraint doing its job, not a gap.
Hello from a tree
Nothing here was written as markup.
Every one of these goes through the same pipeline a model’s answer would: interpreted, analysed, weighed, judged, applied, appended. The log is real and it is in this tab — reload the page and the example is back as it was.
show the treehide the tree
{
"kind": "element",
"id": "n_firsttree5",
"type": "loom.page",
"props": {
"fills": true,
"width": "readable",
"loom:theme": {
"palette": "minimal",
"fontPack": "minimal-sans",
"stylePreset": "precise"
}
},
"children": [
{
"kind": "element",
"id": "n_firsttree2",
"type": "loom.heading",
"props": {
"level": 1
},
"children": [
{
"kind": "text",
"id": "n_firsttree1",
"value": "Hello from a tree"
}
]
},
{
"kind": "element",
"id": "n_firsttree4",
"type": "loom.prose",
"props": {},
"children": [
{
"kind": "text",
"id": "n_firsttree3",
"value": "Nothing here was written as markup."
}
]
}
]
}Behavior lives in the component, never in the tree
This is the line that keeps the bargain honest, and it is worth learning before anything else.
A node says what — a heading at level one, a section with this eyebrow, a grid with three columns. It never says how. There is no event handler in a tree, no expression, no callback, no string that gets evaluated. What a primitive does when it is clicked is part of the component you registered and vouched for.
That is why a proposal is safe to gate rather than to sandbox. The worst a
delta can do is arrange your own vocabulary into a page you did not want — which
is exactly the kind of mistake a review can catch, and exactly the kind an
eval cannot.
Four operations, and no fifth
A TreeDelta is an ordered list of operations. There are four:
| Operation | What it does |
|---|---|
insert | put a new node at an address |
remove | take a node out |
move | put an existing node somewhere else |
configure | change a node's props |
A re-theme is a configure. Reordering two sections is a move. Adding a
feature to a grid is an insert. There is no operation that replaces the tree,
because a change you cannot describe is a change you cannot review.
What this site is
Every rendered example on these pages is a real tree, mounted through the real runtime, with the same registry a host would use. Nothing here is a screenshot of a page that used to work. If an example stops rendering, the documentation fails its own tests before it misleads anyone.
Where to go next
Step one of the route above is Installation, and it is the right answer for almost everybody.
Two other openings are worth knowing about. If you would rather be persuaded before you type anything, How it fits together is the whole system in eight paragraphs and What it costs you is the same system from the other side — the eight things Loom takes away. And if you are deciding what an AI is allowed to touch on a page you already care about, What AI may change is the page that answers it.