A simple, privacy-focused video calling application built with WebRTC. No accounts, no tracking, just instant calls with up to 4 participants.
- Instant calls – One tap to start, share a link to connect
- No accounts required – Just open and call
- Privacy-first – No tracking, no analytics, end-to-end encrypted peer-to-peer video
- Resilient signaling – WebSocket with SSE fallback when WS is blocked
- Adaptive multi-party rooms – New-capable clients create group-capable rooms by default, with legacy-first rooms still capped at 2 participants
- Mobile-friendly – Works on Android Chrome, iOS Safari, desktop browsers, and native Android/iOS clients
- Desktop screen sharing (web) – In-call screen share control on desktop browsers that support
getDisplayMedia(not shown on mobile browsers) - Recent calls on home – Web and Android home screens show your latest calls with live room occupancy (Android supports long-press remove)
- Android saved rooms – Name and pin rooms on home, choose whether they appear above or below recent calls, and create links that add named rooms on recipient devices
- Android camera source cycle – In-call source switch cycles through
selfie(default) ->world->composite(world feed with circular selfie overlay), automatically skipscompositewhen unsupported, and shows a flashlight toggle inworld/compositewhen flash hardware is available; flashlight preference is remembered during the call and reapplied when returning to supported modes - Android world/composite pinch zoom – When local video is the large in-call view in
worldorcomposite, pinch gesture zooms the camera capture itself so both local preview and the remote participant see the zoomed detail - Android HD video toggle (experimental) – Settings include an
HD Video (experimental)switch for higher camera/composite quality; default mode keeps legacy640x480camera constraints for stability - iOS native client (SwiftUI) – Native iOS app in
client-ios/mirrors Android parity flow: saved rooms + recents ordering, structured deep-link parsing, invite push toggle, encrypted push snapshots, waiting-room invite action, adaptive multi-party layout with local PIP, diagnostics screen, mode-based camera cycle with composite fallback, world/composite pinch zoom, ReplayKit screen share toggle, and in-call realtime stats/debug panel - Self-hostable – Run your own instance with full control
- Optional join alerts – Encrypted push notifications with snapshot previews (web + native Android + native iOS)
- Room invite push – In waiting state you can explicitly invite subscribers of the room; Android and iOS show these only for saved rooms and have a Settings toggle to disable invite notifications
- In-app feedback – Send bug reports and suggestions directly from the app (web footer, Android/iOS settings); messages are forwarded to a configurable Telegram chat
-
Copy the environment template:
cp .env.example .env
-
Build the frontend:
cd client npm install npm run build -
Start the development stack:
docker compose up -d --build
-
Open http://localhost in your browser
If you prefer to run the components manually:
cd client
npm install
npm run devcd server
go run .Requires Go 1.24+ and a .env file in the root directory.
The native Android app lives in client-android/.
- Open
client-android/in Android Studio. - Sync Gradle.
- Run on a device or emulator (minSdk 26).
- Default WebRTC provider is
local7559(client-android/serenada-core/libs/libwebrtc-7559_173-arm64.aar). - Rebuild the patched libwebrtc AAR on Linux with
bash tools/build_libwebrtc_android_7559.sh. - When updating that AAR, regenerate
client-android/serenada-core/libs/libwebrtc-7559_173-arm64.aar.sha256(Gradle now verifies it before build).
By default the app targets https://serenada.app, and the server host can be changed in Settings.
The Android app language can also be set in Settings: Auto (default), English, Русский, Español, Français. Auto follows the device language and falls back to English.
To enable native Android push receive, provide Firebase Gradle properties when building the app (firebaseAppId, firebaseApiKey, firebaseProjectId, firebaseSenderId).
The native iOS app lives in client-ios/.
- Install
xcodegen(if not already installed). - Generate project files:
cd client-ios xcodegen generate - Open
SerenadaiOS.xcodeprojin Xcode. - Run
SerenadaiOSon iOS 16+ simulator/device. - Build and vendor pinned WebRTC XCFramework:
This script also patches
bash tools/build_libwebrtc_ios_7559.sh
rtc_base/ssl_roots.hfrom the current root bundle (same approach as Android) and strips dSYMs by default to keep repository artifact size manageable. - If you replace
client-ios/Vendor/WebRTC/WebRTC.xcframeworkmanually, regenerate checksum:cd client-ios ./scripts/update_webrtc_checksum.sh - For local-only device signing overrides (without committing team IDs), use
client-ios/LocalSigning.xcconfig. Seeclient-ios/README.md.
iOS universal links are enabled for serenada.app and serenada-app.ru via associated domains plus /.well-known/apple-app-site-association.
Note: iOS Simulator can run signaling and call flow, but local camera preview reliability varies by host setup; use a physical iPhone to validate local camera capture.
See DEPLOY.md for detailed self-hosting instructions.
Quick setup script (downloads, installs dependencies, and provisions the stack):
curl -fsSL https://serenada.app/tools/setup.sh -o setup-serenada.sh
bash setup-serenada.shThe server includes an in-repo load conduit for signaling capacity validation.
Quick run (local Docker stack):
./server/loadtest/run-local.shWhat it does:
- Starts local services with
ENABLE_INTERNAL_STATS=1 - Sets a local
INTERNAL_STATS_TOKENautomatically (override via env if needed) - Validates
/api/room-idand/api/internal/stats - Runs
go run ./cmd/loadconduitfromserver/ - Writes a JSON report to
server/loadtest/reports/
Common overrides:
START_CLIENTS=20 STEP_CLIENTS=20 MAX_CLIENTS=200 STEADY_SECONDS=600 ./server/loadtest/run-local.shStabilization and join-tolerance controls:
PRE_RAMP_STABILIZE_SECONDS=10 MAX_JOIN_ERROR_RATE=0.005 ./server/loadtest/run-local.shTo avoid local/NAT throttling while testing, set a bypass allowlist:
RATE_LIMIT_BYPASS_IPS=127.0.0.1,::1 ./server/loadtest/run-local.shDirect conduit usage:
cd server
go run ./cmd/loadconduit --base-url http://localhost --report-json ./loadtest/reports/manual.jsonDetailed request/timing sequence:
bash tools/integration-test/run.sh # Run signaling integration tests (requires Go 1.24+)Verify SDK versions match across all platforms:
node scripts/check-version-parity.mjs┌─────────────────┐ ┌─────────────────┐
│ Browser A │◄───────►│ Browser B │
│ (React SPA) │ WebRTC │ (React SPA) │
└────────┬────────┘ └────────┬────────┘
│ │
│ WS/SSE (signaling) │
▼ ▼
┌─────────────────────────────────────────────┐
│ Go Signaling Server │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ STUN/TURN Server (Coturn) │
└─────────────────────────────────────────────┘
- Frontend: React + TypeScript + Vite
- Backend: Go (signaling server)
- Media: WebRTC with STUN/TURN support via Coturn
- Deployment: Docker Compose with Nginx reverse proxy
All three native SDKs follow a headless core + optional UI pattern:
SerenadaCore— Entry point.createRoom()is async (async throwson iOS,suspendon Android).join()returns aSerenadaSession.SerenadaSession— The active call session. Exposes two observable snapshots:state(CallState) — App-facing call state: phase, local/remote participants, connection status, errors.diagnostics(CallDiagnostics) — Low-level transport info: ICE/peer/signaling states, realtime stats, camera/flash state, feature degradations.
SerenadaCallFlow— Pre-built UI component (SwiftUI / Jetpack Compose / React) that consumes a session and renders the full call flow. Optional — you can build your own UI fromstateanddiagnostics.
Android enforces main-thread access on all public SDK entrypoints with fail-fast preconditions.
- SDK API Reference – Generated API docs for all platforms (Web, Android, iOS)
- Deployment Guide – Self-hosting instructions
- Protocol Specification – Signaling protocol (WebSocket + SSE)
- Push Notifications – Encrypted snapshot notifications
- Android Client README – Kotlin native app setup and build notes
- iOS Client README – SwiftUI native app setup and build notes
server/loadtest/run-local.sh– Local signaling load sweep runnerserver/loadtest/LOAD_SIMULATION_SEQUENCE.md– Detailed load-conduit HTTP/WS call sequence and timing
| Component | Technology |
|---|---|
| Frontend | React 19, TypeScript, Vite |
| Backend | Go 1.24+ |
| Media | WebRTC, Coturn |
| Proxy | Nginx |
| Containers | Docker, Docker Compose |
This project is licensed under the BSD 3-Clause License. See LICENSE for details.