MorphScreen against OpenUI, measured
OpenUI is the open-source generative UI library we measure ourselves against. Both formats get the same briefs, the same models and providers, and the same rules for what counts as valid. Of 63 rows with numbers for both, MorphScreen is ahead on 51, OpenUI on 6, and 6 are even. 23 rows are ours alone so far.
Scoreboard of 2026-10-25: commit a2eaaf1+changes, prompt f6ec7f010086. Every row comes from the benchmark in our repository, run again whenever the prompt or the core changes.
Every row
| What | MorphScreen | OpenUI | Ahead | How it was measured |
|---|---|---|---|---|
| System prompt, tokens | 1,340 | 14,041 (chat library), 5,017 (benchmark's) | MorphScreen | o200k on the prompt each format's model sees |
| Valid answers, Nemotron 3 Ultra, 46 briefs x 4 | 89.7% (165/184) | 81.0% (149/184) | MorphScreen | generative-ui-bench rules; ours prompt v09, OpenUI's benchmark prompt (its answers from the v05 run; its prompt didn't change) |
| Briefs valid every time, Nemotron 3 Ultra, 46 briefs x 4 | 31 of 46 | 26 of 46 | MorphScreen | all repeats of a brief complete |
| Blank answers, Nemotron 3 Ultra, 46 briefs x 4 | 0 | 0 | Even | the model wrote nothing usable |
| Answer tokens, Nemotron 3 Ultra, 46 briefs x 4 (mean) | 1,340 | 1,492 | MorphScreen | o200k on the answer text |
| Valid answers, small model (Nemotron 3 Nano 30B-A3B), 46 x 2 | 66.3% (61/92) | 31.5% (29/92) | MorphScreen | the closest free model to Gemma 4 26B (Gemma's free endpoint was rate-limited upstream) |
| Briefs valid every time, small model (Nemotron 3 Nano 30B-A3B), 46 x 2 | 21 of 46 | 8 of 46 | MorphScreen | all repeats of a brief complete |
| Blank answers, small model (Nemotron 3 Nano 30B-A3B), 46 x 2 | 0 | 4 | MorphScreen | the model wrote nothing usable |
| Answer tokens, small model (Nemotron 3 Nano 30B-A3B), 46 x 2 (mean) | 454 | 852 | MorphScreen | o200k on the answer text |
| Valid answers, DeepSeek V4 Flash, 46 briefs x 4 | 92.9% (171/184) | 91.8% (169/184) | MorphScreen | generative-ui-bench rules, the model's own reasoning left on as the benchmark has it; both formats through OpenRouter on the same providers (GMI Cloud first, then Relace, then Baidu), 2026-10-23 |
| Briefs valid every time, DeepSeek V4 Flash, 46 briefs x 4 | 33 of 46 | 31 of 46 | MorphScreen | all repeats of a brief complete |
| Blank answers, DeepSeek V4 Flash, 46 briefs x 4 | 4 | 3 | OpenUI | the model wrote nothing usable |
| Answer tokens, DeepSeek V4 Flash, 46 briefs x 4 (mean) | 896 | 1,020 | MorphScreen | o200k on the answer text |
| Valid answers, DeepSeek V4 Flash, reasoning off, 46 briefs x 4 | 89.1% (164/184) | 84.8% (156/184) | MorphScreen | the same, with reasoning off for both formats (roadmap A2: whether reasoning off should be the API's default for this model) |
| Briefs valid every time, DeepSeek V4 Flash, reasoning off, 46 briefs x 4 | 31 of 46 | 26 of 46 | MorphScreen | all repeats of a brief complete |
| Blank answers, DeepSeek V4 Flash, reasoning off, 46 briefs x 4 | 0 | 0 | Even | the model wrote nothing usable |
| Answer tokens, DeepSeek V4 Flash, reasoning off, 46 briefs x 4 (mean) | 1,140 | 932 | OpenUI | o200k on the answer text |
| Valid answers, DeepSeek V4 Flash, reasoning off, our Q12 prompt, 46 x 4 | 89.1% (164/184) | 84.8% (156/184) | MorphScreen | ours with the prompt of 2026-10-24 (photos where people want to see things, specific and never linked from another site; maps with coordinates; sliders with units), OpenUI's answers from the run above (its prompt didn't change) |
| Briefs valid every time, DeepSeek V4 Flash, reasoning off, our Q12 prompt, 46 x 4 | 31 of 46 | 26 of 46 | MorphScreen | all repeats of a brief complete |
| Blank answers, DeepSeek V4 Flash, reasoning off, our Q12 prompt, 46 x 4 | 0 | 0 | Even | the model wrote nothing usable |
| Answer tokens, DeepSeek V4 Flash, reasoning off, our Q12 prompt, 46 x 4 (mean) | 1,079 | 932 | OpenUI | o200k on the answer text |
| Valid answers, DeepSeek V4 Flash, reasoning off, prompt v0.11, core alone, 46 x 2 | 92.4% (85/92) | 87.0% (80/92) | MorphScreen | ours with prompt v0.11 (2026-10-26: answers to use, a Button with ask under each option, photos asked for only when the app gives a photo key), the core's components only, served by Baidu; OpenUI's answers from the reasoning-off run (its prompt didn't change) |
| Briefs valid every time, DeepSeek V4 Flash, reasoning off, prompt v0.11, core alone, 46 x 2 | 39 of 46 | 35 of 46 | MorphScreen | all repeats of a brief complete |
| Blank answers, DeepSeek V4 Flash, reasoning off, prompt v0.11, core alone, 46 x 2 | 0 | 0 | Even | the model wrote nothing usable |
| Answer tokens, DeepSeek V4 Flash, reasoning off, prompt v0.11, core alone, 46 x 2 (mean) | 1,003 | 913 | OpenUI | o200k on the answer text |
| Valid answers, DeepSeek V4 Flash, reasoning off, answers to use (Q13), every pack (the API's default), 46 x 2 | 87.0% (80/92) | 87.0% (80/92) | Even | the same prompt with every pack, as the API and the SDKs now load unless an app names its packs: more components to use, more ways to slip (chart data, tabs left open); repair takes them on |
| Briefs valid every time, DeepSeek V4 Flash, reasoning off, answers to use (Q13), every pack (the API's default), 46 x 2 | 34 of 46 | 35 of 46 | OpenUI | all repeats of a brief complete |
| Blank answers, DeepSeek V4 Flash, reasoning off, answers to use (Q13), every pack (the API's default), 46 x 2 | 0 | 0 | Even | the model wrote nothing usable |
| Answer tokens, DeepSeek V4 Flash, reasoning off, answers to use (Q13), every pack (the API's default), 46 x 2 (mean) | 1,268 | 913 | OpenUI | o200k on the answer text |
| Answers with something to use, before Q13 | 53 of 96 (55%); 1 such components an answer | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 96 answers to 14 everyday questions (bench/briefs/everyday.ts), before Q13; a quiz, flashcards, a map, a carousel, a button, a form or a what-if (follow-ups, tabs and details counted apart) |
| The component a question calls for, before Q13 | 12 of 16 (75%) | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 96 answers to 14 everyday questions (bench/briefs/everyday.ts), before Q13; Products for shoes to buy, a Quiz or Flashcards for a lesson or phrases, a Compare for two phones |
| Valid answers to everyday questions, before Q13 | 80 of 96 (83%) | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 96 answers to 14 everyday questions (bench/briefs/everyday.ts), before Q13 |
| Answers with something to use, Q13, every pack | 56 of 56 (100%); 6.7 such components an answer | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 56 answers to 14 everyday questions (bench/briefs/everyday.ts), Q13, every pack; a quiz, flashcards, a map, a carousel, a button, a form or a what-if (follow-ups, tabs and details counted apart) |
| The component a question calls for, Q13, every pack | 15 of 16 (94%) | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 56 answers to 14 everyday questions (bench/briefs/everyday.ts), Q13, every pack; Products for shoes to buy, a Quiz or Flashcards for a lesson or phrases, a Compare for two phones |
| Valid answers to everyday questions, Q13, every pack | 49 of 56 (88%) | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 56 answers to 14 everyday questions (bench/briefs/everyday.ts), Q13, every pack |
| Photos where a person would want them, before Q12 | 0 of 28 (0%) | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 40 answers to 10 questions as people type them (bench/briefs/everyday.ts), before Q12 |
| Maps for places to go, with coordinates, before Q12 | 4 of 16 (25%); 0 of 4 (0%) with Lat and Lng | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 40 answers to 10 questions as people type them (bench/briefs/everyday.ts), before Q12 |
| Photos linked from another site, before Q12 | 28 | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 40 answers to 10 questions as people type them (bench/briefs/everyday.ts), before Q12; such a photo never loads in our SDKs, and the API asks for it by its alt text instead |
| Photos where a person would want them, the Q12 prompt | 41 of 56 (73%) | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 80 answers to 10 questions as people type them (bench/briefs/everyday.ts), the Q12 prompt |
| Maps for places to go, with coordinates, the Q12 prompt | 29 of 32 (91%); 35 of 35 (100%) with Lat and Lng | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 80 answers to 10 questions as people type them (bench/briefs/everyday.ts), the Q12 prompt |
| Photos linked from another site, the Q12 prompt | 2 | not measured | Not measured | DeepSeek V4 Flash, reasoning off, every pack, 80 answers to 10 questions as people type them (bench/briefs/everyday.ts), the Q12 prompt; such a photo never loads in our SDKs, and the API asks for it by its alt text instead |
| Answers with errors against OpenUI's chat library, 10 briefs x 2 | 2 of 20 | 6 of 20 | MorphScreen | the library OpenUI's apps use (14k-token prompt); lang-core's parser, counting what the benchmark counts |
| Answer tokens against the chat library, total | 30,692 | 61,308 | MorphScreen | fewer tokens is less cost and less waiting |
| Download for a server-rendered answer (brotli) | 22.2 KB, no JavaScript | 787 KB | MorphScreen | our HTML needs only the stylesheet; OpenUI always needs its renderer bundle |
| Download for streaming in the browser (brotli) | 270 KB (JS, WASM core, CSS) | 787 KB | MorphScreen | sdk/web/dist against OpenUI's React renderer bundle and stylesheet |
| Parse and validate a finished answer, all briefs | 5.03 ms | 5.31 ms | MorphScreen | median of 50; the WASM core and lang-core, both in Node, each on its own answers to the same briefs |
| Stream all briefs, 32-char chunks | 7.7 ms | 1018.8 ms | MorphScreen | median of 5, same setup; ours reads each byte once |
| Memory while parsing, per byte of answer | 19x (worst 31x) | 54x (worst 297x) | MorphScreen | ours: the core's peak heap (native); OpenUI: what lang-core's streaming parser keeps after the answer (Node, after GC) |
| CPU to stream and render in the browser | 655 ms | 5841 ms | MorphScreen | Chrome, 20 answers, 32-character chunks, layout forced after every chunk in both |
| Time to first text on screen | 73 ms | 150 ms | MorphScreen | Chrome, 20 answers, 32-character chunks every 30 ms; median |
| Layout shift while streaming | 0.014 | 0.914 | MorphScreen | Chrome, 20 answers, 32-character chunks; cumulative layout shift, all answers |
| Memory after the answer, in the browser | 541 KB JS + 1.4 MB WASM | 3.7 MB JS | MorphScreen | Chrome, 20 answers, 32-character chunks; median JS heap after GC, plus our WASM memory |
| Accessibility (axe violations) | 0 | 595 | MorphScreen | Chrome, 20 answers, 32-character chunks; axe-core on the finished answer, light theme |
| Render errors while streaming | 0 | 7 of 20 | MorphScreen | OpenUI throws React's 'maximum update depth exceeded' (its #990) when each chunk renders synchronously; fine with normal batched updates |
| Browser page equals the server HTML | 20 of 20 | no server HTML | MorphScreen | the same answer rendered on a server needs no JavaScript and looks the same |
| Layout defects per answer, phone (390 px) | 0 (177 of 177 clean) | 0.69 (124 of 177 clean) | MorphScreen | 177 answer pairs, same briefs, bench/quality/check.js on the finished answer: overflow, clipped or broken words and IDs, tiny or cramped text, stranded words, lone or uneven cards, chart labels drawn over each other; most common, ours: none; OpenUI: clipped-text 47, broken-id 31, overflow 16 |
| Layout defects per answer, chat column (760 px) | 0 (177 of 177 clean) | 0.31 (146 of 177 clean) | MorphScreen | 177 answer pairs, same briefs, bench/quality/check.js on the finished answer: overflow, clipped or broken words and IDs, tiny or cramped text, stranded words, lone or uneven cards, chart labels drawn over each other; most common, ours: none; OpenUI: clipped-text 28, overflow 10, page-overflow 8 |
| Answers with low-contrast text | 0 of 177 | 177 of 177 | MorphScreen | axe-core's color-contrast rule on the same renders, light theme |
| Answers that contact a host the app didn't choose | 0 of 177 | 33 of 177 (121 requests: api.qrserver.com, cdn.mos.cms, i.scdn.co, images.unsplash.com, ...; 5 while the URL was still arriving) | MorphScreen | the same answer pairs in Chrome, every request to another host counted; a model-written image URL that loads tells its host who is reading. Ours loads images only from the API's media origin and the app's hosts (photos the API found, credited); none here |
| Layout defects per answer, Compose | 0 at 390 dp (177 of 177 clean), 0 at 760 dp (177 of 177 clean) | no Compose renderer | MorphScreen | desktop Compose on the JVM, the answers in the web rows, checked from the semantics tree and each text's layout; chart labels are drawn on a canvas and not checked |
| Layout defects per answer, SwiftUI | 0 at 390 pt (177 of 177 clean), 0 at 760 pt (177 of 177 clean) | 0.06 at 390 pt (170 of 177 clean), 0.13 at 760 pt (167 of 176 clean); 1 render crashed | MorphScreen | macOS, offscreen window, our SwiftUI view against OpenUI's Swift port on the same answers; text found by Apple's Vision in each screenshot and checked against the answer's own text: split IDs and words, ellipses the answer didn't write, text cut at the window edge, paragraphs under 16 characters a line |
| Main-thread time to stream and render, SwiftUI | 1744 ms | 23480 ms | MorphScreen | macOS on Apple silicon (no iPhone or simulator here), offscreen SwiftUI window, 20 answers, 32-character chunks, layout after every chunk; the 19 answers both rendered, warm pass |
| Memory added by an answer, SwiftUI | 25.5 MB | 26.7 MB | MorphScreen | macOS on Apple silicon (no iPhone or simulator here), offscreen SwiftUI window, 20 answers, 32-character chunks, layout after every chunk; median peak process footprint over the starting footprint |
| Crashes while streaming, SwiftUI | 0 of 20 | 1 of 20 | MorphScreen | each answer in its own process; OpenUI traps inside Swift Charts on b5-plant__r1 |
| Compose renderer streams every bench answer | 20 of 20, 6.4 s in all | no Compose renderer | MorphScreen | Mac OS X aarch64, JDK 21.0.12.1, desktop Compose 1.12 (Skia), 1520 x 2400 px, 32-character chunks, render after every chunk; Android itself not built here (no Android SDK) |
| Device features asked for that work (camera, scan, map, location, files) | 26 of 26 | 0 of 26 | MorphScreen | 8 device briefs, 16 answers per format; OpenUI has no such components and asks for typed text instead |
| Answers with errors, device briefs | 1 of 16 | 4 of 16 | MorphScreen | ours with the device pack, same rules as above |
| Valid answers, pack briefs (128 answers, 8 of 8 packs) | 95.3% (122/128) | 78.9% (101/128) | MorphScreen | Nemotron 3 Ultra, the answers both formats have (up to 8 briefs x 2 per pack), ours with that pack, OpenUI with its chat library; ours by pack: data 16/16 (OpenUI 14), commerce 15/16 (OpenUI 13), scheduling 14/16 (OpenUI 12), media 15/16 (OpenUI 10), docs 15/16 (OpenUI 13), inputs 16/16 (OpenUI 15), device 15/16 (OpenUI 13), interactive 16/16 (OpenUI 11); the pack component each brief calls for was used 155 of 200 times |
| Answer tokens, pack briefs (total) | 74,244 | 143,106 | MorphScreen | o200k on the answer text, same runs |
| Valid answers with every pack in the prompt, 92 regular answers | 91.3% (84/92) (91.3% (84/92) without packs) | 77.2% (71/92) | MorphScreen | generative-ui-bench briefs, core v0.5 with and without every pack, the same repeats; 60 of 92 answers used a pack component (they can, the briefs don't need one); prompt 2,954 tokens with every pack |
| Valid answers with the interactive pack in the prompt, 92 regular answers | 92.4% (85/92) (88.0% (81/92) without it) | 77.2% (71/92) | MorphScreen | generative-ui-bench briefs, prompt v0.9 with and without the interactive pack, the same repeats; 6 of 92 answers used an interactive component (they can, the briefs don't need one) |
| Answers with errors made valid by repair, deepseek/deepseek-v4-flash on its own benchmark answers | 24 of 24 (100%); 24 improved | no repair in its open-source SDK; Autofix, in its paid gateway, not measured | Not measured | every recorded answer with errors in 2 runs, repaired by deepseek/deepseek-v4-flash through openrouter.ai; a repair is kept only when the core finds fewer errors.; providers {"order":["gmicloud","relace","baidu"],"ignore":["streamlake"]}; 2026-10-23, 0de5367 |
| Time a repair adds, deepseek/deepseek-v4-flash on its own benchmark answers | 2.2 s median, 13.7 s p95 | not measured | Not measured | the repair call alone, after the answer has streamed; only answers with errors pay it |
| Answers with errors made valid by repair, deepseek/deepseek-v4-flash on other models' answers | 73 of 73 (100%); 73 improved | no repair in its open-source SDK; Autofix, in its paid gateway, not measured | Not measured | every recorded answer with errors in 7 runs, repaired by deepseek/deepseek-v4-flash through openrouter.ai; a repair is kept only when the core finds fewer errors.; providers {"order":["gmicloud","relace","baidu"],"ignore":["streamlake"]}; 2026-10-23, 0de5367 |
| Time a repair adds, deepseek/deepseek-v4-flash on other models' answers | 2.3 s median, 5.3 s p95 | not measured | Not measured | the repair call alone, after the answer has streamed; only answers with errors pay it |
| Answers with errors repaired by one repair call, gemma-4-31b-it on other models' answers | 10 of 73 (14%) | no repair in its open-source SDK; Autofix, in its paid gateway, not measured | Not measured | every recorded answer with errors in 7 runs, repaired by gemma-4-31b-it through generativelanguage.googleapis.com; a repair is kept only when the core finds fewer errors. 51 stopped at the 30 s limit or couldn't reach the model: free models that think at length first, on a slow night; of the 22 that replied in time, 10 were repaired; 2026-10-22, da13327 |
| Time a repair adds, gemma-4-31b-it on other models' answers | 30.0 s median, 30.0 s p95 | not measured | Not measured | the repair call alone, after the answer has streamed; only answers with errors pay it |
| Answers with errors repaired by one repair call | 26 of 73 (36%) | no repair in its open-source SDK; Autofix, in its paid gateway, not measured | Not measured | every recorded answer with errors in 7 runs, repaired through OpenRouter by the model that wrote it (nemotron-3-ultra-550b-a55b 22 of 42, nemotron-3-nano-omni-30b-a3b-reasoning 4 of 31); a repair is kept only when the core finds fewer errors. 42 stopped at the 30 s limit or couldn't reach the model: free models that think at length first, on a slow night; of the 31 that replied in time, 26 were repaired; 2026-10-22, e01648b |
| Time a repair adds | 30.3 s median, 30.5 s p95 | not measured | Not measured | the repair call alone, after the answer has streamed; only answers with errors pay it |
| Valid after repair, DeepSeek V4 Flash, 46 briefs x 4 | 95.1% (175/184) | 91.8% (169/184) (no repair in its open-source SDK) | MorphScreen | the same answers as the row above, each with errors repaired by the same model (4 made valid); an answer cut off at the token limit or left blank isn't repaired (the API gives a blank one a fresh try, not measured here); OpenUI's paid gateway has Autofix, not measured; 2026-10-23, 0de5367 |
| Valid after repair, DeepSeek V4 Flash, reasoning off, 46 briefs x 4 | 100.0% (184/184) | 84.8% (156/184) (no repair in its open-source SDK) | MorphScreen | the same answers as the row above, each with errors repaired by the same model (20 made valid); an answer cut off at the token limit or left blank isn't repaired (the API gives a blank one a fresh try, not measured here); OpenUI's paid gateway has Autofix, not measured; 2026-10-23, 0de5367 |
| Platforms | HTML without JavaScript, web (WASM core + DOM, React), SwiftUI (iOS, macOS), Compose (desktop JVM; the Android build needs the SDK) | React (Swift port in review: #1295) | MorphScreen | renderers on the same core and op stream |
| Blind preference between screenshots | Not measured | |||
| Time to first component through the API | Not measured | |||
| Cost per 1,000 responses, same model | Not measured | |||
| Components available, standalone | 71 (75 with parts; core 25, 8 more packs) | 63 (84 with parts) | MorphScreen | parts that only live inside another component left out on both sides (ours: 4, like Tab and Step; OpenUI's chat library: 21 item parts like SelectItem, TabItem, Series); packs add components without adding prompt to apps that don't use them |