Skip to content

feat(desktop): play a message aloud, with a TL;DR audio mode #4403

Description

@boydyngo

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

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:

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions