Page builder
Nothing on the page yet. Describe one above, or open a code-composed example and use it to try the live edit path.
It knows five blocks: an intro paragraph, a card, a callout, a stat tile and a form. Say "side by side" and it puts two of them in a row.
Nothing in the closed set matched that. Try asking for an intro, a card, a callout, a stat or a form.
The composition, as JSON
The artifact rather than a description of one, and what Open reads back in. A form block's data is what matchSpec returned, one level in. The page reads its own state back out of here on load, which is how a static host can tell a fresh visit from one a server already composed. The exported file is this plus one line naming GRAIN, because a file you take away should say where it came from and a pane showing you your own state does not need signing.
Taking the page away
Three files off the one composition, and they answer three different questions. The JSON is the composition, and it is the only one that comes back: Open reads it and the page is the page again, which makes it the file to keep. The page is a whole document you can open or send, and it links this site's stylesheets rather than copying them, so it needs the network to look like what you built. The tags are the markup on its own, for pasting into a page that already loads GRAIN.
The tag source is the interesting one, because it is not a translation. Every class in it belongs to GRAIN, and a molecule in GRAIN is a documented class convention a page author writes by hand, so what a composed page produced and what a developer would have typed are the same markup. The builder did the choosing, not the inventing. The one thing that does not travel with it is the six-column grid the widths key off, which is this page's own CSS rather than GRAIN's, so it ships with the page export and is named in a comment on the tag export.
Both HTML forms come out without the builder's fingerprints on them: the block addresses, the block ids, and the binding directives the browser uses to fill a cloned block. An exported page ships no dispatcher, so an address on it would advertise an operation nothing can perform, and that is the one mistake this whole design keeps saying out loud that it will not make.
All three carry the GRAIN byline, and two of them carry it twice. The footer is the half you can see and the half someone will eventually delete; the comment at the top of the file and the attribute on the root element are the half that survives that. They are named here rather than hidden, because a mark you have to find is a different thing from a mark you are told about.
Open replaces the canvas rather than adding to it, which is the opposite of what a prompt does and the reason the button says Open rather than Import. A hand-edited file gets the same treatment a description gets: anything this build cannot render is listed by name under Can't build, and the rest of the page still comes up.
What it builds, what it edits, and where it stops
Describe a page and press Build it. The code-owned matcher gives the examples a stable preview; Build it asks the local model to add blocks and draft text into registered fields. The model has not earned your trust with copy yet. Check the canvas before you use or export anything. On capable devices its first run downloads about 875 MB; other devices use the 350 MB model. Both cache files on this device, and your prompt stays here.
Two WebGPU checks on October 11, 2026, each scored 2/8 with the 0.5B model. The 1.5B model scored 6/8 on October 11 and in one October 12 run. After its timeout increased to 60 seconds, another October 12 run scored 5/8. Width and move requests passed, but both deletion cases failed; the copy-edit refusal passed. The first request took 50 seconds. The model chose the first card instead of the second, and the guard refused it. The draft omitted Utrecht and added unrequested marketing phrases. A warm repeat in the earlier run included Utrecht but still added unsupported phrases. These small samples are not success rates. One October 11 scenario took 40.5 seconds from a fresh browser cache, including model loading and generation. The browser profile occupied 1.6 GB on disk after that run.
A focused October 13 recheck applied both deletion requests after the Builder showed and corrected the model's wrong target for the second card. The draft still omitted Utrecht. This 2/3 result measures the reviewed page workflow, not the model's own accuracy. In a later eight-case run, both deletion requests applied and the review proposal corrected the relative second-card request's operation and target. The explicit-ID request passed as proposed by the model. The run scored 7/8, with width, move, and copy-edit refusal cases also passing. The generated draft included unsupported marketing claims. First answers can take over a minute, and requests stop after 75 seconds.
On October 13, a fresh eight-case WebGPU run scored 7/8: the six edit requests and the copy-edit refusal passed. The relative deletion passed after review corrected the model's proposed span change on the wrong block; the model proposed the exact-ID deletion correctly. The draft kept the business name and product but omitted Utrecht and added unsupported bread claims. Two warm repeats of the same prompt scored 0/1 and 1/1. The first omitted Utrecht again. The second included it, but added unsupported claims and an unrequested callout. All three drafts added details the brief did not support, and only one retained the supplied city. The score checks fact retention, not whether generated claims are true. The review panel keeps the original brief beside editable draft text, but the model has not shown reliable writing.
Each edit appears as a proposal first. You can approve or cancel it, and the Builder checks the canvas again before it reports a change. When one edit verb, value, and live block are clear in your request but the model proposes a different action or target, the proposal shows both versions and uses your explicit request. Ambiguous references keep the model's proposal for review, and missing targets are refused. If the model declines or returns an unusable answer for a request whose supported edit and live target are explicit, the Builder can offer a disclosed, request-based recovery for approval. These safeguards make the edit path safer; they do not prove the model resolved the request correctly. The block rail remains the dependable way to resize, move, or remove something. The model may add new blocks; it cannot revise copy already on the canvas because GRAIN has no registered operation for that.
for (const block of composition.blocks) render(block.component, block.data, block.props)
The Can't build list holds two different answers. A shell, a side rail or a top bar is the frame a page sits in rather than content it can hold, so it is refused on principle. Everything else is a gap with a date on it: the component exists, it is documented, and the block set has not grown a template for it. A picture is the honest example, because a generated page has no real image to point at and an invented one is a broken picture with a confident name.
About the form on this page
The form on the canvas is one block, and its insides are the same closed set of fields this page used to be entirely about. Ask for a box to tick and you get one, and the desk can tick it: until 2026-08-14 the vocabulary had no verb that could, because the only verb for a form control writes the control's value, and a tick box's value is what the form submits rather than whether it is ticked, so the write landed, reported success and moved nothing. A tick box carries a different kind of address for exactly that reason.
Fields are grouped by kind, text fields first, then a message box, then choices, then anything to tick. Interleaving them in the order you asked is not supported, on purpose, a v1 limit. The address each field carries sits on the control itself, so the desk writes into a generated field with nothing registered by hand. That was not true when this page was first built: the atoms addressed the label around the control, a label has nothing to write into, and the write was dropped without a word. Building this found it and the atoms were fixed, which is the whole argument for building a demo rather than describing one.
Nothing here submits anywhere. This is a static site, there is no submit control on a generated form, and there is no verb in the vocabulary that could press one.
Examples
These links carry the prompt in the address and open a preview composed by the code-owned matcher. Press Build it to ask the on-device model to add blocks when WebGPU is available. Capable devices download about 875 MB of model files for the Builder; other devices use about 350 MB. Files stay cached on this device. Your prompt stays here, and the model's choices and copy may still need work.
AI helps me write and review. I direct the work, test the result, and answer for what ships. How I work with AI →