Writing
A Runtime for Non-Deterministic UI
June 5, 2026
JSON Lisp started with a question: what if AI could stream the right interface for one user instead of forcing everyone through the winning side of an A/B test?
Most product teams still design for the average.
Run an A/B test. Pick the version that wins with 51% of users. Ship it to everyone. The losers do not get a vote after that. They just live inside the winning average.
That made sense when interfaces had to be mostly fixed. A product team could not design a different workflow, explanation, layout, or decision path for every person. The cost was too high. So we optimized the shared screen and accepted the tradeoff.
AI changes that assumption.
Imagine a product that does not ask, “Which interface won for most users?” Imagine it asks, “What does this user need right now, and how do they prefer to process information?” One person may need a checklist. Another may need a comparison table. Another may need a guided sequence. Another may need dense controls because they already understand the domain.
That is not personalization as decoration. It is a different idea of interface design.
Instead of being the short end of an A/B test, the user ends up in a different place because the software can produce the right UI for the moment. The application becomes less like a fixed set of screens and more like a runtime that can receive a task-specific program.
That is where JSON Lisp comes from.
The project is public here: Cause-of-a-Kind/json-lisp.
The original seed was work I did at The Knot Worldwide. We were exploring how to sync UI across web, iOS, and Android from a shared JSON data structure. Because the product had a shared UI kit, we could express new views as JSON and let each platform interpret that structure in its native environment: JavaScript on web, Swift on iOS, Kotlin on Android.
That work was not about AI. It was about portability, consistency, and reducing duplicated product logic across platforms. But the idea stuck with me: if a UI can be expressed as structured data and interpreted by a known runtime, the screen does not have to begin life as hand-written application code.
JSON Lisp takes that idea and points it at AI.
If an LLM is going to create UI or small programs on the fly, maybe it should not generate normal frontend code at all.
Maybe it should generate a compact program representation that the browser already knows how to execute.
That is the backbone idea: a browser-native runtime where structured JSON can describe dynamic interfaces and program behavior in a token-efficient format. The LLM does not have to write a full React app. It can stream a smaller, constrained program into a running application that targets a known runtime.
The Lisp part of the name is intentional. Clojure and other Lisp traditions treat code as data in a way that has always felt unusually well matched to this problem. If the program can be expressed directly as a data structure, you are not asking the model to produce a pile of syntax and hoping a compiler makes sense of it. You are asking it to write into the shape of the abstract syntax tree.
That gives the runtime more leverage. The program is easier to inspect, transform, validate, constrain, transmit, and potentially generate in pieces. For AI-generated software, that flexibility matters. The output is not just text that happens to look like code. It is structured material the system can reason about before it becomes behavior.
At the smallest level, the program is just JSON. A model can emit something like this instead of a component file:
{
"v": "0.1",
"state": {
"mode": "checklist"
},
"view": [
"html.section",
{ "class": "stack" },
[
["html.h2", {}, ["Your next three steps"]],
["html.ol", {}, [
["html.li", {}, ["Confirm the business goal"]],
["html.li", {}, ["Pick the safest next action"]],
["html.li", {}, ["Ask for help if the answer is unclear"]]
]]
]
]
}
The host app gives that document to the runtime:
import { renderJsonLisp } from "@json-lisp/dom"
const app = renderJsonLisp(program, {
target: document.getElementById("app")
})
Then the UI can still behave like software, not just static content. State, events, and updates are part of the document:
{
"v": "0.1",
"state": {
"count": 0
},
"view": [
"html.button",
{
"@click": ["!set", "count", ["+", ["$get", ["$state"], "count"], 1]]
},
[
"Count: ",
["text", ["$get", ["$state"], "count"]]
]
]
}
That is intentionally not a lot of syntax. The point is to give the model a small target: describe the document, read state, handle an event, update the model, render the result.
That matters because tokens are not just a billing concern. They are a design constraint. The longer and looser the generated output, the more chances the model has to drift, contradict itself, invent missing pieces, or produce code that looks plausible but is not safe to run.
A compact runtime changes the bargain. The model operates inside a smaller language. The browser receives structured data instead of arbitrary source code. The system can reason about what is allowed. Interfaces can become non-deterministic in a controlled way: generated for the moment, shaped by context, but still rendered through a known execution model.
This is not only a technical experiment. It points at a different product pattern: non-deterministic UI.
Most software assumes the interface is designed ahead of time. Non-deterministic UI assumes the application can generate an interface at runtime based on the user’s goal, context, available data, and safe actions. The interface may not need to be fixed. It can be produced for the task at hand.
That does not mean chaos. It means the runtime becomes the contract.
The product question becomes: what small set of primitives can express enough useful interface and program behavior for an LLM to assemble something meaningful, without making the output huge, fragile, or unsafe?
That is where JSON Lisp is interesting. It treats AI-written UI as a runtime-design problem instead of a prompt-writing trick. The goal is not to make the model produce prettier code. The goal is to give the model a better target.
For founders building AI products, this distinction matters. If your product depends on generated software behavior, you should care about the shape of the thing being generated. Free-form code is powerful, but it is expensive to trust. A compact program format can make the system easier to stream, inspect, transmit, constrain, and evolve.
The future of AI interfaces may not be one perfect app screen that wins the test and gets forced on everyone.
It may be a runtime that lets the right screen exist for one person, just long enough to do the job.