Chrome extension that transcribes voice messages on WhatsApp Web, Telegram Web and Discord — entirely in the browser, with no audio sent anywhere.
Transcription runs locally using Whisper via transformers.js and ONNX Runtime WebAssembly. No API key or account required.
| Platform | URL |
|---|---|
| WhatsApp Web | web.whatsapp.com |
| Telegram Web | web.telegram.org |
| Discord | discord.com |
- 100% local — no API key, no server, no audio sent anywhere
- On-demand — only active while the floating panel is open; dormant otherwise
- Cached — the same audio is never transcribed twice (SHA-256 hash stored in
chrome.storage.local) - Three Whisper models — Tiny (~40 MB), Small (~130 MB, recommended), Large v3 Turbo (~800 MB)
- 14 languages — select your language once in settings
- Model management — download and delete models directly from the settings panel
- Visual Queue — placeholder bubbles indicate which queued voice messages are waiting to be processed
- Pause — keep the panel open but temporarily stop transcribing new messages
- Silent Mode — automatically mute the voice message while transcribing
- Export — save all transcriptions to a text file (in settings)
- Timestamps — optional [MM:SS] markers in the text
- Floating panel — draggable, persistent across navigation, close and reopen any time
- Copy — click any entry to copy it, or use "Copy all"
- Stop — cancel an in-progress transcription without unloading the model from memory
- Chrome 112 or later (Manifest V3 + Offscreen Documents API)
- Node.js 18 or later (build only)
git clone https://github.com/YOUR_USERNAME/whatsapp-web-transcriber.git
cd whatsapp-web-transcriber
npm install
npm run buildThen load the extension in Chrome:
- Go to
chrome://extensions/ - Enable Developer mode (top-right toggle)
- Click Load unpacked → select the
dist/folder
- Open any supported platform
- Click the extension icon (top-right of Chrome) to open the transcription panel
- First run: open ⚙ Settings, select your language, then choose a model — it downloads automatically (~130 MB for the recommended Whisper Small)
- Keep the panel open and play any voice message — the transcription appears in the panel
- Click any entry to copy it to the clipboard
Note: The extension is dormant while the panel is closed — no audio is intercepted or transcribed.
| Button | Action |
|---|---|
| ⚙️ | Settings — language, model, download, export, timestamps and more |
| ⏸️/ |
Pause/Resume transcription (keeps panel open but stops processing new messages) |
| 📋 | Copy all transcriptions to clipboard |
| 🔊/🔇 | Toggle Silent Mode (mutes the audio of the voice message when transcribing) |
| 🗑 | Clear the visible list (cache preserved — same audio won't re-transcribe) |
| ⏹ | Stop the current in-progress transcription |
| ✕ | Hide panel (reopen from extension icon) |
Export and cache controls are in ⚙ Settings.
src/
├── injected/interceptor.ts — HTMLMediaElement.src patch (world: MAIN, document_start)
├── content/index.ts — fetch, OGG/Opus decode, cache check, port to SW
├── background/service-worker.ts — port router, offscreen lifecycle, model cache reconciliation
├── offscreen/whisper.ts — Whisper pipeline (transformers.js + ONNX WASM)
└── ui/ — floating panel, settings, styles, drag
injected/interceptor.ts runs in the page context (world: "MAIN") at document_start, before any site JS. It patches HTMLMediaElement.prototype.src. Two patterns are captured:
- Blob URLs (
blob:https://...): WhatsApp Web and Telegram Web download audio to memory before playing it, producing a blob URL. - Discord CDN URLs (
cdn.discordapp.com/.../voice-message*): Discord voice messages are served directly from the CDN as signed HTTPS URLs.
The manifest restricts which origins the script is injected on, so the patch is a no-op on every other site.
Site assigns audio URL (blob or CDN)
→ injected script (.src setter fires → postMessage to content script)
→ content script
fetch(url) → ArrayBuffer
AudioContext.decodeAudioData() → 16 kHz Float32 PCM
SHA-256 hash → check chrome.storage.local
[cache hit] → display immediately
[cache miss] → open port to service worker
→ service worker
chrome.runtime.sendMessage(WHISPER_TRANSCRIBE)
→ offscreen document
load / reuse Whisper model (ONNX WASM)
run inference
← text
store in chrome.storage.local
← TRANSCRIBE_RESULT via port
display in panel
OGG/Opus decoding happens in the content script (not the offscreen document) because Chrome MV3 offscreen documents lack the codec in AudioContext.
The content-script ↔ service-worker channel is a long-lived port (chrome.runtime.connect), which keeps the MV3 service worker alive for the entire duration of the transcription.
Models are fetched from Hugging Face via transformers.js and stored in the browser's Cache API (caches.open('transformers-cache')). After the first download no network request is made.
| Model | Quantization | Size | Notes |
|---|---|---|---|
onnx-community/whisper-tiny |
q4 | ~40 MB | fastest |
onnx-community/whisper-small |
q4 | ~130 MB | default · recommended |
onnx-community/whisper-large-v3-turbo |
q4 | ~800 MB | most accurate |
WebGPU is intentionally disabled — ONNX Runtime WebGPU has numerical overflow issues with q4 models on Windows, causing Whisper to hallucinate [Music] instead of speech. WASM inference uses up to 4 threads when SharedArrayBuffer is available.
(Note: tracked upstream at microsoft/onnxruntime#26732, fix proposed in PR #29599 — locally validated, ~2x speedup with no accuracy loss once merged. See docs/webgpu-onnxruntime-fix.md for test methodology and re-enable steps.)
Key inference settings that reduce hallucinations and repetition loops:
num_beams: 1,temperature: 0— fast deterministic decodingrepetition_penalty: 1.1— discourages repeating tokenscondition_on_prev_tokens: false— prevents chunk-boundary repetitioncompression_ratio_threshold: 2.4,no_speech_threshold: 0.5— discard non-speech chunks- Post-processing: consecutive identical segments are stripped before display
- No audio leaves the browser
- No telemetry or analytics
- No external network requests after the one-time model download from Hugging Face
- Transcription cache stored in
chrome.storage.local, clearable from the panel at any time
- Summarization: Use a local SLM (Small Language Model) via WebGPU to summarize very long voice messages into bullet points.
- Built-in Translation: Use Whisper's native
task: "translate"to automatically translate foreign language voice messages to English (or your native language) on the fly. - Speaker Diarization: Separate and label text by different speakers (e.g., "Speaker 1", "Speaker 2") for group audio or calls.
npm run build # production build → dist/
npm run dev # same as build (reload extension manually in chrome://extensions after each build)
npm run lint # TypeScript type check (tsc --noEmit)MIT
- transformers.js — in-browser Whisper via ONNX Runtime
- onnx-community — quantized Whisper ONNX models