WAID - one codebase, every platform

Write Vite, get 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.

  • app.example.com

    counter.app

    3

    Real DOM - no runtime

  • app

    counter.app

    3

    GPU via Detir (wgpu)

  • counter.app

    3

    GPU via Detir (Metal)

  • 9:41

    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

Reach web, desktop, iOS and Android from Vite.

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

A real static website

The same source builds a standard static site. Deploy the dist to any CDN or nginx - no runtime, no shell.

iOS - live

A native iOS app

Your dist plus the Detir runtime, drawn with Metal. A signed device build, not a web page in a wrapper.

Desktop - landing

A native desktop app

A packaged desktop bundle rendered on the GPU with wgpu. Landing as the runtime publishes.

Android - landing

A native Android app

A generated Android app rendered with Vulkan / GLES. Landing as the runtime publishes.

Rendering

GPU-native, no WebView

Native targets are drawn on the GPU by Detir, never a browser in a shell - so they look the same across devices.

Your code

No rewrite to learn

You write an ordinary Vite + React + Tailwind app. There is nothing WAID-specific in your source.

Build pipeline

Turn a web build into native artifacts.

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

Keep your web stack. Reach every platform.

You keep a single ordinary Vite + React + Tailwind app. The same source becomes a website and native apps for desktop, iOS and Android - so there is one thing to build, test and ship, not four. Every target renders the same app, so what you see on one screen is what ships on the rest.
  • One source of truth - no per-platform fork to keep in sync
  • The web build is a real static site, deployable anywhere today
  • Native targets are GPU-rendered by Detir, not a browser in a shell
  • app.example.com

    counter.app

    3

    Real DOM - no runtime

  • app

    counter.app

    3

    GPU via Detir (wgpu)

  • counter.app

    3

    GPU via Detir (Metal)

  • 9:41

    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

The dist is the app. No rewrite.

WAID does not parse, transform or compile your code. There are no WAID-specific directives, no per-platform branches and no special components to adopt. Search your source for “waid” and the only hit is waid.config.json. Anything that builds to a static dist works - and the same dist is exactly what ships to every platform.
  • No new framework, language or API to learn
  • Your existing Vite + React app builds unchanged
  • The built dist is the material every artifact embeds
~/my-app · waid
$ 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/

  • .waid/build/web/static site
  • .waid/build/ios/MyApp.ipaios artifact
  • .waid/build/desktop/MyApp.appdesktop bundle
  • .waid/build/android/MyApp.apkandroid artifact

Multi-platform delivery

One web codebase across every target.

WAID takes an existing Vite and React application across web, desktop and native targets while preserving the codebase, web semantics and static site output.

WAIDElectronTauriReact NativeFlutter
RenderingGPU-native (Detir)Chromium / DOMOS WebViewOS-native widgetsGPU-native (Skia / Impeller)
Bundles a browser?NoYes - ships ChromiumNo - uses the OS WebViewNoNo
Your codebaseVite + React, unchangedWeb app + Node mainWeb app + Rust coreRN components - a rewriteDart + Flutter widgets - a rewrite
Web targetReal static site, no runtimeDesktop onlyDesktop / mobile shellVia react-native-webCanvas / Wasm build
Native artifactsDesktop + iOS + AndroidDesktop onlyDesktop + mobile (beta)iOS + AndroidDesktop + iOS + Android
Fidelity across platformsOne renderer - pixel-identicalChromium everywhere (heavy)Varies by OS WebViewPer-OS native widgetsOne renderer - consistent

Under the hood

How it actually holds together.

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.

~/my-app · waid
$ 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/

  • .waid/build/web/static site
  • .waid/build/ios/MyApp.ipaios artifact
  • .waid/build/desktop/MyApp.appdesktop bundle
  • .waid/build/android/MyApp.apkandroid artifact

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.

waid.config.json
{  "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

Native macOS menus from JS

Author the real menu bar at runtime with waid.menus.set([...]). Accelerators fire before page handlers, exactly like a native app.

iOS today

Camera, microphone & media

Flip capture and playback flags in waid.config.json - with Apple usage strings - and the Detir runtime wires them to the OS.

Desktop-only

Desktop host functions

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

One GPU engine, per-platform backend

detir-scene draws every native target: wgpu on desktop, Metal (DetirFFI.xcframework) on iOS, Vulkan / GLES on Android. No WebView anywhere.

native macOS menus, from JS
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.

01

Explicit override

--runtime-dir (or WAID_RUNTIME_DIR) points at a local runtime tree - the offline / dev path.

02

Source-built cache

If a Detir checkout is reachable, WAID builds the runtime and stages it under ~/.detir/runtimes/source/.

03

Published cache

A previously fetched runtime at ~/.detir/runtimes/<version>/<platform>/ is reused - offline after first use.

04

Verified fetch

Otherwise WAID downloads the pinned runtime, checks its sha256, and extracts it. A mismatch is a hard error - never a stale fallback.

WAID walkthrough

Take your React app native.

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.