The old version of TransLee worked. People used it. It just made you wait.
The Old Pipeline: Record, Wait, Stitch
The original app was built in Flutter, with a Node.js server in the middle talking to OpenAI’s Whisper for speech-to-text and GPT for translation. The flow looked like this:
[Record 5-15 seconds] -> [Whisper: speech to text]
-> [GPT: translate text, using the tail
of the previous chunk for context]
-> [Text-to-speech]
-> [Play the translated audio]
⬑ repeat, waiting the full cycle each time ⬏
Five to fifteen seconds isn’t a random number. It was a setting I chose on purpose, because shorter chunks meant the translation kept losing the thread mid-sentence, and longer chunks meant a longer wait before you heard anything at all. The fix for the “losing the thread” problem was to keep the last bit of the previous chunk’s text and glue it onto the front of the next one before sending it off to be translated, so GPT had enough context to know what was going on. It worked. It also meant every single sentence arrived several seconds after it was actually spoken.
The New Pipeline: One Open Connection, No Waiting
Gemini Live Translate doesn’t work in chunks at all. The browser opens one WebSocket connection directly to Google’s servers and keeps it open for the whole session:
[Mic, 16kHz PCM16 audio] --WebSocket-->
[gemini-3.5-live-translate-preview]
--stream--> [Translated voice, 24kHz PCM audio]
+ [Live text transcript, both languages]
There’s no record-then-send step. Audio goes out continuously as you speak, and translated audio comes back continuously as the model produces it. Google describes the model as streaming translated audio in roughly 100-millisecond chunks as soon as it has enough context to be confident, and that matches what it actually feels like to use: not instant, but a short, steady lag instead of a long, fixed one.
The model also detects the spoken language on its own. The old app only ever supported Korean and English, chosen manually. The new one figures out what language you’re speaking and translates it to a target you pick.
How Real Is “Real Time,” Actually?
Worth being precise here instead of just saying “instant.” A third-party benchmark published the same day Gemini 3.5 Live Translate launched (LiveLingo Research, June 9, 2026) tested it against three other systems, including Google’s own older Cloud Translation pipeline and a Whisper-plus-GPT-4o-mini setup similar to what my old app used. Gemini Live Translate scored 4.93 out of 5 for translation comprehension accuracy, the best of the four, and had a median latency of about 2.9 seconds from the start of speech to the first translated audio.
Google’s own benchmark calls a 2.9-second lag “real time.” I mention that not to argue with it, but because it’s a useful yardstick: TransLee’s lag, in normal use, feels shorter than that and never gets worse the longer you talk, which is the part that actually matters when you’re watching a five-minute news segment instead of a five-second clip.
The Landmine: One Wrong Field Name, One Cryptic Error
Before any audio can flow over that WebSocket connection, the browser has to send one message first, a kind of form the server reads before it says “go ahead.” That first message is called the setup message. It tells the server which model to use, what kind of output you want back, and, for a translator, which language to translate into. Get any part of that form wrong, and the server closes the connection before you ever get to test whether the translation itself works.
The setting that tells the model which language to translate into is called translationConfig. Where it sits inside that form matters as much as including it at all. JSON, the text format these setup messages are written in, is strict about structure, a setting placed one level too high or too low is treated as if it were never sent, the same way a form-scanning machine won’t find your zip code if you wrote it in the city box instead of the zip code box.
Getting this part of the handshake right took an evening by itself. The setup message I sent looked reasonable:
{
"model": "models/gemini-3.5-live-translate-preview",
"generationConfig": { "responseModalities": ["AUDIO"] },
"translationConfig": { "targetLanguageCode": "en" }
}
The server closed the connection every time with code 1007 and this message:
Invalid JSON payload received. Unknown name "translationConfig" at 'setup': Cannot find field.
1007 is a standard WebSocket status code, not something Google made up for this API, it always means roughly “I couldn’t understand what you sent me.” The specific complaint, “Cannot find field,” meant the server was looking for translationConfig somewhere it wasn’t. The fix, once we found it, was one level of nesting: translationConfig has to live inside generationConfig, not at the top level of setup.
"generationConfig": {
"responseModalities": ["AUDIO"],
"translationConfig": { "targetLanguageCode": "en" }
}
Google’s published documentation didn’t spell this exact nesting out clearly enough to catch on the first try. We found the actual answer by reading the source code of Google’s own google-genai SDK, version 2.11.0, specifically the file _live_converters.py, the code Google’s own tools use to build this exact message. Older versions of the same SDK use a different nesting entirely, which is why some example code circulating online looks like it flatly contradicts other example code, they were written against different SDK versions. If you’re building on this API and hitting the same 1007 error, check your SDK version before you check anything else. Reading a library’s actual source code, not just its docs, is a reliable habit whenever the docs and your error message disagree, code can’t drift out of date the way a doc page sometimes does.
I haven’t measured a clean cost comparison between the old Whisper-plus-GPT pipeline and the new streaming model yet, minutes of use aren’t the same unit as API calls. I’ll come back with real numbers once I’ve used this enough to report something honest.