Real-time speech translation for live events. The speaker talks. Every audience member reads it instantly, in their own language.
A speaker starts a session and shares a short code. Anyone can join from their phone or laptop, pick from 33 languages, and watch the translation stream in live, word-by-word β no app install, no account.
sequenceDiagram
participant S as π€ Speaker
participant B as βοΈ Backend
participant LLM as π§ LLM
participant A as π± Audience (ΓN)
S->>B: speech β text (WebSocket)
B->>LLM: buffered sentence
LLM-->>B: streamed translation tokens
B-->>A: fan-out, per audience language
A-->>B: reactions, questions
B-->>S: live audience roster + Q&A
- Speech-to-text runs in the speaker's browser (Web Speech API) by default β no audio leaves the device. An optional Gemini Live server-side mode can be enabled for higher accuracy on noisy mics or unsupported browsers.
- Translation streams token-by-token through any OpenAI-compatible LLM, with automatic failover to a second provider on rate limits.
- Audience members can react with emoji and ask questions back, auto-translated into the speaker's language.
| π£οΈ Live transcription | Real-time speaker captions, no lag between speaking and text |
| π 33 languages | 22 scheduled Indian languages + 11 international |
| β‘ Streaming translation | Tokens appear as the LLM generates them, not after the full sentence |
| π Provider failover | Automatic switch to a fallback LLM on 429s |
| π Live Q&A | Audience questions auto-translate into the speaker's language |
| π¬ Reactions | Lightweight emoji feedback from the crowd |
| π Optional TTS | Audience can have translations read aloud |
| π Light & dark themes | Tuned contrast, not a default Tailwind flip |
| π± Mobile-first | Tested down to 280px viewports, notch/safe-area aware |
| π« No accounts | Join with a session code, nothing to sign up for |
cd backend
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
# Edit .env β set LLM_BASE_URL and LLM_API_KEY at minimum
uvicorn main:app --reload --port 8000cd frontend
npm install
npm run dev
# Opens at http://localhost:5173Vite proxies /api and /ws to localhost:8000 automatically β no frontend env vars needed for local dev.
cp backend/.env.example backend/.env
# Edit backend/.env with your API keys
docker compose up --build
# Frontend: http://localhost:3000 | Backend: http://localhost:8000Option A β Blueprint (recommended)
- Push this repo to GitHub.
- Go to render.com β New β Blueprint.
- Connect your repo. Render reads
render.yamland creates the service automatically. - Set the required environment variables in the Render dashboard (see table below).
Option B β Manual
- New Web Service β connect repo
- Root Directory:
backend - Runtime: Python 3
- Build Command:
pip install -r requirements.txt - Start Command:
uvicorn main:app --host 0.0.0.0 --port $PORT - Add environment variables (see table below)
Note the service URL β you'll need it for the frontend step. It looks like https://vociblink-backend.onrender.com.
- vercel.com β New Project β import your repo.
- Root Directory:
frontend(framework auto-detects as Vite). - Add environment variables:
| Key | Value |
|---|---|
VITE_API_URL |
vociblink-backend.onrender.com |
VITE_WS_URL |
vociblink-backend.onrender.com |
- Deploy.
Back on the Render dashboard, set:
| Key | Value |
|---|---|
CORS_ORIGINS |
["https://your-project.vercel.app"] |
Redeploy. Done.
| Variable | Required | Description |
|---|---|---|
LLM_BASE_URL |
Yes | Base URL of any OpenAI-compatible API |
LLM_API_KEY |
Yes | API key for the primary provider |
LLM_MODEL |
Yes | Model name (default: mistral-small-latest) |
LLM_FALLBACK_BASE_URL |
No | Fallback provider URL β auto-used when primary returns 429 |
LLM_FALLBACK_API_KEY |
No | Fallback provider API key |
LLM_FALLBACK_MODEL |
No | Fallback model name |
GEMINI_API_KEY |
No | Enables server-side Gemini Live STT; omit to use browser STT only |
GEMINI_MODEL |
No | Gemini Live model (default: gemini-3.1-flash-live-preview) |
CORS_ORIGINS |
Prod | JSON array of allowed origins, e.g. ["https://app.vercel.app"] |
HOST |
No | Bind host (default: 0.0.0.0) |
PORT |
No | Port (default: 8000; Render injects this automatically) |
| Variable | When needed | Description |
|---|---|---|
VITE_API_URL |
Production | Backend HTTP base URL |
VITE_WS_URL |
Production | Backend WebSocket base URL |
Any OpenAI-compatible API works. The backend supports a primary + fallback and switches automatically on rate limits.
| Provider | Free Tier | LLM_BASE_URL |
Suggested LLM_MODEL |
|---|---|---|---|
| Mistral | Yes | https://api.mistral.ai/v1/ |
mistral-small-latest |
| NVIDIA NIM | Yes | https://integrate.api.nvidia.com/v1/ |
meta/llama-3.1-8b-instruct |
| Groq | Yes | https://api.groq.com/openai/v1/ |
llama-3.1-8b-instant |
| OpenAI | No | https://api.openai.com/v1/ |
gpt-4o-mini |
Recommended setup: Mistral as primary (generous free tier) + NVIDIA NIM or Groq as fallback.
- The free tier spins down after 15 minutes of inactivity. First request after sleep takes ~30β60 seconds.
- An active WebSocket connection keeps the service alive.
- Set up an uptime monitor (e.g. UptimeRobot pinging
/api/health) to prevent cold starts during events. - The backend runs a translation warmup request on startup to reduce first-translation latency.
| Layer | Technology |
|---|---|
| Frontend | React 18, TypeScript, Tailwind CSS, Vite |
| Backend | FastAPI, Python 3.12, asyncio |
| Transport | WebSockets (native) |
| Speech-to-Text | Browser Web Speech API, optional Gemini Live |
| Translation | Any OpenAI-compatible LLM |
| TTS (optional) | Browser SpeechSynthesis API |
vociblink/
βββ render.yaml # Render Blueprint
βββ docker-compose.yml
βββ backend/
β βββ main.py # FastAPI app, WebSocket handlers, translation pipeline
β βββ core/config.py # Env-var settings (pydantic-settings)
β βββ models/schemas.py # Pydantic schemas + LANGUAGE_NAMES map
β βββ services/
β β βββ session_manager.py # In-memory session state (speakers + audience)
β β βββ translation_service.py # Streaming LLM translation + provider failover + cache
β β βββ broadcast_manager.py # WebSocket fan-out helpers
β β βββ content_filter.py # Profanity filter (word-boundary aware)
β β βββ gemini_stt.py # Optional server-side speech-to-text (Gemini Live)
β βββ .env.example
β βββ requirements.txt
β βββ Dockerfile
βββ frontend/
βββ public/
β βββ pcmWorkletProcessor.js # AudioWorklet: resamples mic input to 16kHz
βββ src/
β βββ pages/ # Home.tsx, Speaker.tsx, Audience.tsx
β βββ components/ # TranscriptBlock, LanguageSelector, MicrophoneButton, β¦
β βββ lib/
β β βββ audio.ts # TTSPlayer (browser SpeechSynthesis)
β β βββ audioCapture.ts # Mic capture β AudioWorklet β PCM stream
β β βββ websocket.ts # Reconnecting WebSocket client + URL helpers
β β βββ speech.ts # Cross-browser SpeechRecognition wrapper
β β βββ theme.tsx # ThemeProvider + useTheme (light/dark)
β βββ styles/globals.css # CSS custom properties (semantic color tokens)
β βββ types/index.ts
βββ vercel.json # SPA rewrite rule for Vercel
βββ tailwind.config.js
βββ vite.config.ts
βββ Dockerfile
33 languages β 22 scheduled Indian languages + 11 international (click to expand)
Indian: Hindi Β· Bengali Β· Telugu Β· Marathi Β· Tamil Β· Urdu Β· Gujarati Β· Kannada Β· Odia Β· Malayalam Β· Punjabi Β· Assamese Β· Maithili Β· Sanskrit Β· Konkani Β· Nepali Β· Sindhi Β· Dogri Β· Manipuri Β· Bodo Β· Santali Β· Kashmiri
International: English Β· Spanish Β· French Β· German Β· Portuguese Β· Italian Β· Russian Β· Japanese Β· Korean Β· Chinese Β· Arabic
Speaker β wss://host/ws/speaker/{session_id}
| Direction | Message | Description |
|---|---|---|
| β Backend | {type:"transcript", text, is_final} |
STT result (interim or final) |
| β Backend | {type:"set_session_title", title} |
Update session title |
| β Backend | {type:"set_question_language", language} |
Language for audience questions |
| β Backend | {type:"ping"} |
Keep-alive |
| β Backend | {type:"end_session"} |
Close session |
| Backend β | {type:"session_info", β¦} |
Session metadata on connect |
| Backend β | {type:"audience_list", members} |
Live audience roster |
| Backend β | {type:"audience_reaction", emoji, sender_name} |
Emoji reaction |
| Backend β | {type:"question", original_text, translated_text, β¦} |
Audience question |
Audience β wss://host/ws/audience/{session_id}?language=hi&name=Alice
| Direction | Message | Description |
|---|---|---|
| Backend β | {type:"translation_start"} |
New translation stream beginning |
| Backend β | {type:"translation_token", token} |
Streamed translation chunk |
| Backend β | {type:"translation_end", is_final} |
Stream complete |
| Backend β | {type:"speaker_status", active} |
Speaker online/offline |
| β Backend | {type:"language_change", language} |
Switch translation language |
| β Backend | {type:"reaction", emoji} |
Send emoji reaction |
| β Backend | {type:"question_text", text} |
Send a question |
All Rights Reserved. This is proprietary code β viewing this repository does not grant permission to use, copy, modify, or distribute it. See LICENSE.