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.
Tin Hat / For the nerds
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.
Counted from the 11.1.1 release, vendored libraries left out.
Native, not a website in a box
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.
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
is code we wrote. is the open-source software it's built on.
Built from scratch
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
Special thanks
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
And none of it exists without open source. Thank you to these projects:
Nerd questions
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.
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.
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.
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.
Not a native one yet. On a Mac, Tin Hat Web does everything a browser allows.