← Documents
Reference · Live Translation

Live Translation Subtitles

Real-time speech-to-text and translation system that lets guests follow conversations and events in their own language on their phone. A venue microphone captures speech, a server transcribes and translates, and each guest's device displays rolling subtitles in their chosen language.

Prepared 2026-05-05 · Sponic Gardens · Ported from the Alpaca Playhouse spec, adapted for Sponic infrastructure
Contents
  1. Use cases
  2. Architecture
  3. Components
  4. Guest UI — subtitle view
  5. Mobile app (Android)
  6. Network & connectivity
  7. Cost analysis
  8. Implementation phases
  9. Dependencies & API keys
  10. Risk & mitigation

Use cases

Target languages: English, Polish, Spanish, French, German, Portuguese, Italian, Hindi, Arabic

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Venue Mic       β”‚  USB mic, Jabra Speak, or conference mic
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚ audio stream (WebSocket or chunked PCM)
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   ALPUCA β€” primary (M4 Mac Mini, Poland venue)   β”‚
β”‚   M4 Pro 10-core Β· Neural Engine Β· on-prem       β”‚
β”‚                                                   β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
β”‚  β”‚  STT      │──▢│  Translation Fan-Out β”‚         β”‚
β”‚  β”‚ Deepgram  β”‚   β”‚  DeepL / Helsinki-NLPβ”‚         β”‚
β”‚  β”‚ EU / localβ”‚   β”‚  (per requested lang) β”‚         β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
β”‚                             β”‚                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚  β”‚  WebSocket Server                     β”‚        β”‚
β”‚  β”‚  /ws/subtitles?lang=pl                β”‚        β”‚
β”‚  β”‚  /ws/subtitles?lang=en (transcript)   β”‚        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚ WebSocket push (~1 Kbps per client β€” text only)
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Guest Phones / Tablets  β”‚
β”‚  (Web app or native)     β”‚
β”‚                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚ Language picker     β”‚  β”‚
β”‚  β”‚ Rolling subtitles   β”‚  β”‚
β”‚  β”‚ Font size control   β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Fallback: Oracle Phoenix (Arizona) when ALPUCA is unavailable
Venue location: Poland β€” ALPUCA is the primary server. Realistic scale: up to 8 mics, max 80 subtitle clients. Total bandwidth: ~0.6 Mbps worst-case (8 Opus audio streams in + 80 subtitle clients out). Any venue internet connection handles this trivially. ALPUCA in Poland has ~300 ms less round-trip latency than Oracle Phoenix (Arizona). Oracle is the fallback when ALPUCA is unavailable.

Scenario 1 β€” LAN (venue WiFi only, no internet required)

Guests connect to ALPUCA directly on the venue's local network. Oracle is not in the data path.

AttributeALPUCA (Poland, on-prem)Oracle Phoenix (Arizona)
FeasibilityYes β€” on-premNot reachable on LAN
Guest network req.Venue WiFi onlyRequires internet (unavailable)
WebSocket latency<5 msN/A
STT β€” local Whisper~1–2 s / chunk (M4 Neural Engine)N/A
STT β€” Deepgram EU~30–50 ms (if internet is also available)N/A
Translation β€” DeepL~15–25 ms to Germany (if internet available)N/A
Translation β€” local Helsinki-NLPFree, instant (Neural Engine)N/A
End-to-end (cloud STT)~1.8 sN/A
End-to-end (local Whisper)~2–3 sN/A
Cloud API cost / hr$0.28 (cloud mode) or $0 (fully local)N/A
Server cost~$5–8/mo electricityN/A
PrivacyAudio never leaves the building (local mode)N/A
VerdictALPUCA wins by default β€” Oracle is unreachable without internet.

Scenario 2 β€” Poland, internet-connected guests

Guests have internet access and connect over the public web. ALPUCA sits at the venue in Poland. Oracle Phoenix is in Arizona (~150 ms RTT from Poland).

