Tin Hat / For the nerds

Under the hat.

Tin Hat is a native desktop app written in Rust, with an interface built in Qt Quick and drawn by your GPU. It isn't Electron: there's no copy of Chrome running your chat, no Node.js, no website in a box. Here's the whole stack, what we built from scratch and what we built it on.

tinhat · spec sheet · 11.1.1
ui
Qt 6 Quick (QML) · KDE Kirigami · GPU-drawn
core
Rust · CXX-Qt bridge · tokio
chat
Matrix · matrix-rust-sdk · E2EE
calls
MatrixRTC · LiveKit · libwebrtc (patched)
video
NVENC AV1/H.264 → VA-API → VP9 · dav1d
audio
GStreamer · WebRTC AEC3 · per-app capture
ships
Flatpak · Windows installer · the web
electron
none
113klines of Rust in the desktop app
56klines of QML across 272 interface files
535Rust tests, plus end-to-end tests between the apps
0lines of Electron

Counted from the 11.1.1 release, vendored libraries left out.

Native, not a website in a box

What "not Electron" actually means.

Electron apps ship a whole browser and run a web page inside it. Tin Hat compiles to one native program, so the interface, the call engine and the media pipelines talk to your computer directly.

A typical Electron app

  • Bundles its own copy of Chromium and Node.js
  • The interface is a web page (HTML, CSS, JavaScript)
  • Calls and media go through the browser's WebRTC
  • Reaches the OS through the browser's APIs

Tin Hat

  • Compiled Rust with Qt 6, one native program
  • The interface is Qt Quick, drawn by your GPU
  • Media runs in native pipelines: libwebrtc, GStreamer, NVENC
  • Talks to the OS directly: PipeWire, desktop portals, WASAPI, Direct3D

The one exception, for honesty: when you press play on an embedded YouTube, Twitch or Spotify player in a chat, Tin Hat opens a small Qt WebEngine view for that player, and only then. Nothing browser-based starts until you click. Watch Party plays through mpv.

The stack

From your GPU to our server.

is code we wrote. is the open-source software it's built on.

Tin Hat desktop

Interfaceevery screen, 272 QML filesQt 6 Quick · KDE Kirigami
Bridgetyped Rust ↔ QML objectsCXX-Qt
Rust corecall engine · theme, layout & button engines · widgets · updatermatrix-rust-sdk · tokio
Mediacapture & encoder ladder · echo cancellation wiring · self-healing pipelinesLiveKit SDK + libwebrtc (patched) · GStreamer · dav1d · mpv
Your OSLinux: Flatpak, PipeWire, portals · Windows: Direct3D 11, WASAPI · NVENC / VA-API

Our server

Matrix homeserverSynapse on PostgreSQL: accounts, rooms, history
Call relayLiveKit SFU + a TURN relay: forwards encrypted media it can't read
Tin Hat APIsoundboard · GIFs · invites · opt-in presence · push · game serversNode.js
Memories recorderTin Hat itself, running headless as a bot
Bug trackerthe page behind "Report a bug"

Tin Hat Web

Same protocol, same look132k lines of TypeScript, React & CSS · every control hand-builtReact 19 · Vite · matrix-js-sdk with Rust crypto in WebAssembly · livekit-client (patched)

Built from scratch

Everything we wrote ourselves.

The desktop app

Every screen, from sign-in to Memories, is QML we wrote, driven by a Rust core: 113,229 lines of Rust and 55,589 lines of QML across 7 crates.

A web twin, not a port

Tin Hat Web isn't Element with a new coat of paint. It's a separate React and TypeScript app that matches the desktop app screen for screen and speaks exactly the same protocol, with no UI library: every control is hand-built.

Written twice, agreeing to the byte

The theme, layout and button engines exist in both Rust and TypeScript, and both must produce identical output on shared test vectors. The Game Reel's fairness maths alone is cross-checked on 24,000 cases.

Calls

MatrixRTC over LiveKit, with media keys sent over encrypted to-device messages, so Element X on a phone can join the same call. Tin Hat's own data messages carry "heard by" receipts, game chat, pull-aside corners, echo checks and moderator mutes.

AV1, encrypted

