Your Google Meet transcripts, saved on your own machine. No cloud. No account. No bot in your call.
LocalCaptions is a Chrome extension that shows your Google Meet transcript in real time, lets you copy any part instantly, and auto-saves every meeting to a searchable history you can revisit anytime.
Unlike cloud transcription tools, LocalCaptions never sends your conversations anywhere. It reads Google Meet's own built-in captions (you turn on CC, Google does the speech-to-text) and cleans them into a proper speaker-by-speaker transcript that lives only in your browser. It makes zero network requests - you own your data, full stop.
| LocalCaptions | Typical cloud note-taker | |
|---|---|---|
| Where your transcript lives | Your browser only | A company's servers |
| Network requests | None | Uploads audio/transcript |
| Account / sign-up | None | Required |
| Bot joins your meeting | No | Often |
| Cost | Free & open source | Freemium / subscription |
| Works offline-ish | Yes (only needs Meet itself online) | No |
- Real-time overlay panel inside the Meet tab - draggable, resizable, minimizable.
- Copy anything, live - select text directly, copy a single line, or copy the whole transcript.
- Auto-saved history - every meeting is stored locally (IndexedDB) as it happens, so you never lose a transcript even if the tab crashes.
- Searchable history page - browse, full-text search, view, and re-export any past meeting.
- Export to
.txtor Markdown.md. - Smart de-duplication - Meet's captions constantly rewrite themselves word-by-word; LocalCaptions stabilizes them into one clean line per utterance (no repeated/garbled text).
- Resilient capture - layered DOM selectors with structural fallbacks survive Google's frequent UI changes.
- Private by design - no network calls, no analytics, no API keys, no telemetry. See PRIVACY.md.
LocalCaptions isn't on the Chrome Web Store - you load it directly as an unpacked extension.
- Download the code - either:
git clone https://github.com/Obaid51/LocalCaptions.git, or- download the ZIP from the GitHub page (Code → Download ZIP) and unzip it.
- Open
chrome://extensionsin Chrome (or any Chromium browser: Edge, Brave, Arc, etc.). - Turn on Developer mode (top-right toggle).
- Click Load unpacked and select the project folder (the one containing
manifest.json). - LocalCaptions appears in your toolbar. Pin it for quick access (puzzle-piece icon → pin).
A welcome tab (the history page) opens on first install. To update later, git pull (or re-download)
and click the Reload ↻ icon on the extension card.
- Join any Google Meet call.
- Turn on captions: click the CC button in Meet's toolbar (or press
c).- If you forget, the LocalCaptions panel shows a "Turn on captions" button that does it for you.
- The LocalCaptions panel appears (top-right) and fills with the transcript in real time.
- Drag the header to move it; drag the bottom-right corner to resize.
- Select text and
Ctrl/Cmd+Cto copy any portion, or hover a line and click ⧉. - Header buttons: search, copy all, download
.txt, open history, minimize, hide.
- When the meeting ends, the transcript is already saved.
- Click the toolbar icon → Open full history (or the panel's history button) to browse, search, view, and export past meetings anytime.
Note: LocalCaptions captures whatever Google Meet's captions produce. Captions must be on for capture to work - Google only generates caption text while CC is enabled.
Google Meet captions (DOM)
| MutationObserver + 1s poll (meet-scraper.js)
v
Transcript engine (transcript-engine.js) <- dedupe + stabilize utterances
| update -> live panel | persist -> save
v v
In-page panel Service worker -> IndexedDB
(panel.js, Shadow DOM) (service-worker.js, lib/storage.js)
^
Popup & History pages read the same DB
meet-scraper.js- finds the captions region ([role="region"][aria-label="Captions"], with fallbacks), extracts each speaker turn, filters out Meet's own UI controls, and assigns every caption row a stable key. ThrottledMutationObserver+ a 1-second polling safety net.transcript-engine.js- pure, unit-tested logic. Meet rewrites captions live ("mic, so" -> "microphones only"); the engine tracks each turn by a stable id and only commits stabilized text, so you get one clean line per utterance. It also merges a turn that Google re-renders under a fresh DOM node mid-sentence (so it doesn't duplicate), while never merging two genuinely separate utterances.panel.js- the overlay, rendered in a Shadow DOM so Meet's CSS can't interfere.service-worker.js+lib/storage.js- persist every turn to IndexedDB as it's finalized. The content script (meet.google.com origin) streams turns to the worker (extension origin), which owns the database.popup/andhistory/- read that same database directly.
Requirements: Node 18+ (only for the test suite and icon generation - the extension itself has no runtime dependencies and no build step).
npm install # installs jsdom (test-only dependency)
npm test # runs the engine + scraper + integration suites (node --test)
npm run gen-icons # regenerates icons/*.png from tools/gen-icons.mjs (no deps)- Tests live in
test/(28 cases, via jsdom) and cover:- the deduplication engine - growth, stabilization, element-replacement merge, id-collision safety, same-batch merge eligibility, separate-turn detection, flush;
- the DOM scraper - region detection, class-based + structural extraction, stable keying, teardown safety, participants-panel rejection, UI-control filtering;
- an end-to-end integration test that runs the real scraper + engine against a scripted, mutating Meet caption DOM and asserts a clean, merged, lossless transcript.
- No build step - the extension loads the source directly.
manifest.json MV3 manifest
src/
content/
transcript-engine.js pure dedup/stabilize logic (UMD, tested)
meet-scraper.js Meet DOM selectors + observers (UMD, tested)
panel.js Shadow-DOM real-time overlay
panel.css host anchor styles
content.js entry: wires scraper -> engine -> panel -> storage
background/
service-worker.js IndexedDB writer + history tab opener
lib/
storage.js IndexedDB layer (shared by SW/popup/history)
popup/ toolbar popup (status, copy, open history)
history/ full transcript browser (search/view/export/delete)
icons/ generated PNG icons
tools/gen-icons.mjs zero-dependency PNG icon generator
test/ node:test suites (jsdom)
See CONTRIBUTING.md for how to help - especially keeping the Meet selectors working.
Google Meet changes its HTML often. If transcripts stop appearing, the selectors are the
single place to update: src/content/meet-scraper.js -> SELECTORS. The scraper already
falls back to structure (avatar-anchored blocks, largest text node) when class names change,
so most Meet updates won't break it - but a major redesign might need a selector refresh.
Please open an issue if you hit this.
LocalCaptions makes zero network requests. Transcripts are stored only in your browser's local IndexedDB and never leave your device. Uninstalling the extension or using Clear all in the history page removes the data. Full details in PRIVACY.md.
- Captures only what Google Meet's captions emit; accuracy is Google's, not ours.
- Google Meet only (by design). Zoom/Teams are not supported.
- Captions must be turned on during the meeting.
- Live captions self-correct, so very short utterances spoken in the last moment before an abrupt tab close may occasionally be truncated.
Contributions are welcome - bug reports, selector fixes, and features. Start with
CONTRIBUTING.md. Please run npm test before opening a PR.
MIT © LocalCaptions contributors. Do whatever you want with it - it's your data and your code.