Skip to content
Velta GetDownload
Menu

News

Engineering

Our Rust UI take: what two days of native Velta taught us

· 5 min read

We built a working Velta client for Android in Rust — no WebView, no browser engine — and ran it on a mid-range phone next to the web app. Then we put our Android effort back into the Kotlin client. Both halves of that sentence are good news; this is the take.

Why we tried it

The Velta app you use on Windows, macOS and Linux is one build-less web UI wrapped in a system WebView, with the Delta Chat messaging core linked in-process. That shape has served well — but the pull of a native Rust UI is obvious: drop the WebView entirely, keep the core in-process, and share one user interface across every platform. A Rust UI toolkit (Masonry) with a working Android story exists, so we gave it a real shot: not a sketch, a client. Chat list with unread pills, chat view with day separators and quotes, a composer that rides the keyboard, swipe gestures, live incoming messages, links and invites — all rendered in Rust, booted on a phone.

What it proved

The architecture question is answered, and the answer is yes. Cold start to first frame: 157 ms on the phone, against 184 ms for the web app on the same device. Memory within noise of the web app (~126 MB vs ~113 MB — both carry the full messaging core in-process, which is why they sit so close). A chat list rebuild takes 3–13 ms; incoming messages land while you watch. Nothing about native performance scared us off, and the numbers are worth having: whichever UI ships, the core belongs in the process.

Where the bill came due

A UI toolkit draws. Everything a phone actually does around the drawing is yours to build, and the itemized list grew faster than the feature list. Copy and paste: a hand-written bridge into the platform clipboard service. The soft keyboard: a poller thread reading window insets, because the activity backend ignores the resize event. Typed text: a mirror of the input buffer, because the events never arrive. Scrolling: a gesture bridge. Text selection: a patched copy of the toolkit we vendor and maintain. One uncleared platform exception took the whole app down — once was enough to see the pattern.

The renderers told the same story from a different angle. Vello's GPU path deadlocks outright on Adreno-class phones. The hybrid path draws no color emoji — a dealbreaker for a messenger, where an emoji is content, not decoration. Skia met the full requirement set and carried a working liquid-glass prototype. And then we learned the punchline: Android's own platform shaders do displacement maps and blur natively. The effect we spent a day forking a rendering stack to get is a built-in on the platform we were leaving.

What remained was the honest mountain: notifications, attachment picking, share intents, accessibility, push, background sync — each one another bridge like the ones above, each one a new crash class. For a project with one maintainer, that math does not close.

The decision

The Kotlin client (Jetpack Compose, the same Delta Chat core in process) is back in active work, as a work in progress. The architecture we validated carries over whole; there the platform layer is the operating system's, not our code. It already carries encrypted-at-rest storage with a biometric lock, QR onboarding, relay management, account switching, attachments, reactions and background sync. Meanwhile the Tauri app stays the Android app that ships, and desktop keeps the web UI. The Rust client stays around as a lab: a place where renderer experiments cost an afternoon instead of a release.

What we keep

The native Android client is the work now. The next thing Android users should notice is not a new renderer — it is notifications, attachments and accessibility that simply work, because the platform provides them.