A Tampermonkey/Violentmonkey userscript that reads new chat messages aloud on any website using the browser's built-in text-to-speech, with a floating control panel for playback (Start / Pause / Skip / Stop) and per-user filtering.
The default container selector targets the Angular-based chat layout used on the primary site, so users of that site need zero manual configuration.
- Works on any website — use the "Pick Message Area" button to select any element on any page as the message source
- Live chat narration — new messages are spoken as they arrive
- Playback controls — Start, Pause, Skip current message, Stop (clears the queue)
- Smart body extraction — strips timestamps, date stamps, sender prefixes, and badge words injected by the chat UI
- URL redaction — URLs in messages are replaced with "URL posted" instead of being read aloud
- Optional sender name — toggle whether to prepend the sender's name (off by default)
- Optional first-name only — "Alice Smith" → "Alice"
- Optional timestamp announcement — toggle whether to read "10:57 AM" out loud
- Ignore list — comma-separated usernames to skip (matches full or first name)
- Skip-own-messages — don't read messages you yourself sent
- Allowed sites — restrict the panel to specific URLs; leave empty to show on all sites
- Voice / rate / volume picker — uses any voice installed in your OS
- Collapsible UI — playback controls remain visible while settings collapse
- Draggable panel — move it out of the way
- Persistent config — settings saved to
localStorage, survive page reloads - Live queue counter — see how many messages are waiting to be read
- Install Tampermonkey (Chrome / Edge / Firefox / Safari) or Violentmonkey.
- Open the Tampermonkey dashboard.
- Click + → Create a new script.
- Delete the template code.
- Paste the contents of
streaming-message-reader.user.js. - Save (
Ctrl+S). - Open the target site. The orange-bordered Streaming Message Reader panel appears in the top-right corner.
- Click Test Voice to confirm audio works.
- (Optional) Click Pick Message Area and click the chat message list to lock in the exact DOM selector. Auto-detect handles this automatically for common chat patterns.
- Click ▶ Start.
New messages in the watched container will be spoken as they arrive.
| Button | Behavior |
|---|---|
| ▶ Start | Begins reading new messages. If paused, resumes. |
| ⏸ Pause | Pauses the current utterance. Queue is preserved. |
| ⏭ Skip | Cancels the current message and immediately starts the next queued message. |
| ⏹ Stop | Cancels current utterance, clears the queue, ignores incoming messages until Start. |
| Setting | Default | Description |
|---|---|---|
| Read new messages as they arrive | off | Master toggle for queueing new messages |
| Sender name | off | Prepend the sender's name to every message |
| First name only | on | Read "Alice" instead of "Alice Smith" |
| Timestamp | off | Prepend "10:57 AM" to every message |
| Skip own msgs | on | Combined with "My username" below |
| My username | empty | Your display name, used for the self-skip filter |
| Skip these users | empty | Comma-separated ignore list (matches full or first name) |
| Allowed sites | primary site | URL patterns — one per line; empty = show on all sites |
| Voice / Rate / Volume | system default / 1.0 / 1.0 | Standard text-to-speech parameters |
- Locates the chat container (uses the configured CSS selector, then auto-detects, then falls back to the element picker).
- Attaches a
MutationObserverto that container withsubtree: true. - When new nodes are added, tries Angular-specific extraction (
<app-st-compactmessage>) first, then falls back to generic extraction. - Generic extraction: finds sender in
<strong>,<b>, or elements with common class names; strips timestamps and date stamps; falls back toname: bodysplitting. - De-duplicates against a 5000-entry recent-messages buffer.
- Filters using the ignore list / self-skip.
- Builds the spoken string from toggleable parts (timestamp, sender, body); replaces URLs with "URL posted".
- Pushes onto a queue and feeds the browser's
SpeechSynthesisone utterance at a time.
A generation counter invalidates pending onend callbacks when the user hits Skip or Stop, so playback control is always precise.
- Chrome / Edge — fully supported (recommended).
- Firefox — supported; voice list may be smaller.
- Safari — supported with caveats; speech rate behavior differs.
Requires the Web Speech API (window.speechSynthesis), available in all modern browsers.
- Voice availability depends on your OS. Windows ships with "Microsoft David" / "Microsoft Zira"; macOS has many more.
- Chrome stops speech synthesis after ~15 seconds of continuous audio in some versions — usually fine since individual messages are short.
- On Angular SPAs, Angular component names can change between builds. The manual Pick Message Area button always works as a fallback.
- The script runs in the top frame only (
@all-frames false).
Single-file userscript — no build step required.
git clone https://github.com/sbcmsbgithub/streaming-message-reader.git
cd streaming-message-reader
# Edit
vim streaming-message-reader.user.js
# Test: paste the updated source into Tampermonkey's editor and reload the target tab.See CLAUDE.md for in-depth project context (architecture, DOM specifics, extraction strategy) — useful when iterating with Claude Code.
Pull requests welcome. Please:
- Bump the
// @versionfield in the userscript header following semver. - Add an entry to
CHANGELOG.md. - Test on a live session before submitting.
- The Tampermonkey project for making userscripts trivial to install.