Any two of 20 languages, both ways. They speak English and you hear Turkish; you speak Turkish and they hear English, in your own voice. A Chrome extension with no backend: the keys stay in your browser.
| Languages | 20, any two of them; English and Turkish by default |
| Modes | Voice, subtitles only or off, for each direction |
| Delay | About 1.2 s after each sentence, measured with the real services; Meet's own delay comes on top |
| Backend | None |
| Interface | English or Turkish |
- Twenty languages, any two of them in either direction: English, Turkish, German, French, Spanish, Italian, Portuguese, Dutch, Polish, Russian, Ukrainian, Arabic, Greek, Romanian, Swedish, Indonesian, Hindi, Chinese, Japanese and Korean.
- Listens to the call through Chrome tab capture and to you through your microphone, transcribes both sides with ElevenLabs Scribe v2 Realtime, translates with Claude (or for free, see below) and speaks the result with ElevenLabs v4 Turbo, which keeps a natural pace and starts as fast as the older Flash v2.5. Each sentence goes to the voice with the one before it (and after it, when known), so the intonation carries on between sentences.
- Your translated voice goes into the call in place of your microphone. Meet keeps working as usual, and muting in Meet stops everything that would reach the call.
- A mode for each direction: spoken translation, subtitles only (no speech credits used) or off, switchable mid-call from the popup or the panel on the page.
- In voice mode you hear only the translation; their own voice is muted. In subtitles-only mode and with translation off you hear them normally.
- Subtitles like a film's, low in the Meet window above Meet's controls: only the translation, in large type, unless the settings ask for their own words under it. While they are still talking, a dimmed draft of their words so far, translated on your computer by Chrome's own translator (free, whichever translation the call uses), stands where the sentence's translation will come. Each line stays long enough to read, at most two at a time. They move above Meet's own captions when those are on, and never take a click.
- Your own voice clone, recorded on the settings page. The other person's voice is cloned only after you confirm they agreed, and the clone is deleted when the call ends.
- A panel with both sides' subtitles, the mode switches, the live delay and a usage counter with an estimated cost. It starts as a small button low on the left of the Meet window, opens with a click and can be dragged anywhere. A mark on the button shows when there is a warning to read.
- Optional call recording: the Meet tab's picture with both sides' audio, saved as MP4 on this computer only.
- Echo guard, a speaker mode for calls without headphones, automatic reconnects, and Meet's microphone followed automatically.
- Leaving the call, or leaving Meet in the same tab, ends the translation by itself, so nothing keeps listening or billing. If it stops because of an error (a used-up quota, for example), the panel stays open and says why.
- Settings changed mid-call apply at once, the call languages included. A service that stops answering is given up after a few seconds, so one stuck sentence never holds up the ones after it.
Read this first.
- The translated voice starts about 1.2 s after a sentence ends (measured with the real services, Meet's own delay on top; 1.5 s before version 1.1). Most of it is the wait for silence: the extension watches the sound level itself and closes a sentence after 0.7 s of silence, or sooner when the words so far read as a finished sentence. Speech recognition's own silence detection answered about 0.4 s later, so it is not used. In a long turn, finished sentences are translated while the speaker keeps going. Settings can shorten the wait to 0.5 s (about 1 s in all), at the cost of a long sentence being split now and then.
- Free translation through Chrome's built-in translator is literal: "saat üç" comes out as "15.00 on Friday". Everyday meeting phrases are put in plain words first, but a free DeepL key or Claude gives natural sentences.
- Two Meet participants on the same computer garble each other's audio, because Meet's adaptive audio and Chrome's echo cancellation treat them as one room. Test with a second device or a second person.
- Tested in real Meet calls with two and three participants, and automatically in Chromium against a mock Meet call. Meet's page changes over time, so try a call before an important meeting.
- Group calls work: in a test call with three English speakers taking turns (the real speech services, a stand-in Meet page) each one was translated in order. Every remote voice comes through one translation voice, and when two people talk at the same time usually only one of them is caught.
- Cloning someone's voice needs their consent, under ElevenLabs' terms and data protection law.
Meet tab audio -> Scribe (their language) -> Claude or free -> ElevenLabs -> your headphones and the subtitle bar
your microphone -> Scribe (your language) -> Claude or free -> ElevenLabs -> the call, in place of your mic
| Part | Where it runs | What it does |
|---|---|---|
src/content/page-hook.js |
Meet's page | Swaps the microphone track on Meet's RTCRtpSender for the translation track, mirrors Meet's mute |
src/content/bridge.js |
Content script | Connects the page with the engine, shows the panel and the subtitle bar |
src/content/subtitle-bar.js |
Content script | The subtitles low in the Meet window |
src/offscreen/engine.js |
Offscreen document | Tab capture, microphone, speech recognition, translation, speech, voice clone, recording |
src/offscreen/stt.js |
Offscreen document | Scribe v2 Realtime client: reconnects, keeps audio while reconnecting, recovers a segment the server never commits |
src/shared/turn.js |
Offscreen document | Hears from the sound level when a speaker has finished, so each sentence is closed at once |
src/offscreen/drafts.js |
Offscreen document | Their words so far, translated on this computer while they talk, for the subtitle drafts |
src/offscreen/free-translator.js |
Offscreen document | Free translation: tries free services from the best down |
src/background/service-worker.js |
Service worker | Starts and stops sessions, keeps state for the popup |
Choose Free as the translation model in the settings and no Anthropic key is needed. The extension tries free services from the best quality down and moves to the next one by itself when a service runs out of quota, hits a rate limit, is slow or fails. The page never shows which one translated.
| Order | Service | Free allowance | Needs |
|---|---|---|---|
| 1 | DeepL API Free | 500,000 characters a month | Your free DeepL key |
| 2 | Mistral (mistral-medium-latest) |
Free plan with rate limits | Your free Mistral key; turn off training on your data in Mistral's privacy settings |
| 3 | Groq openai/gpt-oss-120b |
Daily request and token limits per model | Your free Groq key |
| 4 | Groq qwen/qwen3.8-27b |
Its own daily limits | The same Groq key |
| 5 | Chrome's built-in translator | No limit, runs on your computer | Chrome 138 or newer on desktop |
- With no keys at all, Chrome's translator does the work. Its language packs download once, after a click: the settings page has a button for it, and the popup's Start button does it too.
- Chrome's translator takes everyday meeting talk word for word ("Let's touch base on Monday" came out as "Pazartesi günü üsse dokunalım", "Sesim geliyor mu?" as "Is my voice coming?"), so such phrases are put in plainer words of the same language before it translates (
src/shared/plain.js, English and Turkish, each rewrite checked through Chrome's translator), and the special terms from the settings come back spelled as written. A free DeepL key still gives much more natural sentences. - Keys are optional and belong to each person, because free quotas are per account. A key built into the extension could be copied out of it, and everyone would share one quota.
- Gemini is left out: on its free tier Google may use what you send to improve its products, and people may read it.
- Speech recognition and speech still use ElevenLabs, so an ElevenLabs key is always needed.
- Chrome 116 or newer (or another Chromium browser); 138 or newer for free translation without keys
- Node.js 18 or newer, only to build from source
- An ElevenLabs API key (voice cloning needs a paid plan), and an Anthropic API key unless you choose free translation
- Headphones
- Download meet-interpreter.zip from the latest release and unzip it.
- Open
chrome://extensions, turn on Developer mode, choose Load unpacked and pick the unzippedmeet-interpreterfolder. - The settings page opens by itself. The rest, from the ElevenLabs key to your first call, is in the step-by-step guide with pictures: SETUP.md, in Turkish SETUP.tr.md.
Keep the folder where it is: Chrome runs the extension from it. To update, replace the folder's contents with the new release and press reload on the extension.
Set up WSL first with this guide: https://x.com/UfukDegen/status/1944066889346429338, then follow the macOS steps inside WSL and load the dist folder from \\wsl$\ in Chrome.
git clone https://github.com/UfukNode/meet-interpreter.git
cd meet-interpreter
npm install
npm run buildBuilt dist/
Open chrome://extensions, turn on Developer mode, choose Load unpacked and pick the dist folder. After pulling changes, run npm run build again and press reload on the extension. npm run zip builds and packs dist into meet-interpreter.zip, the file a release carries.
npm test # unit tests: audio, sentences, echo guard, ordering, speech recognition protocol, settings
npm run test:e2e # builds, loads the extension into Chromium and drives a mock Meet call
npm run test:scenarios # the same mock call through restarts, a reload, failures, settings changes and leaving
npm run test:api # live check of every API with latency numbers (keys in .env)
npm run test:latency # end of a sentence to its first translated sound, with the real ElevenLabs
npm run test:free # live check of DeepL, Mistral and Groq free plans (their keys in .env)
node test/meet/mock-call-demo.mjs # both directions with the real ElevenLabs, recorded by the extension
node test/meet/group-call.mjs # a four-person call with the real ElevenLabs and Chrome's translator- The end-to-end test needs no keys: besides the connection checks it runs a full translation round trip against a local stand-in for the ElevenLabs and Anthropic APIs (
test/e2e/mock-api.mjs, reached through a local proxy). With keys in.envit repeats the round trip against the real APIs. - The scenario test needs no keys either. The stand-in fails on purpose (a dropped connection, used-up quotas, an overloaded Claude), and the call is restarted, reloaded, switched between modes, changed in the settings, left and closed.
- Test speech is made with the macOS
saycommand and ffmpeg, so the audio tests run on macOS. - For a real call there is a debug copy, compiled in only by
npm run build:debug: withlocalStorage['meet-tercuman-debug'] = '<port>'set on the Meet page andnode test/meet/debug-sink-server.mjs <port>running, the extension copies the exact audio it sends to speech recognition, the transcripts and the recording to127.0.0.1, and the panel shows a DEBUG badge. The normal build ignores the flag.
- No backend, no analytics and no remote code: everything ships inside the extension.
- API keys are kept in
chrome.storage.localand sent only to the service they belong to:api.elevenlabs.io,api.anthropic.com, and DeepL, Mistral or Groq when you add their keys. - The script inside Meet's page makes no network requests. Audio reaches it through a private
MessagePort. - Nothing reaches the call while Meet shows you as muted, while translation to them is off, or before the call has started.
- The other person's voice is cloned only after you tick the consent box, and the clone is deleted after the call unless you change that setting.
- Recordings stay in the extension's storage on your computer until you save or delete them.
- The panel's controls answer only your own clicks and keys: a script in Meet's page cannot tick the consent box or switch a mode.
- The debug copy exists only in a debug build (
npm run build:debug), shows a DEBUG badge while it runs, and only ever sends to127.0.0.1.
The panel counts what a call uses and estimates the cost from list prices: Scribe v2 Realtime is $0.39 an hour per active direction, Flash v2.5 and v4 Turbo speech is $0.04 per 1,000 characters, and Claude is billed per token (Haiku 5.5 is the cheapest). Subtitles-only mode makes no speech at all, and free translation costs nothing. A direction turned off listens to nothing, and 30 seconds after the call ends the translation stops by itself.