I have wanted HTML, CSS and JavaScript gone as the application platform since before WebAssembly had a name.
Not improved. Not hidden under another framework. Gone.
That sounds ridiculous because the web won. HTML is everywhere. JavaScript runs everywhere. There are millions of developers, libraries for everything and an answer on Stack Overflow for every way the stack can betray you.
But ubiquity does not turn three historical accidents into a coherent application model. It merely makes replacing them extremely difficult.
You can see the failure every time HTML quietly gives up. A workflow graph is a good example. So is a timeline, waveform editor, diagram surface, node editor, game, or sufficiently ambitious text editor. The page around it remains HTML. The interesting part becomes canvas, SVG, WebGL, or a private pile of absolutely positioned elements.
We declare victory because pixels appeared. Then somebody tries to use the thing.
Does the mouse wheel scroll the page, pan the surface, or zoom it? Does trackpad pinch mean browser page zoom or graph zoom? What happens when a drag leaves the surface? Which layer owns pointer capture? Does keyboard focus enter it? Can a screen reader understand it? Does its hover state look remotely related to the application surrounding it?
The browser has no idea. The custom control provider decides, and every provider invents a different answer.
The custom surface has become a second UI system embedded inside the first one.
This is why sophisticated HTML5 controls so often feel wrong even when they look polished. The problem is not a missing CSS property. The control has no encompassing model of the application. Mouse handling, wheel momentum, pinch-to-zoom, touch arbitration, focus, selection, keyboard shortcuts, context menus and accessibility are rebuilt inside an island.
It works. So does a lot of technical debt.
The hack inside the hack
HTML is excellent at documents. It is also a practical way to build conventional forms. We turned it into a general application platform because it was already everywhere, not because a document tree, a cascade and a scripting language somehow formed a brilliant UI architecture.
When the document model runs out of vocabulary, the accepted answer is to insert a custom rendering surface and rebuild what is missing in JavaScript. That creates two owners for the same interaction:
- The browser owns page scrolling, page zoom, focus traversal, text input, accessibility and platform conventions.
- The embedded surface owns its own hit testing, gesture state, camera, selection, rendering and often another accessibility approximation.
The boundary is where applications become uncanny. Wheel input is captured when the user meant to scroll. Pinch changes the wrong scale. A drag gets stuck because the release happened elsewhere. Keyboard commands work until focus crosses into the island. The graph is themed, but not really part of the theme.
None of this is impossible to fix. Pointer Events, touch-action, ARIA, hidden editors, DOM overlays and a heroic quantity of JavaScript can reproduce almost anything.
But once a custom widget has implemented all of that, it has built a small UI framework. The next widget builds another one.
A canvas is not a UI framework
Replacing the entire page with one enormous canvas does not solve anything. It merely makes the island larger.
A canvas gives us pixels. It does not give us retained control identity, layout, focus, text editing, IME, selection, clipboard behavior, password-manager integration, browser find, semantics, accessibility, touch conventions, or platform-aware shortcuts.
This is where most canvas UI arguments go off the rails. A canvas is a framebuffer, not an application platform.
EffinDOM exists to provide the missing application model. Canvas is one presentation surface used by the browser host. It is not the architecture.
The runtime owns one retained tree for layout, controls, rendering, input, text, selection, scrolling and semantics. It then projects the parts that the host platform must own. In the browser that includes a semantic DOM, mirrored editor state, IME integration and browser-facing behavior. On native desktop it includes the operating system accessibility and input adapters.
Your retained application
│
┌─────────┴─────────┐
▼ ▼
FUI-RS FUI-AS
Rust application AssemblyScript web
SDK SDK
└─────────┬─────────┘
▼
EffinDOM
layout · text · input · semantics
╱ ╲
▼ ▼
Browser/WASM Native desktop
canvas + host Metal · D3D12 · Vulkan
The goal is not to ban platform-specific code. That would turn portability into a prison, and I have no interest in replacing one prison with another. The normal application path should be materially the same. Genuinely platform-specific capabilities should remain available behind clean boundaries.
Why start with the browser?
Most custom-rendered UI frameworks begin natively and later acquire a WebAssembly target. That is perfectly understandable. Native is easier.
EffinDOM went in the opposite direction.
I started in the browser precisely because it is harder. A non-DOM UI that merely draws correctly is not a credible web application. It has to participate in browser behaviour:
- Keyboard and touch text selection.
- IME composition and CJK input.
- Clipboard operations and platform shortcuts.
- Password-manager and form integration.
- Accessibility and semantic inspection.
- Browser and retained find-on-page.
- Routing, asynchronous fonts and lifecycle.
- Page zoom, trackpad gestures and nested scrolling.
Those requirements shaped the runtime before there was a native host. Once they worked, moving the same retained model to macOS, Windows and Linux was still a lot of work, but the direction was clean. The runtime already knew what the application meant. Native adapters connected that meaning to another host.
EffinDOM is not a native framework with a canvas build. It was born on the web and ported outward.
The hybrid route is still a hybrid
Cross-platform UI looks remarkably like the car industry.
Putting an HTML application inside Electron or a system WebView is the conventional hybrid. It gives the application a native body while carrying the browser drivetrain underneath. It is pragmatic. It also makes every application drag a browser around so we can pretend a website became native.
There is a cleverer middle ground: remove the WebView and render an HTML-oriented application model natively. That is a substantial engineering achievement. It is also the plug-in hybrid. Part of the drivetrain changed, but the application still begins with a model designed for the document web and now needs translation into another host.
EffinDOM and FUI are the EV bet. I am not trying to work out how to render HTML somewhere else. I am asking what should replace it.
The analogy also captures the uncomfortable part. HTML has had three decades to put petrol stations on every corner: component libraries, browser tooling, integrations, hosting, documentation, experienced developers and an answer for almost every failure.
FUI has a working drivetrain. Its charging network is young. Built-in controls, web and desktop hosts, packaging, official Agent Skills, documentation and access to ordinary language ecosystems cover a surprising amount. The third-party ecosystem does not have three decades behind it. Early adopters will occasionally need to plan the route themselves.
That is the bet. A useful platform creates demand for more controls and tooling. Better tooling makes the next application cheaper. Eventually the ecosystem develops gravity of its own.
The control that made the claim real
The hardest control was not a graph, shader or some impressive GPU demo. It was the boring multiline text box everybody assumes an operating system simply gives you: TextArea.
Text editing exposes every shortcut a UI framework is tempted to take. Monospaced fonts, fixed line heights, wrapping, non-wrapping, visible-line processing, incremental edits and static content all deserve specialised fast paths. Then those paths combine. Every combination must still preserve Unicode boundaries, caret movement, forward and reverse selection, scrolling, hit testing, clipboard behaviour, accessibility and font fallback.
On the web, EffinDOM renders the visible editor while mirrored DOM editor state stays synchronised for IME and browser integration. Text inputs also need the right semantics for password-manager extensions. A fast path that draws the correct pixels but breaks composition or caret geometry is not an optimisation. It is a bug with good frame time.
Find-on-page has the same character. The normal desktop shortcut opens EffinDOM's retained find experience. Browser-native find can search projected semantic text as well. Its highlight may use DOM font rendering that looks slightly different from the canvas text underneath. That is an honest host boundary, not a reason to pretend find does not exist.
Why retained FUI?
I did not want another framework that repeatedly rebuilds a description of the UI so it can diff that description against the UI it already had. FUI uses retained controls. Construct the control once. Keep its identity. Change the property that changed.
FUI-RS exposes that model to Rust. FUI-AS exposes the same controls and fluent conventions to AssemblyScript. The syntax belongs to each language; the runtime beneath them is shared.
A control keeps its identity. Focus, selection, editing state, scroll position, subscriptions and overlays remain attached to the thing the application created. Event handlers mutate the retained state they actually intend to change.
use fui::prelude::*;
fn build_page() -> FlexBox {
ui! {
column().fill_size().padding(24.0, 24.0, 24.0, 24.0) {
text("One retained UI").font_size(28.0),
button("Run it").on_click(|_| {
logger::info("App", "Still the same button");
}),
}
}
}
fui_app!(FlexBox, build_page);
function buildPage(): FlexBox {
return Column(
new Text("One retained UI").fontSize(28.0),
new Button("Run it"),
).fillSize().padding(24.0, 24.0, 24.0, 24.0);
}
The application model does not change just because the language does.
The C++ runtime underneath is deliberate. Rendering, layout, shaping, input and semantics are shared infrastructure for FUI-RS, FUI-AS and future language SDKs. A narrow ABI lets that runtime integrate with Skia, Yoga, HarfBuzz, ICU, SDL and native platform APIs without reimplementing the engine for every language.
What exists today
This is no longer a renderer experiment. EffinDOM runs in browsers through WebAssembly and natively on macOS, Windows and Linux. FUI-RS covers both environments, including ARM64 and x64 for each desktop operating system. FUI-AS is currently a browser SDK. Native applications embed neither Chromium nor a system WebView.
The same public application model covers retained controls, text editing, selection, IME, accessibility, touch interaction, custom drawing, remote UI assets, workers and themes. Routed universal projects can compile each browser route as an independent WASM micro-frontend while lazily composing the same retained page crates into one native shell.
cargo-fui creates, builds and packages applications. Native packages are DMG on macOS, MSIX on Windows and AppImage on Linux. Native-only development does not require Node.js or npm.
Those figures were measured against the release demos. Of the approximately 7.6 MB compressed cold transfer, the shared EffinDOM core is about 1.9 MB, the UI runtime about 810 KB, ICU data about 2.4 MB, and the six built-in fonts about 2 MB in total. The remaining transfer includes the browser bridge, harness and application route.
The language-specific part is comparatively small. The FUI-RS home route application WASM was approximately 321 KB compressed; the FUI-AS home route was approximately 122 KB compressed. Demo-page images are excluded. The macOS bundle compressed to approximately 20 MB and started effectively instantly in manual testing. These are measurements, not cross-framework benchmark claims.
The runtime and built-in assets are content-addressed and reused within a top-level site across routes and FUI language SDKs. Browser cache partitioning means unrelated sites cannot be promised cross-site reuse.
What does not exist yet
Feature-rich early access is still early access.
- The public API is pre-1.0 and breaking changes remain possible.
- iOS and Android are targets for later work, not supported platforms today.
- The third-party control and integration ecosystem is young.
- Linux AT-SPI exists, but validation across the wider desktop and screen-reader ecosystem remains early.
The official FUI SDKs use AGPL/commercial dual licensing. The underlying EffinDOM runtime is licensed separately so other developers can build FUI-style SDKs for languages the official project cannot maintain. Commercial licensing is intended to fund continued maintenance and a durable team behind the platform.
The point
The web keeps proving that applications need more than documents. Our answer has been to place increasingly sophisticated private UI systems inside HTML and repair the seams one component at a time.
I refuse to believe another layer of JavaScript is the best civilisation can do.
EffinDOM is my attempt to replace the application UI model without throwing away the browser and operating-system integration users expect. FUI-RS and FUI-AS are its first application surfaces. It is far enough along to build real applications and early enough that somebody using it can still change its direction.
The first community application is Galaga-RS, a complete space shooter with a live browser build. That is one independent project, not an ecosystem. The next useful evidence is what somebody else tries to build and where the model fails them.
Disclosure: I used AI to help organise and edit this article. The argument, technical details, opinions and final approval are mine.
Try the claim, not the pitch
Open it. Scaffold it. Break it.
Use the live demo, create a small universal application, and report what blocks a real project. This is the stage where technical criticism can still materially change the framework.