Summary
A desktop and tablet audio tool for tabletop roleplaying game masters. It plays ambience, music and effects during a session, and remote players hear the same audio through a browser page or a Discord bot. In-person play needs the app alone.
Built solo. Not a game, and not built in a game engine.
The problem
The competition is browse-and-trigger: a library, a search box, a play button. That works when you are auditioning sounds and fails when you are actually running a game, because at that moment you are already talking, tracking initiative, and making something up. The tool has to be operable with the small fraction of attention that is left.
So the wedge here is not features. It is that operating it has to cost almost nothing.
What I built
All of it: the audio engine, the state model, the play surface, the scene builder, campaigns, the library, hotkeys and their OS-level registration, the settings layer, and the export and import path.
The architecture is unusual for an audio app and deliberately so. Nothing in the application calls play. Session state is the single source of truth, the audio graph is derived from it, and exactly one module is permitted to start or stop a sound. Every state change is a timestamped serialisable command. That is what makes “play this only for me” a routing decision rather than a branch in playback logic, and it is why the same state can drive a local mix and a remote stream without either knowing about the other.
Technical detail
Tauri with a TypeScript and Svelte 5 front end over the Web Audio API. One audio context, five buses, two output taps – monitor and broadcast – off a single graph. Two stems on the wire: music, and everything else.
The core is enforced pure by two mechanisms that each catch what the other
cannot: a lint rule for imports, and a second TypeScript pass with no DOM
library and no ambient types, which makes touching document or AudioContext
a compile error rather than a lint warning that can be silenced inline.
This is the project that shows the toolchain is not the skill. It is TypeScript, Rust and Web Audio rather than C# and Unity, and the reasoning behind it is the same reasoning.