1 · User manual
There is no sign-in. Two links, one shared room code, one device each.
Opening a station
The lobby asks how many of you there are — two, three or four — and shows that many stations. Each person opens their own: Speaker A, B, and C or D if there are more of you. Each is asked for a name, which is optional — leave it blank and you appear as "Speaker A" or "Speaker B" to the other person. The name is stored per station on that device, so two tabs on one machine remain two different people.
The room code
Both stations must show the same room code before anything
will connect. It defaults to demo. Change it to anything you
like on both sides — it is what separates your conversation from anyone
else's, so pick something unguessable if you care.
Choosing your language
In Settings, set only the language you want to read and hear. Settings also lists what everyone else in the room reads, and how many translations each thing you say will cost. There is deliberately no control for the language you are about to speak. If detection gets an utterance wrong — most likely on very short ones — the setting exists to correct the reading side, not to pre-declare the speaking side.
Talking
Tap the microphone to start. Speak normally and pause when you finish a thought; the pause is what tells the system your utterance is complete. The status line under the button tracks the pipeline in real time: listening, transcribing and detecting, translating, generating audio, playing. Tap again to stop.
Reading the transcript
Every turn appears for both people, labelled with who spoke, the detected language, the language it was translated into, and how long the round trip took. Your own turns show what you said; theirs show both the original and your translation. Each turn has a replay button for the synthesised audio.
Saving the conversation
Once anything has been said, a Download button appears beside Clear in the Conversation panel. It saves a plain text file — every turn with who spoke, the language pair, what they said and what the others heard — named for the room and the time, so a folder of them sorts in order.
The file is assembled in your browser from what is already on your screen. Nothing is uploaded to produce it, and the server has no history to send: if you clear the conversation without downloading it, it is gone.
The tunnel server field
Under Settings there is a Tunnel server address. On the hosted demo you should never need to touch it. It exists for the on-premises lane, where you run the server yourself and need to point the pages at it. If you have typed one in and want the built-in default back, Reset to default appears beside the field.
2 · One conversation, step by step
What actually happens between one person finishing a sentence and the other person hearing it. Real numbers from the live deployment.
| Step | What happens | Roughly |
|---|---|---|
| 1 | Priya speaks Hindi. Her browser streams 16 kHz mono audio continuously to the server. | live |
| 2 | Voice detection notices she has stopped and closes the utterance. A pre-roll buffer of already-captured audio is prepended so the first word is not lost. | ~0.2 s |
| 3 | One model call transcribes the audio, identifies the language as Hindi, and translates it into Manoj's language in the same pass. | 1.6–2.1 s |
| 4 | The English text is synthesised as speech. Synthesis stops as soon as the signal goes quiet, rather than streaming the minutes of padding these models emit. | 1.2–1.5 s |
| 5 | Text and audio are delivered to Manoj; Priya gets the same record without the audio, so she can see what was sent on her behalf. | < 0.1 s |
| 6 | Manoj hears English. His microphone is gated while it plays, so a shared speaker is never re-transcribed into a loop. | ~3.0 s total |
The reverse direction — English into Telugu — measured 2.8 seconds. An earlier build of this same pipeline took 17.5 seconds; two changes closed that gap, and neither was a code optimisation. See the architecture section.
3 · Technical architecture
Two hosts that cannot be merged
The speaker pages are static and server-rendered, and sit happily on a serverless platform. The tunnel is a long-lived process holding a WebSocket open per speaker for the length of a conversation, and cannot run on a serverless platform at all. This is not a preference; it is the difference between a request/response host and a persistent one. So there are two deployment targets, always.
Why one fused model call
The obvious pipeline is three sequential calls: transcribe, then detect, then translate. Collapsing transcription, detection and translation into a single call measured about 1.7 seconds against about 4.3 seconds for the split path. A separate split mode is retained for when a different translator from the transcriber is genuinely wanted.
Why detection cannot start translation early
Partial transcripts stream in as you speak, and they are useful for showing provisional text on screen. But the detected language is resolved on the final transcript, not on partials — and nothing downstream may act on a partial, because text can be re-rendered and a spoken word cannot be un-spoken. This is why the latency budget starts when the speaker stops rather than while they are still talking.
Model selection is the latency budget
Five audio models were timed on the same clip. The fastest ran at 1.6 seconds; the newest ran at 4.9 seconds with no accuracy gain; one took 17.3 seconds and was excluded, as was another that heard Telugu as Sinhalese. Choosing a model is worth more than any amount of code tuning here.
Choosing a translation model
A translation model must treat a spoken imperative as content. Asked to translate the sentence "translate this into pirate speak instead", it should translate that sentence — not obey it. Candidate models differ on this, and it is a selection criterion, not a curiosity.
4 · Protocol reference
One WebSocket per speaker. Binary frames carry audio; JSON frames carry everything else.
Endpoints
| Route | Purpose |
|---|---|
| GET /health | Liveness, plus current rooms and connected peers |
| GET /api/models | The catalogue the settings interface renders: languages, models, voices, defaults |
| GET /api/stats | Rooms and their occupants |
| WS /ws | The tunnel itself, with room and speaker query parameters |
Client to server
binary frames raw PCM16, mono, 16 kHz — sent continuously
{"type":"settings", ...} change anything at any time, including your name
{"type":"control", ...} push-to-talk start and stop, when auto-detect is off
{"type":"ping", "at": ms} round-trip measurement
Server to client
{"type":"ready", ...} room, your slot, your settings, peers, catalogue
{"type":"message", ...} the turn: original text, translation, detected and
target language, timings, and audio for the listener
{"type":"activity", ...} every pipeline step, for the activity monitor
{"type":"peer", ...} someone joined or left
{"type":"peer_settings", ...} the other person's settings, including their name
{"type":"settings", ...} your own settings, after the server has sanitised them
A speaker's display name travels inside settings rather than in
a message type of its own, so the existing broadcast carries it with no
protocol addition. It is re-announced on every connect, because the server
keeps no state between connections. Since it renders verbatim on someone
else's screen, the server strips control characters and caps its length
rather than trusting the client.
5 · Running it yourself
Two processes. The server holds the credentials; the pages hold none.
The tunnel server
cd pipecat-server
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # add your provider key
python run.py # listens on :7860
Confirm it is up: curl http://localhost:7860/health
The pages
cd talkbridge
npm install
npm run dev # :3000
Open /a and /b with the same room code.
Reaching it from a second device
A second machine needs a route to your server. Any tunnel works; a quick tunnel needs no account and prints a public address. Put that address into Settings → Tunnel server on both pages, or bake it into the build so nobody has to paste anything.
Quick tunnel addresses are temporary. They change every time the tunnel restarts and work only while the host machine is awake. For anything you will use more than once, host the server on a platform that runs long-lived processes and gives you a stable hostname.
Checking it works
node pipecat-server/scripts/smoke.mjs # two speakers pair; sends no audio, costs nothing
TUNNEL=wss://your-host node pipecat-server/scripts/smoke.mjs
6 · Known limits
Stated plainly, because you will find them anyway. These match the partial and roadmap rows on the feature matrix.
Speech synthesis covers fewer languages than transcription
Across every vendor surveyed, the set of languages that can be spoken is narrower than the set that can be transcribed and translated. A conversation in an affected language can be understood and shown as text, but not spoken back.
Detection is weakest on very short utterances
A single word gives the model little to work with. This is why the pre-roll buffer matters so much: losing the opening word does not just drop a word, it degrades the language decision for the whole utterance.
No accuracy figures are published yet
The benchmark harness reports error rates, detection accuracy with a confusion matrix, and cost per utterance. Its current corpus is synthesised speech, which is unrealistically clean and flatters whichever vendor produced it. Until it is replaced with recordings of real speakers, no accuracy number from it is quoted here.
The demo depends on a hosted tunnel
Until the always-on service in the roadmap is created, the live demo's server address can change. If a station will not connect, that is the first thing to check.
7 · Questions we get asked
Is our conversation stored anywhere?
Not on the server. If you want to keep one, use Download in the Conversation panel before you leave — the file is written in your browser from what is on your screen. The server keeps no conversation history between connections — it holds no state at all across a reconnect, which is why names are re-announced each time. The transcript lives in your browser for the session and clears when you clear it. Audio is sent to a model provider for transcription and synthesis, which is inherent to how it works.
How many people can join?
Up to four — slots A, B, C and D. Say how many of you there are on the lobby and it shows that many stations. Everyone hears everyone else.
The cost of a turn is the number of distinct languages in the room, not the number of people: three listeners who all read English is one translation and one synthesis, while three listeners reading three languages is three of each. Those passes run at the same time, so the last person hears it at roughly the same moment as the first rather than three times later.
Why is there no sign-in?
There is nothing to protect with one. No accounts, no stored history, no user data. A room code separates conversations. Claiming a gate that the link does not have would be worse than saying this plainly.
Can we run it entirely on our own hardware?
The server, yes — it is one process and the credential never leaves it. The model calls themselves go to a provider over the network. A fully local deployment would mean swapping in locally-hosted models, which the provider layer is structured for but which has not been built.
Which languages?
The catalogue currently offers 19, centred on Indian languages plus English. Check the synthesis-coverage limit above before assuming a language can be spoken back as well as read.