Independent test · single operator · August 2026

What survives when a
design system meets Omma

One prompt, held constant. One variable changed at a time. Every generated project measured with a script rather than read by eye. This is what happened when a real, published design system was fed into the canvas — where it transferred completely, where it stopped, and what one sentence in a prompt turned out to be worth.

Naming fidelity, with spec
0%

Sixty tokens declared, sixty carrying the system's own names. Zero generic substitutes, including two private namespaces a generator would normally normalise away.

Compact file vs full spec
×0

A 5.6 KB instruction file matched a 103 KB specification on naming and beat it on value fidelity. Eighteen times less input, same transfer.

Lines needed to bind the 3D layer
0

One sentence of intent moved the scene from hand-copied values to live resolution of the page's custom properties. A thirty-line contract was not required.

Method

Nothing below is an impression.

One canonical prompt, frozen at the first run and never edited. It describes a screen, not an aesthetic: no colours, no fonts, no measurements. Changing it would break the comparison, so it did not change.

One variable per run. Between two arms, only the specification loaded changes, or only one clause added to the prompt. Each arm runs in a fresh project.

Code is the evidence. A screenshot tells you whether it is beautiful; only the code tells you whether the token arrived. Every run was exported and analysed.

Six dimensions, measured: token purity, naming fidelity, value fidelity, component architecture, motion discipline, and how far the token system reaches into the 3D layer — plus one metric added for this test, described below.

The scale was anchored before the runs, not after. A floor: a generic hero built with no specification at all. A ceiling: the design system's own source. The ceiling matters — 100% does not exist even there, and an output read against perfection is read wrong.

What transferred

With the specification loaded, the system reaches the DOM and the stylesheet essentially intact. The two private, non-obvious namespaces survived with their own names — the case where a generator normally reaches for --gray-* and --primary.

Dimension Floor · no spec Baseline · no spec With spec Ceiling · the system's own source
Naming fidelity 0% ~25% 100% 96.8%
Token purity 7.4% 22.8% 70.3% 74.3%
Value fidelity 0% 0% 96.3% 68.3%
Canonical component classes 1 / 15 2 / 15 7 / 15 13 / 15
Off-system easing curves 1 / 1 0 / 4 0 / 2
Token reach into the 3D layer 0% 0% 0%

The last row is the interesting one, and the rest of this document is about it.

A compact file matched a large one

The same prompt was run against the full 103 KB specification and against a 5.6 KB instruction file derived from the same system. Naming fidelity: 100% both times. Value fidelity: 96.3% for the large file, 100% for the compact one. Token purity within two points. The compact file carried fewer component classes — five against seven — which is the one place the extra size paid for itself.

Read as a product fact: the runtime does not need a large document to honour a design system. It needs the right small one.

Repeated one week later

The identical prompt with the identical file was run again seven days after the first measurement, in a fresh project. Naming fidelity 100% both times; both private namespaces surviving with the same counts, twelve and eight; token purity within six tenths of a point; the 3D layer behaving identically. On this arm the generation behaviour is unchanged over the week.

The metric this test had to add

No existing measurement covered it, because no previous target had a layer the stylesheet cannot reach. Three states, and the distance between the second and the third is the whole subject:

State 01

None

The generator ignores the system outside the CSS. The 3D layer is invented from scratch.

State 02

Mirror

It recognises that the system should govern that layer, and hand-copies the values into JavaScript. Correct today, drifting tomorrow.

State 03

Live

It resolves the values at runtime from the page's own custom properties. Change a token in the stylesheet and the scene changes with it.

Without any 3D instruction, the default is mirror

With the specification loaded and nothing said about the 3D layer, the generator understood that the system was meant to govern the scene — it quoted the system's own colour-budget law to decide which single object earned an emissive surface — and then wrote a block of colour constants at the top of the scene file, annotated “token mirror, kept in sync”. Thirty-four values, all exact, zero drift.

The comment is the admission: it knows it is a copy, and it knows the copy will rot. The specification forbids precisely this. The rule was broken because no channel existed — not one line of the scene read a custom property. That is a wiring gap, not disobedience.

In a later run the same behaviour appeared without the annotation: the palette values simply inlined into the three.js calls, correct and untraceable. Same fidelity, no admission.

What each instruction bought

Five arms, same specification, same canonical prompt, each with a different amount of instruction about the 3D layer. Everything to the right of “binding” is the price paid for it.

What the prompt added Binding Naming fidelity Values copied by hand Reduced motion in the scene
Nothing mirror 100% 6 absent
A plain-language request naming the tokens mirror, reasoned 15 absent, but offered
One sentence of intent: never copy a token value into JavaScript live, and reactive 90.4% 11 absent
The mechanism named: resolve via getComputedStyle live 74.2% 1 absent
Three clauses: no copies, read the system's own names, honour reduced motion live 100% 0 present

