oriel
Oriel logo

Zig 0.16 · Linux · Windows · macOS · Android · iOS

Apps in Zig.
UI with the web.

Oriel gives your React, Vue, Svelte or plain HTML frontend a native app on desktop and mobile, with typed Zig commands to call. Use the system WebView or try the experimental native renderer with the same web files. One codebase, five platforms.

$ curl -fsSL https://raw.githubusercontent.com/highercomve/Oriel/main/install.sh | sh

Why Oriel

Everything a desktop app needs, compiled in only when you ask for it.

Small and fast to build

A release app is a few MB. The build cache is hundreds of MB, not gigabytes, and Zig rebuilds incrementally.

Typed both ways

Write Commands and Events as plain Zig structs. invoke and listen are generated for TypeScript at compile time.

Secure by default

Navigation limits, per-origin command capabilities, a strict CSP, and a bridge injected only into your own pages.

Batteries, opt-in

Tray, updater, SQLite and sqlite-vec, offline text to speech, the phone's own model, camera, lifecycle events, dialogs, notifications, global shortcuts, clipboard, file watching, deep links, audio capture, media server.

Local AI built in

llama.cpp and whisper.cpp as modules: CUDA, Vulkan and Metal on the desktop, Metal on iOS, and ARM dotprod/i8mm CPU kernels, Vulkan, OpenCL and Hexagon on Android.

Voice, tuned

oriel.dictation: live dictation and file transcription out of the box. Whisper with voice detection, live draft text and a measured CPU or GPU per device, or the phone's own recognizer.

Chat, tuned

oriel.chat is a local LLM in a module: models, the chat template, streamed tokens and a reused KV cache, on the CPU or the GPU, per device.

Speech, offline

oriel.tts: Kokoro-82M text to speech out of the box. Verified voice downloads, bundled phoneme data, and streaming playback on the CPU or GPU.

Who said what

oriel.speakers: speaker embeddings and diarization, offline. Voices clustered into speakers over a recording, and matched against the voices an app remembers.

Phones and tablets too

The same app on Android (phones, desktop mode, ChromeOS) and iOS. Tiles, a keyboard, notification actions and deep links stand in for the tray and hotkeys.

Reach past the window

System-wide hotkeys, typing and pasting into other apps, image clipboard, and microphone or system audio capture, on X11, Wayland, Windows and macOS.

Signed updates

oriel.updater checks Ed25519-signed manifests, swaps the binary, AppImage or .app atomically and restarts in place on Linux, Windows and macOS. The page can't change the URL or the key.

Find nearby devices

oriel.network announces and finds services on the local network with mDNS/DNS-SD, through each platform's own responder; oriel.bluetooth sends a Bluetooth LE beacon nearby phones see. No server in between.

Your own native code

App extensions: your Kotlin, Swift, Objective-C or Zig, declared by name in build.zig and called with one API on every platform, from Zig or the page.

Ship it

oriel package builds deb, rpm, AppImage, .dmg and Windows setup.exe (natively or cross-compiled from Linux); oriel android build an APK and AAB; oriel ios build an .ipa.

Native UI renderer

Render your frontend with native views using the experimental, opt-in native UI renderer.

Your frontend, drawn natively

-Dnative_ui runs the same HTML, CSS and JavaScript with QuickJS, a DOM written in Zig and platform drawing on Linux, Windows, macOS, Android and iOS. React, Vue, Svelte, Preact and CSS-in-JS apps run on it.

Text and controls that fit

Mixed-font lines, baseline alignment, decorated inline text and styled SVGs. Android text widths and control sizes follow its WebView; macOS buttons keep their default AppKit appearance until styled.

Canvas from JavaScript or Zig

Breakout runs either way, in both renderers. Numeric canvas recording and transform updates reduce work per frame; the render bench covers rows, animation, canvas and memory on Android and desktop. Battery savings have not been established.

Explore the native renderer

Read the changelog

The native renderer is experimental; accessibility, full CSS grid and shadow DOM remain incomplete. See the release changelog for details.

One struct, both sides

A pub fn in Commands is callable from the page. Its arguments and return type become the TypeScript types.

src/main.zig
pub const Commands = struct {
    /// invoke("greet", { name })
    pub fn greet(gpa: std.mem.Allocator, args: struct {
        name: []const u8,
    }) ![]const u8 {
        events.emit(.greeted, .{ .count = 1 });
        return std.fmt.allocPrint(gpa,
            "Hello, {s}!", .{args.name});
    }
};
frontend/src/App.tsx
import { invoke, listen } from "./oriel";

// Typed from the Zig structs: name is a string,
// the result is Promise<string>.
const text = await invoke("greet", { name: "Ada" });

listen("greeted", ({ count }) => {
  console.log(`greeted ${count} times`);
});

Built with Oriel

GhostPen: AI text editing anywhere on the desktop, ported from Tauri. Built-in models (llama.cpp), live captions and dictation (whisper.cpp) on the GPU (Vulkan, CUDA, Metal), and meetings with speakers by voice (oriel.speakers), on Linux, Windows and macOS.

GhostPen: the Playground and the menu, running a built-in model

The showcase

examples/showcase: every Oriel feature in one app, from one codebase, for Linux, Windows, macOS, Android and iOS. Below, on Android: Dictate and Chat on a Pixel, the rest on a Poco X3.

