Program Runtimes and Surfaces — April 2026

How a program runs, and how it reaches a surface (DOM, WGPU, terminal, native widgets). The pilot picks a narrow path; this research documents the full territory so future work can reach for what's deferred without re-running the survey.

The driving question: can a program in a folk-like language be one thing — one source file, one process identity — that has both a UI surface and capabilities (fs, network, shell, substrate) without becoming a framework? The pilot's answer is "not in the unified-runtime sense; yes in the composition sense." This file shows why each unification path was considered and what the costs were.


Frame

Three concerns sit on different axes:

The unification question was: what arrangement lets a program of any language reach any surface, with capabilities, while staying folk-feeling?


Hard constraint discovered

Bun and wry cannot share a process. Wry uses the OS-native webview (WebView2 on Windows, WKWebView on macOS, WebKitGTK on Linux). The webview's V8 isolate is owned by Microsoft / Apple / WebKitGTK; Bun's runtime is a separate JavaScriptCore-based process. There is no API in wry or in Bun for embedding one inside the other.

This rules out the runtime-fusion approach (Electron's nodeIntegration) without a custom webview build. Every "unified single program" topology has to confront this.


Topology survey

A. Embedded runtime in webview process — Electron with nodeIntegration

A custom Chromium build that embeds Node.js into the renderer process's V8 isolate. The renderer's JavaScript context has both browser globals (window, document) and Node globals (require, process, Buffer, fs) visible simultaneously. Synchronous fs from React works; everything is one process, one isolate, one heap.

B. Process-separated, IPC-bridged — Tauri

OS-native webview in process A; Rust (or Bun, in our case) in process B. Capabilities exposed as commands; frontend calls invoke('cmd', args) over IPC. Webview is sandboxed; host has system access.

C. Custom React reconciler — render in Bun, emit to surface

react-reconciler is the public package React publishes for building custom renderers. It provides:

It does not provide:

Charted precedents. React Native (production, 10 years), react-three-fiber (mature), Ink (~1500 LOC for terminal renderer), React PDF. Custom reconcilers are a real pattern.

What it costs for our case. A reconciler in Bun that emits a stream of typed ops (create, append, set-attr, attach-listener); a host applier that consumes them on a target surface (Blitz, webview, WGPU canvas). The reconciler ~1500 LOC; event/attr/controlled-input plumbing ~3–5k LOC. Real engineering, not days.

Why it was set aside for the pilot. Replicating react-dom's behavior is the load-bearing piece — the hard part isn't the reconciler, it's reproducing the things react-dom already does. We become load-bearing on something we'd have to keep aligned with react-dom forever.

D. DOM shim in Bun — linkedom + unmodified react-dom

Run a real DOM (linkedom or jsdom) in Bun. react-dom renders against it normally — it thinks it's in a browser. Watch mutations via MutationObserver; serialize them; ship to webview which mirrors on the real DOM. Events flow back: serialize, deserialize against the linkedom element, react-dom dispatches.

E. SSR + HTML diff — LiveView pattern with React

The third class. Don't run React continuously on the server; render once per state change to HTML, ship to client, apply via a small DOM-diff library.

F. Native UI from folk languages

Honest read. There is no Tk-grade folk-language combination that also produces a designed-feeling modern UI without significant framework adoption. The combinations that look modern (Slint, Dioxus, Compose) lean on a primary language ecosystem the way React leans on JS. "Go native, stay folk, look modern" doesn't have an off-the-shelf answer.

G. Embedded JS runtime in host — deno_core, QuickJS

Embed a JS runtime inside the Rust host. Programs run in-process. No extra process hop, no IPC. Host owns the renderer (Blitz / WGPU / etc.).

H. WASM as the program runtime

Every program is a WASM module. Source: any language that compiles to WASM (Rust, TS via Bun, Python via Pyodide, AssemblyScript, Go, C). The host runs a WASM runtime (Wasmtime); exposes host functions for substrate, capabilities, surface ops as Component Model interfaces (WIT).

I. Server-rendered HTML + client hydration (Astro / Next-style islands)

Server renders HTML; client hydrates specific "islands" with real React. Static parts stay static. The 60fps-needing components become islands; the rest is server-rendered.


Component agnosticism — the honest finding

There's no clean way to define UI components language-and-technology-agnostic at the source level. Three partial paths exist:

So whatever tech the component library is written in is slightly first-class. Other languages consume it either by being in the same runtime or by talking to it over a stable protocol. For TS+React, "the protocol" is the React element tree shape. There is no universal answer.


The composition realization

The first-party UI primitives already include compositions of programs (see pilot/host.mdui/recipe, the spawned-composition concept, the tile tree). A composition of programs is an island system, just at a coarser primitive: the program.

This dissolves a substantial part of the unification anxiety. We don't need to fuse runtimes inside a program because we have a primitive above the program — the composition — that already does what islands do.


What modern webviews actually offer

Worth grounding plainly:


The pilot path — chosen

Programs come in kinds. The kind is declared on the program archetype.

Both kinds:

A complex UI that needs both DOM and fs is a composition of two or more programs — a UI program (webview) and a tool program (Bun), bound by their shared scope, talking through substrate. The composition reads as one thing to the user.

This isn't a last-resort compromise. It's the substrate's natural shape — small folk programs composed, joined by the field, each clear about what it is. Closer to the values than runtime fusion would have been.


What's deferred (not abandoned)

These remain reachable from the pilot's foundations. Each was set aside because the engineering investment was disproportionate to the pilot's scope, not because it was wrong.

Unified single-runtime programs

A program with both DOM and capabilities in one source file, one process, one identity — Electron-with-nodeIntegration semantics. Reachable via:

Other surfaces

Language-agnostic programs

The shebang model already permits it: #!/usr/bin/env python, #!/usr/bin/env ruby, #!/usr/bin/env <whatever>. The runtime lives in the VM. The SDK becomes available in those languages — either as a reimplementation of the protocol per language, or as a thin client over the stdio JSON-lines transport.

For non-TS programs to drive a webview surface, they'd need either:

Host-rendered surfaces (Path 2)

Programs in any language emit a stream of surface operations (DOM mutations, WGPU draw calls, terminal cells); the host owns the renderer (Blitz for DOM, WGPU surface for native, terminal buffer for terminal); the host applies. Surface-agnostic at the program side, language-agnostic at the surface side. Substantial engineering: a surface op protocol, a host renderer per surface kind, the engine's routing logic.

The closest existing model is React Native's bridge pattern, which proves it works at scale. Reaching for it in our context would be the right move when (a) language-agnosticism becomes important, and (b) Blitz or similar Rust DOM renderers are mature enough to lean on.

WASM as program runtime

Re-reachable when WASI Preview 2 / Component Model tooling stabilizes and WASM-DOM bindings become practical. The substrate's program archetype could declare runtime: 'wasm' and the engine would route accordingly. The capability declaration model (already in the spec) maps cleanly onto WIT imports.

Going entirely native

Skipping webviews altogether. Slint or Dioxus-Blitz or Compose-Multiplatform for the UI. Trades web-stack maturity for designed-feeling, smaller-binary, no-Chromium UIs. Real engineering investment per platform. Worth revisiting if the Web platform's cost (binary size, cross-platform inconsistency in WebView2 vs WKWebView vs WebKitGTK, evolving sandbox quirks) becomes binding.


Things to remember


Sources and pointers


What this session settled