The transatlantic problem: Oracle's datacenter bandwidth advantage assumed it was geographically close. From Poland, every packet β€” audio chunks in, subtitle pushes out β€” makes a ~300 ms round trip through the US. ALPUCA in Poland eliminates that entirely.
AttributeALPUCA (Poland)Oracle Phoenix (Arizona)Winner
Server location Poland (co-located with venue) Phoenix, AZ β€” ~9,000 km from Warsaw ALPUCA
RTT: venue mic β†’ server ~5–30 ms (same building or Polish ISP) ~150 ms (transatlantic) ALPUCA
RTT: server β†’ Deepgram EU ~30–50 ms (Poland β†’ Frankfurt) ~120–160 ms (Arizona β†’ Frankfurt) ALPUCA
RTT: server β†’ DeepL (Germany) ~15–25 ms ~110–140 ms ALPUCA
RTT: server β†’ guest phones (Poland) ~10–30 ms (Polish ISP) ~150 ms (back across Atlantic) ALPUCA
Total transatlantic overhead 0 ms ~300 ms (150 ms Γ— 2) ALPUCA
End-to-end latency (cloud STT + translation) ~1.8–2.2 s ~2.1–2.5 s ALPUCA
End-to-end latency (local Whisper) ~2–3 s (Neural Engine) ~5–6 s (CPU-only) + 300 ms overhead ALPUCA
Cloud API cost / hr (Deepgram + DeepL) $0.13 + $0.15 = $0.28 $0.13 + $0.15 = $0.28 β€” same, API pricing is per usage not location Tie
Server cost ~$5–8/mo electricity (~7 W idle, ~30 W load) $0 (Always Free) Oracle (marginal)
Subtitle bandwidth per client ~300-byte JSON message every 2–3 s = ~1 Kbps per guest β€” text, not video Tie
Realistic scale (next 3 months) Max 8 mics (not all simultaneous) Β· max 80 subtitle clients Irrelevant to server choice
Total bandwidth at full scale ~0.6 Mbps worst-case (8 Γ— 64 Kbps Opus in + 80 Γ— 1 Kbps subtitles out) Same math Not a factor β€” Starlink Mini provides 10–15 Mbps up (17–25Γ— headroom)
Venue internet Starlink Mini Roaming β€” portable, brought to every event; ~20–40 ms latency to EU ground stations; no dependency on venue WiFi Requires venue/venue internet or Tailscale tunnel ALPUCA
Uptime / reliability Venue power + Polish ISP (single point of failure) Datacenter UPS, auto-restart, ~99.9% Oracle
Hardware / ML compute M4 Pro, Neural Engine β€” fast local inference 4Γ— Ampere ARM, CPU-only, no GPU ALPUCA
Privacy / GDPR Audio stays in EU (Deepgram EU endpoint, DeepL Germany) Audio routed through US infrastructure ALPUCA
Deployment launchd (macOS) systemd (existing flow, prompt-runner already running) Oracle (slightly easier)
Verdict ALPUCA is the primary for all Poland events. Oracle is the fallback when ALPUCA is unavailable β€” its only genuine advantages (reliability, $0 cost) don't outweigh the 300 ms latency penalty and GDPR routing.

Decision guide