The showcase's Dictate tab on a Pixel: whisper small on Vulkan, drafted by base on the CPU, about 1.3 s per live update
Dictate · live text on a Pixel: small on Vulkan, drafted by base on the CPU, ~1.3 s per update
The showcase's Chat tab on a Pixel: Llama 3.2 1B on Vulkan, streamed tokens, models and CPU/GPU choice
Chat · Llama 3.2 1B on Vulkan on a Pixel: streamed tokens, per-device model and CPU/GPU choice
The showcase's Speak tab on a Poco X3: Kokoro 82M reading aloud on the CPU, with first-audio time, real-time factor and gaps
Speak · Kokoro 82M offline, streamed in chunks; on this phone's CPU, first audio in about 4 s
The showcase's Notes tab: notes in SQLite, added by a deep link, each read aloud on a tap
Notes · SQLite, deep links that add a note from anywhere, and read aloud
The showcase's System tab: dictate anywhere, clipboard, notifications and keyboard shortcuts
System · dictate into any app, the clipboard, notifications, shortcuts
The showcase's App tab: windows, the IPC echo and events from a worker
App · windows, IPC round trips, events from worker threads

system-monitor

examples/system-monitor: a live CPU, memory, disk, network and process monitor that reads the kernel directly and spawns nothing, on Linux, Windows and macOS.

  • Three renderers, one app — compiled .oriel views with no JavaScript runtime (the default), the native renderer, or the WebView
  • Applications — each app's combined CPU and memory, its processes as a parent/child tree, or every process
  • Updates itself — Check for updates installs the next release with oriel.updater, signed and verified
  • Ships everywhere — AppImage, deb, rpm, a Windows installer and a macOS .app with every release
Oriel System Monitor on Linux in the Oriel renderer: CPU per core, memory, disks, network and the Applications list
Linux, the Oriel renderer: a frame in under 6 ms, kernel telemetry with no processes spawned

breakout

examples/breakout: a canvas game with an HTML and CSS interface, in the WebView or the native renderer. Drag on a phone; mouse or arrow keys on the desktop.

  • Three ways — the physics and drawing in JavaScript, or in Zig through oriel.canvas
  • 500 balls on a 120 Hz phone — 120 fps from Zig (0.35 ms a frame), 63 fps from JavaScript, 120 in the WebView
  • 500 balls on a 144 Hz laptop — 144 fps from Zig, 59–75 from JavaScript, 103–116 in the WebView
Breakout in the native renderer on Linux, Zig mode: bricks, balls and the stats overlay Breakout in the native renderer on an Android phone, Zig mode, at 120 fps
The native renderer, Zig mode: Linux and an Android phone

All the examples, explained

Platforms

Linux

GTK4 + WebKitGTK 6.0. Complete: every module, deb / rpm / AppImage.

Stable

Windows

Win32 + WebView2. Every module ported, verified on real Windows 11. Build natively or cross-compile from Linux; per-user NSIS installer.

Verified

macOS

AppKit + WKWebView. Every module ported, Metal for whisper / llama, deep links, .app / .dmg packaging. Verified on macOS 15, Apple Silicon.

Verified

Android

Kotlin shell + WebView over JNI, one window per Activity (resizable in desktop mode). APK and AAB. Verified on a Pixel phone, Android desktop mode and the emulator.

Verified

iOS

UIKit + WKWebView from Zig, no Xcode project. .app and .ipa, Metal for whisper / llama. The showcase verified in the iPhone simulator (iOS 18): commands, events, workers, local chat, a second window. Real devices next.

Preview

Coming from Electron or Tauri?

The same model: a web frontend, a native backend, calls between them. What changes is what ships with your app.

ElectronTauriOriel
BackendJavaScript on Node.jsRustZig 0.16
UIChromium, bundled with every appThe system webviewThe system webview, or native views with no webview (native renderer, compiled .oriel views)
App sizeChromium and Node.js in every appSmall: no browser bundledA few MB: no browser bundled
UI ↔ backendipcMain / ipcRenderer through a preload scriptinvoke commands and eventsPlain Zig structs; TypeScript generated at compile time; sync and async worker pool
MobileDesktop onlyAndroid and iOSAndroid and iOS from the same Zig and web code
Local AI and voicePackages you addPlugins or crates you addBuilt in: llama.cpp, whisper.cpp, Kokoro text to speech, speaker diarization
UpdatesautoUpdater or electron-updaterUpdater plugin, signedEd25519-signed manifests, atomic replace, progress events
PackagesElectron Forge or electron-builderThe Tauri bundleroriel package: deb, rpm, AppImage, .dmg, setup.exe; APK, AAB and .ipa
ToolchainNode.js and npmRust and Cargo: multi-GB target directoriesZig: a build cache in hundreds of MB, incremental rebuilds

Porting from Tauri

TauriOriel
invoke / commandsPlain Zig struct; TypeScript generated; sync and async worker pool
Events (emit / listen)Zig → JS, type-checked on both sides; targeted and broadcast
CapabilitiesPer origin, per command, per window
Multiple windowsoriel.window.open() from JS, routes, targeted events
create-tauri-app, tauri dev/buildoriel init (React, Vue, Svelte, vanilla), oriel dev/build/package, oriel doctor

How Oriel compares, in detail

Build your first Oriel app

Install the CLI, run oriel init my-app, and you have a window with hot reload in a minute.

Search documentation

Type to search the documentation.