Screen shares encode on your GPU: NVENC (AV1 where the card can, H.264 otherwise), then VA-API, then software VP9. Getting AV1 through end-to-end encryption took a custom frame wrapper and patches to libwebrtc; the web app decrypts it with a patched LiveKit worker.

Audio that fixes itself

Echo cancellation (WebRTC's AEC3) runs inside Tin Hat's own mic pipeline, fed by whatever speakers you're using. A watchdog rebuilds a stalled pipeline mid-call. Share one app's audio through PipeWire on Linux or WASAPI process loopback on Windows.

The theme engine

Terminal palettes become 52 roles and 1,618 interface tokens, with contrast guards so text stays readable. The colour maths uses lookup tables and Newton's method instead of the platform's maths library, so Rust and TypeScript land on the same colour.

Layouts & edit mode

The window is a docking tree, like a tiling window manager: drag islands, split them, stack them as tabs. Your layouts sync to your other devices through your Matrix account data.

Memories

The recorder is Tin Hat itself, running headless on our server as a bot account. It records each person's mic and screen as separate tracks, so the player can switch points of view afterwards.

Game night lives in Matrix

Calendars, RSVPs, Game Reel spins and widgets are Matrix state events, so they sync to every device and both apps read them. Polls use the same poll events as Element (MSC3381), so Element users can vote too.

Our server bits

About 6k lines of Node.js we wrote: the soundboard, GIF search (the API key stays on the server), invite codes, opt-in presence, phone push, game server controls, the games catalogue and the bug tracker. The heavy lifting is open source: Synapse, LiveKit and a TURN relay.

Shipping it

Our own Flatpak repository with signed commits, a Windows installer that repairs older installs, and a Windows updater that checks each download's SHA-256 before swapping it in (Flatpak verifies Linux updates by signature). Desktop and web release the same day.

This website

Hand-written HTML, CSS and JavaScript: no framework, no trackers, no third-party requests, about 54 KB of code gzipped. The intro is a deterministic timeline, so you can freeze it at any moment: try /?t=11.3.

The long way here

~2 years of building.
And a fair bit of pain.

Tin Hat didn't start as Tin Hat. It started with a group of friends on Element, turned into a patched Element with a soundboard, and became a from-scratch native client when the patches ran out of road.

~2years of development (and pain)
1,720commits, in today's code alone
159bug reports from the crew since August
134of them fixed or shipped

Commits: the desktop app 773, the web app 828, the patched-Element days 119. Everything tried and thrown away before today's code isn't counted.

  1. The early daysElement, then a patched Element: LuesElement, with a soundboard, per-person volume and its own themes.
  2. June 2026The first commit of a native client in Rust: NVENC screen shares straight into LiveKit.
  3. September 2026Renamed Tin Hat with 10.0, and Linux moved to Flatpak.
  4. A week laterTin Hat Web, rebuilt from scratch to match the desktop app.
  5. TodayTin Hat 11.1.1: islands, edit mode, 1,190 themes, widgets and game nights.

Special thanks

To the crew.

To the friends who stuck with it through calls dropping mid-game, mics that echoed, screen shares that went black and some truly messy bugs, and who reported every one of them. Every "it did the thing again" made Tin Hat better.

Thanks for not giving up on it. 🎩

Built on

Standing on some very big shoulders.

And none of it exists without open source. Thank you to these projects:

Nerd questions

Why not Tauri?

Tauri is lighter than Electron, but its interface is still a web page, drawn by the system's web view. Tin Hat's interface is native Qt Quick.

Can other Matrix apps join in?

Yes. Your account is a normal Matrix account, so Element and Element X can chat in the same rooms, and Element X on a phone can join Tin Hat calls. Tin Hat's extras (the soundboard, game chat, the Game Reel) just don't show up there.

What exactly is end-to-end encrypted?

Every call's audio, video and screen shares, with fresh keys for each person, and every direct message: the server relays them without being able to read them. Group rooms made in Tin Hat aren't encrypted today; a padlock shows which rooms are. Room names, member lists and profiles are always ordinary server data.

What graphics card do I need?

Any. NVIDIA cards encode screen shares with NVENC (AV1 on newer cards), AMD and Intel on Linux use VA-API, and everything else falls back to software VP9. The desktop app decodes AV1 in software with dav1d, so any PC can watch.

Is there a Mac app?

Not a native one yet. On a Mac, Tin Hat Web does everything a browser allows.