counter.app
3
Real DOM - no runtime
WAID - one codebase, every platform
Write one ordinary Vite + React app and ship it everywhere - a real static website plus native desktop, iOS and Android apps, all GPU-rendered by the Detir runtime. No WebView, no rewrite. The same app, on every screen.
counter.app
3
Real DOM - no runtime
counter.app
3
GPU via Detir (wgpu)
counter.app
3
GPU via Detir (Metal)
counter.app
3
GPU via Detir (Vulkan)
One App.tsx, four artifacts. Tap Count up on any device - they share the exact same build. The web frame is real DOM; the others are GPU-rendered by Detir, no WebView.
One app, every platform
You write one ordinary web app. WAID hands back a real website plus native apps for every screen - no browser bundled inside, no separate codebase to maintain.
Web - live
The same source builds a standard static site. Deploy the dist to any CDN or nginx - no runtime, no shell.
iOS - live
Your dist plus the Detir runtime, drawn with Metal. A signed device build, not a web page in a wrapper.
Desktop - landing
A packaged desktop bundle rendered on the GPU with wgpu. Landing as the runtime publishes.
Android - landing
A generated Android app rendered with Vulkan / GLES. Landing as the runtime publishes.
Rendering
Native targets are drawn on the GPU by Detir, never a browser in a shell - so they look the same across devices.
Your code
You write an ordinary Vite + React + Tailwind app. There is nothing WAID-specific in your source.
Build pipeline
The build is a straight line, not a fork. Your Vite + React dist goes in, waid build embeds the Detir runtime and rasterizes your icons per platform, and signed native artifacts come out.
Input
Vite + React dist
Your ordinary static build
waid build
Embed + rasterize
Detir runtime in, icons per platform
Output
Signed native artifacts
Desktop, iOS and Android
Shared source
counter.app
3
Real DOM - no runtime
counter.app
3
GPU via Detir (wgpu)
counter.app
3
GPU via Detir (Metal)
counter.app
3
GPU via Detir (Vulkan)
One App.tsx, four artifacts. Tap Count up on any device - they share the exact same build. The web frame is real DOM; the others are GPU-rendered by Detir, no WebView.
No rewrite
$ waid new my-app && cd my-app scaffolded vite + react + tailwind + shadcn$ waid build --src . --target ios building web dist … 42 files (1.8 MB) resolving runtime … detir 0.1.0 · ios · metal [cache hit] embedding DetirFFI.xcframework … 214 MB generating xcode project … MyApp.xcodeproj archiving … signing device buildbuilt ios artifact -> .waid/build/ios/MyApp.ipa (18.4 MB)$ waid deploy --target ios resolving device … udid 00008130-0a11…3e! install + launch land next (Phase 15). build + sign done.$ .waid/build/
Multi-platform delivery
WAID takes an existing Vite and React application across web, desktop and native targets while preserving the codebase, web semantics and static site output.
Under the hood
The story above is the plain version. For engineers, here is the machinery - WAID is a thin native packager over your Vite dist and the Detir runtime. It never parses, transforms or compiles your code.
One command, a native artifact
No transpile, no code rewrite. WAID builds your Vite dist, resolves and embeds the pinned Detir runtime, and hands back a signed native artifact. This run produces a real .ipa; the desktop and Android slots stay hollow until their runtimes publish.
$ waid new my-app && cd my-app scaffolded vite + react + tailwind + shadcn$ waid build --src . --target ios building web dist … 42 files (1.8 MB) resolving runtime … detir 0.1.0 · ios · metal [cache hit] embedding DetirFFI.xcframework … 214 MB generating xcode project … MyApp.xcodeproj archiving … signing device buildbuilt ios artifact -> .waid/build/ios/MyApp.ipa (18.4 MB)$ waid deploy --target ios resolving device … udid 00008130-0a11…3e! install + launch land next (Phase 15). build + sign done.$ .waid/build/
One config, every platform
A single waid.config.json in your source root carries everything the packagers need. It often starts as just a name; capabilities are opt-in, and host functions are desktop-only - declaring them on mobile fails loudly.
{ "name": "peco", // bundle id + app name "icon": "icon.svg", // SVG master, rasterized per platform "iconBackground": "#ffffff", // Android adaptive-icon backdrop "dist": "web/dist", // Vite output (auto-inferred) "media": { // capture + playback "camera_capture": true, // + Apple usage strings "microphone_capture": true }, "host": { // DESKTOP-ONLY - rejected on mobile "fs": true, "shell": true, "vault": true }, "iosBuildProfile": { // iOS device signing "bundleId": "com.splenta.peco", "team": "XXXXXXXXXX" }}GPU-native rendering, no WebView
Native targets do not run your app in a WebView. The Detir runtime draws it on the GPU - wgpu on desktop, Metal on iOS, Vulkan / GLES on Android - and your app reaches real native capabilities too.
Desktop
Author the real menu bar at runtime with waid.menus.set([...]). Accelerators fire before page handlers, exactly like a native app.
iOS today
Flip capture and playback flags in waid.config.json - with Apple usage strings - and the Detir runtime wires them to the OS.
Desktop-only
fs, shell, env, vault, path, and actions are exposed to your app on desktop. Declaring host.* on iOS / Android fails loudly - mobile host is planned.
wgpu / Metal / Vulkan
detir-scene draws every native target: wgpu on desktop, Metal (DetirFFI.xcframework) on iOS, Vulkan / GLES on Android. No WebView anywhere.
if (waid.menus.supported) { waid.menus.set([ { label: "Project", submenu: [ { label: "New Run", accelerator: "CmdOrCtrl+N", onSelect: startRun }, { type: "separator" }, { label: "Archive", enabled: hasSelection, onSelect: archive }, ] }, ])}A runtime it never guesses
GB-scale Detir runtimes are resolved and pinned, never baked into the binary. For every native build WAID walks a fixed four-step order and stops at the first hit - so the runtime it embeds is always the exact, verified one. A checksum mismatch is a hard error, never a stale fallback.
--runtime-dir (or WAID_RUNTIME_DIR) points at a local runtime tree - the offline / dev path.
If a Detir checkout is reachable, WAID builds the runtime and stages it under ~/.detir/runtimes/source/.
A previously fetched runtime at ~/.detir/runtimes/<version>/<platform>/ is reused - offline after first use.
Otherwise WAID downloads the pinned runtime, checks its sha256, and extracts it. A mismatch is a hard error - never a stale fallback.
WAID walkthrough
Bring us the Vite and React application you want to take beyond the browser. We will show you how WAID packages the same project for web, desktop and native targets without moving the interface into a WebView.