feat(desktop): play a message aloud, with a TL;DR audio mode
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
**Motivation**
Buzz has voice where conversation is *live* — huddles — and voice input is growing (#2708 dictation in the composer, #4166 voice notes). There's nothing on the output side for the medium where most agent work actually lands: **a written message in a channel.**
Agent replies are long by nature: a run report, a diff summary, a verdict with caveats. Reading each one is the bottleneck, and it bites hardest exactly when reading is least available — away from the desk, hands on another task, or if listening is simply how you take information in best.
Huddles don't cover this. A huddle is synchronous and ephemeral; the report you want read to you was posted twenty minutes ago in a thread.
There's an accessibility angle too. A screen reader can read a Buzz message today, but it reads *everything* — nav, timestamps, code fences character by character. A control that speaks the message's prose is a different and better affordance than general-purpose assistive tech.
**Proposed solution**
**1. Play aloud, per message**
- An entry in the existing message hover/overflow menu. No new persistent chrome.
- Speaks the message body. Skips fenced code blocks by default (a diff read aloud is noise), with a setting to include them.
- Renders Markdown *for the ear*: headings become pauses, list items become sentences, tables are announced by row, and `**bold**` does not become "asterisk asterisk".
- Standard transport — play / pause / stop — plus a visible indicator of which message is currently speaking.
- **Exactly one message speaks at a time.** Starting a second stops the first.
**2. TL;DR audio**
- A second entry in the same menu: speak a summary instead of the full body.
- For a long agent report the useful twenty seconds is the verdict — what shipped, what broke, what needs a decision. Speaking four minutes of detail to reach it defeats the purpose.
- A no-model version is viable and worth shipping first: speak the opening paragraph plus any trailing `Summary` / `Status` / `Conclusion` section. A model-generated summary is a natural follow-up but shouldn't gate the feature.
**3. Backend**
- This should ride on **#3720**'s pluggable TTS abstraction rather than introduce a second, parallel one. If #3720 lands first, this is largely a UI surface on top of it.
- A zero-config default matters for adoption: every target platform ships a usable system voice (`SpeechSynthesis` in the webview, or platform TTS through Tauri). Self-hosted deployments then point elsewhere via #3720.
**Alternatives considered**
- **OS-level screen reader** — already possible, and the reason this is still worth building: it reads the whole interface rather than the message, and has no notion of "just the conclusion."
- **Do it in a huddle** — wrong shape. Synchronous and ephemeral; doesn't help with a message already in the log.
- **Client-side userscript / external tool** — what I do today (below). It works, but it can't see message boundaries or thread structure, so it can't offer "read *this* message" or "read just the verdict."
- **Auto-play new messages** — deliberately rejected. Unsolicited audio in a chat client is hostile. On request only.
**Additional context**
**Duplicate search:** searched open issues for `voice`, `tts`, `speak`, `aloud`. Closest neighbours, none of which cover playback of a written message:
- **#3720** — pluggable STT/TTS backends for huddles → the natural dependency for this
- **#2708** — dictation in the message composer → the input-side counterpart
- **#4166** — durable recorded voice-note messages → human-recorded audio, not synthesis
**Prior art, and two warnings worth inheriting.** I run a local voice stack ("EchoType") against Buzz — dictation into the composer, and agent replies spoken back. Two things I got badly wrong there are cheap to design for now and expensive to retrofit:
1. **Concurrent playback needs a single owner.** Two things speaking at once isn't a degraded experience, it's an unusable one — and it happens immediately once more than one surface can trigger speech. I ended up needing one mutual-exclusion primitive that *every* playback path acquires before it may open the output device. Retrofitting that across paths each written assuming it was alone was the bulk of the work. If playback lands as "one message at a time" from day one, that cost never arrives.
2. **Audio output devices disappear.** A monitor going to sleep takes its HDMI audio device with it. Most audio stacks enumerate devices once at initialisation, so every subsequent call fails against a stale handle — and if that exception escapes the playback thread, speech dies silently mid-sentence and reads to the user as "the feature is broken" rather than "the device went away." Playback needs to re-initialise and retry, and to fail loudly when it truly can't.
**On implementation:** happy to contribute this rather than just file it, if maintainers are interested in the direction and want to weigh in on where the TTS seam should sit relative to #3720. Flagging that as a question rather than opening a cold PR against a design that hasn't been agreed.
**Out of scope for this issue:** voice input to the composer (#2708), anything huddle-related, and auto-play.
Contributor guide
Research direction
Start by reading #3720's pluggable STT/TTS abstraction and inspect the existing message hover/overflow menu in the desktop client. Define the UI and backend seam around that dependency, then verify that full-message and TL;DR playback share one active message, stop earlier playback, skip fenced code by default, and handle unavailable audio devices visibly.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, tauri
- Domain
- accessibility, desktop
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100