Inspiration

Every streamer has been there: you finish a two-hour broadcast, check the VOD, and realize your audio sounded like you were streaming from inside a tin can. Room acoustics are invisible until they ruin a recording. Professional acoustic measurement tools exist, but they cost hundreds of dollars and require expertise to interpret. We wanted to build something that sits in a streamer's workflow and gives real-time, plain-language feedback about their room's sound -- right inside their stream.

What It Does

RoomTune Live is a local acoustic monitoring daemon that listens to your microphone, analyzes room acoustics in real time, and delivers results through a lightweight OBS overlay and Twitch chat integration. Viewers can type !roomcheck in chat to trigger an on-demand analysis. The system measures:

  • RT60 (Reverberation Time): How long sound takes to decay by 60 dB, estimated via energy decay curve analysis
  • Spectral Centroid: The "brightness" of the room's frequency response
  • Peak-to-RMS Crest Factor: A proxy for how dynamic versus compressed the room sound is

All processing happens locally on your machine. No audio is ever sent to a server.

How We Built It

The project is a TypeScript monorepo with three packages: a Node.js backend daemon, a Vite-powered frontend dashboard, and a shared protocol layer. All generated by Codex

Audio Analysis Pipeline

The backend captures audio using SoX (rec) and pipes raw PCM samples into a sliding analysis window. We compute a Short-Time Fourier Transform to extract frequency-domain features:

$$X[k] = \sum_{n=0}^{N-1} x[n] \cdot e^{-j 2\pi k n / N}$$

From the magnitude spectrum, we derive the spectral centroid as the weighted mean frequency:

$$f_c = \frac{\sum_{k} f_k \cdot |X[k]|^2}{\sum_{k} |X[k]|^2}$$

RT60 is estimated by fitting an energy decay curve to successive frames and extrapolating the -60 dB point.

Real-Time Communication

The daemon exposes a WebSocket server (port 8787) using a typed protocol defined in the shared package. The frontend connects and receives snapshot messages containing the latest metrics, which drive both the dashboard gauges and the OBS overlay. Twitch EventSub webhooks (via IRC) listen for !roomcheck commands and push analysis results back to chat.

Stack: TypeScript, Node.js, Vite, WebSocket (ws), SoX, Twitch IRC/EventSub, OBS Browser Source

Challenges We Ran Into

macOS Audio Permissions. Getting reliable microphone access from a Node.js process on macOS required navigating TCC (Transparency, Consent, and Control) permissions. OBS itself crashed due to a missing NSBluetoothAlwaysUsageDescription key in its Info.plist when Chromium's device enumeration ran -- a bug entirely outside our control that cost hours to diagnose from a raw crash report.

Twitch Token Lifecycle. OAuth tokens from Twitch's implicit grant flow expire silently. We had to add a simulation mode (TWITCH_SIMULATE=true) so we could test the full !roomcheck flow without a live token, and add defensive logging to surface silent failures.

Acoustic Accuracy vs. Latency. Longer analysis windows give more accurate RT60 estimates but increase latency. We settled on a 2048-sample FFT window at 44.1 kHz (about 46 ms), which balances responsiveness with meaningful spectral resolution.

Accomplishments We're Proud Of

  • The entire analysis pipeline runs locally with zero cloud dependencies -- privacy by architecture
  • The OBS overlay is a single HTML page that "just works" as a Browser Source
  • Twitch chat integration lets viewers participate in the acoustic analysis, turning a utility into an interactive experience
  • The codebase passes strict TypeScript checks across all three packages with a shared protocol that keeps frontend and backend in sync

What We Learned

  • Real-time audio DSP in JavaScript is surprisingly viable for monitoring (not synthesis) workloads
  • Twitch's EventSub and IRC systems have subtle failure modes that only surface under real network conditions
  • macOS privacy enforcement (TCC) is strict and unforgiving -- apps crash with SIGKILL, not a polite error dialog
  • Monorepo tooling with npm workspaces and a shared types package dramatically reduces protocol drift bugs

What's Next

  • Calibrated Measurements: Support reference microphone profiles (like the Shure SM57) to report absolute SPL and calibrated RT60
  • Historical Tracking: Store snapshots to a local SQLite database so streamers can see how their room acoustics change over time (new furniture, acoustic panels, etc.)
  • Multi-Mic Support: Compare measurements from different positions in the room to build a spatial acoustic map
  • OBS Plugin: Replace the Browser Source overlay with a native OBS plugin for tighter integration and lower overhead

Built With

Share this project:

Updates

Submission history