SituationUseSTT modeReason
LAN-only event (no internet) ALPUCA Local Whisper + Helsinki-NLP Only option; Neural Engine fast; audio never leaves building; $0 API cost
Poland venue, internet via Starlink Mini β€” any guest count up to 80 ALPUCA Deepgram EU + DeepL 300 ms faster; Starlink provides 10–15 Mbps up (17–25Γ— what's needed); guests on same Starlink WiFi hit ALPUCA at LAN speed
ALPUCA down or Starlink unavailable Oracle Phoenix Deepgram + DeepL Always-on fallback; +300 ms latency is better than no service

Components

1. Mic input capture

Up to 8 mics, not all speaking simultaneously. Each active mic is one independent audio stream to Deepgram (~64 Kbps Opus each). Total inbound at 8 simultaneous: ~512 Kbps β€” well within Starlink Mini's upload.

OptionPriceNotes
Jabra Speak 510~$100Conference speakerphone, USB, good pickup radius β€” good for table discussions
Blue Yeti~$100Studio quality, USB, better for single-speaker or presenter
Rode Wireless GO II~$250Clip-on wireless, best for mobile speaker; up to 2 per receiver
Existing USB mic$0Test with whatever's available first

Multi-mic setup: Deepgram supports multichannel audio β€” each mic gets its own channel, enabling per-speaker transcription and optional speaker labels. Run one Deepgram WebSocket connection per active mic, or use a multichannel USB audio interface to send all channels in one stream.

Software: PyAudio or arecord capturing 16kHz mono PCM per channel, streamed to ALPUCA via local pipe or WebSocket.

2. Speech-to-text (STT) engine

Option A — Self-hosted on ALPUCA (free, ~3s latency):

Option B — Cloud STT (cheap, ~1.5s latency):

Recommendation: Start with Deepgram for quality/latency, fall back to local Whisper if cost matters at scale.

3. Translation engine

Option A — Cloud translation (best quality):

ServicePolish supportCost per 1M charsNotes
DeepL APIExcellent$5.49 (Pro)Best Polish quality
Google TranslateGood$20Widest language coverage
Azure TranslatorGood$10Free 2M chars/month (covered by Founders Hub credit)

Option B — Self-hosted on ALPUCA (free):

Translation caching: Cache translated segments by source hash + target language. If 3 guests want Polish, translate once, broadcast to all. Real-time speech is unique, so the cache mainly helps with repeated phrases and greetings.

Recommendation: DeepL for Polish (their standout language), Azure free tier for others. Azure Translator falls under the Founders Hub $1k credit.

4. WebSocket server

Tech: Node.js with ws library (matches existing edge function patterns) or Python FastAPI with websockets.

Endpoints:

# Primary (Oracle Phoenix β€” datacenter)
wss://subtitles.sponicgardens.com/subtitles?lang=en    # Original transcript
wss://subtitles.sponicgardens.com/subtitles?lang=pl    # Polish translation
wss://subtitles.sponicgardens.com/subtitles?lang=es    # Spanish translation

# Fallback (ALPUCA β€” LAN only)
ws://alpuca.local:8910/subtitles?lang=en
ws://alpuca.local:8910/subtitles?lang=pl

Message format:

{
  "id": "seg_001",
  "text": "Witamy w Sponic Gardens",
  "lang": "pl",
  "source_lang": "en",
  "source_text": "Welcome to Sponic Gardens",
  "timestamp": 1711800000,
  "is_partial": false
}

Connection management:

Guest UI — subtitle view

New screen accessible via the Sponic Gardens web app (progressive web app) and native apps (iOS + Android).

UI layout

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Live Subtitles    [EN β–Ό] A  β”‚  ← language picker + font size
│──────────────────────────────│
β”‚                              β”‚
β”‚  Welcome to Sponic Gardens   β”‚
β”‚                              β”‚
β”‚  The WiFi password is on the β”‚
β”‚  card in your room           β”‚
β”‚                              β”‚
β”‚  β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘            β”‚  ← partial/incoming text (dimmed)
β”‚                              β”‚
β”‚                              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Features

Native app integration

PlatformFilesWebSocket lib
iOS (SwiftUI) Views/SubtitleView.swift, Services/SubtitleService.swift, Models/SubtitleSegment.swift URLSessionWebSocketTask (native, no deps)
Android (Compose) ui/subtitles/SubtitleScreen.kt, services/SubtitleService.kt, models/SubtitleSegment.kt OkHttp WebSocket (already in dependency tree)

Reconnect with exponential backoff (1s, 2s, 4s, max 30s). Entry point: new tab/button on the app home screen — only visible when the subtitle server is broadcasting (check via GET /subtitles/status).

Mobile app (Android)

Native Kotlin + Jetpack Compose app (com.sponicgardens.sponic). Connects to the ALPUCA subtitle backend via OkHttp WebSocket. Built on ALPUCA, published to Cloudflare R2, registered in Supabase mobile_builds.

Current version

AttributeValue
Version0.4.4 (build 8)
Packagecom.sponicgardens.sponic
Min SDK26 (Android 8.0)
Target SDK35
ABIarm64-v8a only
Download (latest)sponic-debug.apk
Build hostALPUCA (M4 Mac Mini, Poland)

Features

Audio recording architecture

The app uses a dual-path approach to capture audio while maintaining SpeechRecognizer access:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  AudioRecord (VOICE_RECOGNITION) β”‚  ← coexists with SpeechRecognizer
β”‚  PCM 16-bit → gain applied       β”‚
β”‚       β”‚                          β”‚
β”‚       β”œβ”€> MediaCodec (AAC-LC)    β”‚  → .m4a file on device
β”‚       β”‚   └─> MediaMuxer (MP4)   β”‚
β”‚       β”‚                          β”‚
β”‚       └─> RMS level (VU meter)   β”‚  → animated bar in UI
β”‚                                  β”‚
β”‚  SpeechRecognizer (parallel)     β”‚  → POST /subtitles/inject
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key files

FilePurpose
MainActivity.ktSingle-activity entry, screen nav, SharedPreferences, AudioDeviceManager lifecycle
ui/screens/LoginScreen.ktGoogle Sign-In (Credential Manager API) + manual name entry fallback
ui/screens/HomeScreen.ktMode selection (View Subtitles, Speak+View), server status, profile menu
ui/screens/SubtitleScreen.ktReal-time subtitle display + mic controls with VU meter and recording timer
ui/screens/SettingsScreen.ktLanguage, mic device, gain, sample rate, noise suppression, recording & sync toggles
ui/screens/RecordingsScreen.ktBrowse/play/share/delete local M4A recordings
audio/AudioRecorder.ktDual-path audio engine: AudioRecord → MediaCodec/MediaMuxer + RMS metering
audio/AudioDeviceManager.ktDevice enumeration, hot-swap callbacks, auto-selection priority, deduplication
network/SubtitleClient.ktOkHttp WebSocket + HTTP client for subtitle backend
model/Models.ktData classes: Language, AppMode, AudioSettings, AudioDevice, Recording, ServerStatus

Build & publish

# One-step build + publish from M4:
cd apps/mobile
./publish.sh "changelog description"

# Publishes to:
#   R2:  mobile/sponic-{version}-debug.apk  (versioned)
#   R2:  mobile/sponic-debug.apk            (latest)
#   Supabase: mobile_builds row with metadata
publish.sh handles everything: SSHs into ALPUCA for the Gradle build, copies the APK back, uploads versioned + latest to R2 (sponic-images bucket), and inserts a build record into Supabase. Credentials are fetched from Bitwarden at runtime.

Supported languages

LanguageCode
Englishen
Polishpl
Spanishes
Frenchfr
Germande
Portuguesept
Italianit
Hindihi
Arabicar

Language defaults from the device's system locale at first launch. Users can change it anytime in Settings, and the preference persists across sessions.

Planned (not yet implemented)

Network & connectivity

Primary: ALPUCA (Poland venue, Starlink Mini)

Venue internet: Starlink Mini Roaming β€” portable, brought to every event.

Starlink Mini specValueOur need
Upload~10–15 Mbps typical~0.6 Mbps worst-case (8 mics + 80 clients) β€” 17–25Γ— headroom
Download~50–100 MbpsNegligible (server receives only mic audio)
Latency (EU ground stations)~20–40 msAdds ~20–40 ms to Deepgram EU and DeepL calls
PortabilityRoaming across EU/PolandWorks at any venue β€” no dependency on venue WiFi

Fallback: Oracle Phoenix (Arizona)

SSE fallback: If WebSocket is blocked by a guest's network or mobile carrier, fall back to SSE (Server-Sent Events) over HTTPS. Implement on both ALPUCA and Oracle servers.

Cost analysis

Per-hour cost (50% speaking time = 30 min audio, ~27K chars)

SetupSTTTranslationTotal / hr
All cloudDeepgram: $0.13DeepL: $0.15$0.28
Hybrid (cloud STT, local translate)$0.13$0$0.13
All local (ALPUCA)$0$0$0.00

Monthly estimates

UsageAll cloudHybridAll local
2 events/week, 2 hrs each$4.48$2.08$0
Daily use, 4 hrs/day$33.60$15.60$0
Always-on ambient (12 hrs/day)$100.80$46.80$0

One-time costs

Implementation phases

Phase 1: Server MVP (1–2 days)

  • Python/Node server: mic capture → Deepgram streaming STT → WebSocket broadcast (English transcript only)
  • Health endpoint: GET /subtitles/status
  • Browser test client (simple HTML + WebSocket listener)
  • Deploy on ALPUCA as launchd service (primary); deploy on Oracle Phoenix as systemd service (fallback)

Phase 2: Translation (1 day)

  • DeepL integration for Polish + Spanish
  • Helsinki-NLP local models as fallback
  • Language-based fan-out: only translate to languages with active listeners
  • Cache layer for repeated phrases

Phase 3: App integration (2–3 days)

  • iOS SubtitleView + SubtitleService
  • Android SubtitleScreen + SubtitleService
  • Language picker with phone locale default
  • Font size control + auto-scroll
  • Connection status indicator
  • Conditional visibility (only show when server is broadcasting)

Phase 4: Polish & production (1–2 days)

  • Partial result rendering (interim gray text)
  • Reconnect logic with exponential backoff
  • Dark mode optimized for event lighting
  • Cloudflare Workers proxy (wss://subtitles.sponicgardens.com) + Tailscale for admin access
  • Logging: session duration, languages requested, character counts → Supabase api_usage_log

Phase 5: Enhancements (future)

  • Speaker diarization (“Speaker 1”, “Speaker 2”)
  • TTS output: read translations aloud via ElevenLabs/Voxtral (accessibility)
  • Kiosk/TV display mode (large text, auto-scroll, no controls)
  • Saved transcripts: export event transcript to PDF/email
  • Whisper local fallback: auto-switch if Deepgram is down or budget exceeded

Operations β€” starting ALPUCA server components

Subtitle backend (required)

The subtitle backend runs on ALPUCA at port 8910. It handles WebSocket subtitle broadcasting and speech injection.

# SSH to ALPUCA
ssh [email protected]

# Start in mock mode (no mic, broadcasts test sentences):
cd /Users/alpuca/Documents/codingprojects/alpacapps/live-subtitles
node server.js --mock

# Start in production mode (requires GEMINI_API_KEY for translation):
GEMINI_API_KEY="your-key" node server.js

# Start with Polish source language mock:
node server.js --mock-pl

Verify the backend is running

# From any machine on the network:
curl http://Alpuca.local:8910/subtitles/status

# Expected response:
# {"active":true,"mock":true,"listeners":0,"languages":[],"supported_languages":["en","pl","es","fr","de","pt","it","hi","ar"]}

Endpoints

EndpointMethodPurpose
ws://Alpuca.local:8910/subtitles?lang=XXWebSocketSubscribe to subtitles in language XX
/subtitles/statusGETHealth check β€” active, mock, listener count
/subtitles/injectPOSTInject text from speech recognition: { text, is_partial, source_lang, speaker }
/subtitles/transcribePOSTSend audio for server-side Gemini STT: { audio: base64, source_lang }
/eventspeakerGETBrowser-based speaker/test client

Running as a persistent service (launchd)

A launchd plist exists at ~/Library/LaunchAgents/com.alpacapps.live-subtitles.plist but requires valid TLS certs at /tmp/subtitle-cert.pem and /tmp/subtitle-key.pem (which don't persist across reboots). For now, start the server manually via SSH. To fix auto-start, either generate persistent self-signed certs outside /tmp or remove the TLS env vars from the plist to run HTTP-only.

Android app (Sponic)

The native Kotlin app connects to the ALPUCA backend. Build on ALPUCA:

ssh [email protected]
cd /Users/alpuca/Documents/codingprojects/sponic/apps/mobile
source ~/.zshrc
./gradlew assembleDebug
# APK: app/build/outputs/apk/debug/app-arm64-v8a-debug.apk

Google Sign-In (OAuth 2.0)

The app uses Android Credential Manager API with Google Sign-In. OAuth credentials are in GCP project Sponica (801803827261), owned by [email protected].

CredentialTypePurpose
Sponic (Web)Web applicationToken audience for setServerClientId() β€” used by Credential Manager API
SponicAndroidAndroidTied to package com.sponicgardens.sponic + debug SHA-1

Stored in Bitwarden: GCP Sponica OAuth Clients (2edd05a6) in DevOps-sponicgarden collection. Console: APIs & Services β†’ Credentials.

The Android client uses the debug keystore SHA-1 (50:0D:86:…:D0:EE). For release builds, generate a new SHA-1 from the release keystore and add it as a separate Android OAuth client in GCP.

Dependencies & API keys

ServiceKey locationFree tier
GCP Sponica (OAuth)Bitwarden: GCP Sponica OAuth Clients (2edd05a6)Free (OAuth is free)
DeepgramBitwarden (DevOps-sponicgarden, to be created)$200 free credit on signup
DeepLBitwarden (DevOps-sponicgarden, to be created)500K chars/month
Azure TranslatorBitwarden (DevOps-sponicgarden, to be created)2M chars/month (Founders Hub credit)
Helsinki-NLP modelsLocal on ALPUCAUnlimited (open source)
Whisper.cppLocal on ALPUCAUnlimited (open source)
All API keys follow the standard Sponic credential flow: store in Bitwarden under the DevOps-sponicgarden collection (ALPU.CA org), reference by BW item ID in config/project.config.ts, and document the unlock recipe in infra/runbook.md.

Risk & mitigation

RiskMitigation
High ambient noise reduces STT accuracyUse directional mic; Deepgram has noise suppression; test mic placement per venue
Multiple simultaneous speakersDeepgram supports multichannel; consider lapel mics for structured events
Polish translation qualityDeepL is best-in-class for Polish; test with native speakers at the venue
WiFi congestion during eventsWebSocket is lightweight (~1 KB/s per client); not a concern
Oracle Phoenix CPU loadCurrently <5% idle with 7 workers; subtitle relay adds negligible load. ALPUCA fallback: Whisper medium uses ~30% of M4, plenty of headroom
Guest adoptionShow QR code on event screens linking to the subtitle page; no app install required for the web version