One sentence was enough

Asking only for the outcome — never copy a token value, if a token changes the scene changes with it — produced a live resolver, a mutation observer that rebuilds the scene when the stylesheet or the colour scheme changes, a correct cubic-bézier solver that parses the curve out of the CSS string rather than retyping it as numbers, and a radius token driving the roundness of the geometry. None of that was asked for by name. The mechanism did not need teaching. Only the intent did.

The price is the vocabulary

Binding forces a choice the plain generation never had to make: which names do I read? And the generator chose its own. One run invented a seven-name semantic layer — --color-accent, --color-card, --color-border — while the design system already ships exactly those roles under its own names, none of which appear in the output. Another went further and added a twelve-name scene layer on top: fog, grid, particle, ring, and three scene durations.

Both layers are defined in terms of the system's real primitives, so the lineage is intact. But the scene is now bound to names the design system does not ship. Dropped into the real page, it would resolve nothing and fall silently back to the literals kept as defaults. Binding and naming are two primitives, not one — and today neither is the default.

The third arm closes it: with the naming clause added, naming fidelity returns to 100%, the binding stays live, and every hand-copied value disappears — including the fallbacks. That run also throws a typed error when a token is missing instead of substituting a literal, which was the third clause, and honours reduced motion inside the WebGL layer, which was the second.

Four reproducible observations

3 of 3 Agent file deletions do not persist

Three times, in three separate projects, a stray entry file at the project root broke the preview by loading an HTML file as a JavaScript module. Three times the agent diagnosed the cause correctly, in detail, and stated it had deleted the file. Three times the file was still there afterwards — once with the deletion claim on screen next to an export that still contained it, still carrying the exact line the message said had been removed.

Moving the entry files to the root worked immediately, both times it was tried. This reads as a scaffold behaviour, not a model failure: the reasoning is right and the delete is issued, and something downstream does not honour it. The cost lands on the user as rounds, credits, and the impression that the agent is not telling the truth.

4 occurrences Reported work and the diff disagree

Beyond the deletions above, one round returned a six-section report describing a 3D refactor in specific technical detail — emissive intensity, light colours, easing sources — against a scene file that was byte-identical to the previous version.

The significant part is that the reasoning in that report was correct, and better than correct: the observation that shadow tokens encode blur and spread rather than a scalar distance, so there is no CSS property from which to read a depth, is true and not obvious. The model understood the contract. Then it reported having applied it. Asking for verbatim evidence — paste back the actual lines, not a summary — was what converted the next round from a report into a real refactor.

5 of 5 Reduced motion never reaches the WebGL layer

Across every run where it was not explicitly requested, there was no prefers-reduced-motion handling in the scene — with objects in perpetual rotation, orbiting rings and satellites. The CSS-level kill switch is emitted correctly and cannot reach WebGL. For a viewer with vestibular sensitivity, that hero cannot be turned off.

Nobody in this category appears to be handling it. Whoever does it first has a differentiator that enterprise buyers understand without explanation.

across sessions One three.js clock bug recurs

The animation loop calls the elapsed-time accessor before the delta accessor, which zeroes the delta — measured at 0.000024 radians instead of 0.302 after two and a half seconds. Anything driven by delta barely moves. It appeared in the first session, was corrected, reappeared in the next project, and appeared again a week later in a fresh project. It is not that it does not learn between projects: it does not learn between sessions.

What Omma does that its category does not

It tries to obey the design system inside the 3D layer. No other builder in this cluster attempts that at all. Given the specification and no 3D instruction, it reached for the system's own colour-budget law to decide which surface earned an emissive, and then went looking for a way to get the values there. What was missing was the channel, not the intent — and by the end of the afternoon the channel existed, built by the runtime itself, from one sentence.

Asked to bind, it also proposed a vocabulary for the part of a design system that does not exist yet: the scene-level roles a 2D system has never needed to name. It did that twice, independently, converging. That is a product behaviour worth knowing about, and the product currently tells nobody it is there.

Elsewhere the output is genuinely good: semantic, readable CSS instead of utility soup; labelled inputs with correct autocomplete and required attributes, so the accessibility floor on forms holds; a real project with declared dependencies that builds cleanly with its own toolchain; and compositional taste that does not read as generated.

Questions

Perimeter and rigour

Tested: generation, fidelity to a loaded specification, the 3D layer, and the structure of the exported project. Not tested: the editor, the canvas, the component library, collaboration, or mobile and XR export. Every claim above is bounded by that perimeter.

One run per condition, uncontrolled, temperature and seed unknown; one arm was replicated a week apart and is noted as such. The floor and ceiling are single measurements, not distributions. These are observations of what happened on a specific afternoon with a specific system — this is what I got when I did this, not Omma does X. Every number here is reproducible from the exported code with the same script.