Livekit: Open Source WebRTC Platform
LiveKit has quickly become the go‑to open‑source solution for anyone wanting to embed real‑time audio, video, and data streams into web or mobile apps. Unlike proprietary services that lock you into a vendor’s pricing model, LiveKit gives you full control over the media stack while still offering a polished, production‑ready API. In this article we’ll unpack the platform’s architecture, walk through two end‑to‑end code examples, and explore real‑world scenarios where LiveKit shines.
What LiveKit Actually Is
At its core, LiveKit is a WebRTC‑based media server written in Go, complemented by client SDKs for JavaScript, iOS, Android, and even Unity. The server handles signaling, routing, and optional recording, while the client SDK abstracts the low‑level WebRTC APIs into simple, declarative calls. Because the server is open source, you can self‑host it on any cloud provider, on‑premises, or even run it locally for development.
LiveKit’s design philosophy revolves around three pillars: scalability, extensibility, and developer friendliness. It uses a stateless architecture where each node can join a cluster, enabling horizontal scaling without complex sharding logic. Plugins let you hook into events like participant join, track publish, or room termination, making custom workflows straightforward.
Core Architecture Overview
The platform consists of three main components:
- LiveKit Server – Handles room management, signaling (via WebSocket), and media routing. It also offers optional TURN/STUN services.
- Client SDKs – Provide high‑level primitives such as
Room,Participant, andTrack. The JavaScript SDK is the most widely used, but native SDKs follow the same API surface. - Control Plane (REST API) – Used for server administration, token generation, and room lifecycle management.
All media flows through the server’s SFU (Selective Forwarding Unit). Unlike an MCU, an SFU forwards each participant’s encoded streams to others without re‑encoding, preserving quality and minimizing CPU load. This makes LiveKit ideal for large group calls where bandwidth efficiency matters.
Getting Started in Five Minutes
First, spin up a LiveKit server. The easiest way is using Docker:
docker run -p 7880:7880 -p 7881:7881 \
-e "LIVEKIT_KEYS=devkey:devsecret" \
livekit/livekit-server:latest
This command launches the server on ports 7880 (HTTP) and 7881 (WebSocket). The environment variable LIVEKIT_KEYS defines a static API key pair for development. In production you’d generate keys via the admin UI or CLI.
Next, install the JavaScript SDK in your web app:
npm install livekit-client
Now you’re ready to write a minimal client that joins a room and publishes a webcam stream.
Example 1: Simple One‑to‑One Video Call
Server‑Side Token Generation (Python)
LiveKit uses JWT‑based access tokens to authorize participants. Below is a tiny Flask endpoint that creates a token for a given user ID and room name.
from flask import Flask, request, jsonify
import jwt
import time
app = Flask(__name__)
API_KEY = "devkey"
API_SECRET = "devsecret"
ISSUER = "myapp"
def generate_token(user_id: str, room_name: str, ttl: int = 3600) -> str:
now = int(time.time())
payload = {
"iss": ISSUER,
"sub": user_id,
"exp": now + ttl,
"room": room_name,
"grant": {
"roomJoin": True,
"room": room_name,
"canPublish": True,
"canSubscribe": True,
},
}
return jwt.encode(payload, API_SECRET, algorithm="HS256")
@app.route("/token")
def token():
user_id = request.args.get("user", "guest")
room = request.args.get("room", "demo")
token = generate_token(user_id, room)
return jsonify({"token": token, "room": room})
if __name__ == "__main__":
app.run(port=5000)
Running this Flask app on localhost:5000 gives you a ready‑to‑use token when you call /token?user=alice&room=testroom. The token encodes all permissions needed for a standard video participant.
Client‑Side JavaScript
Below is a complete HTML page that fetches a token, connects to LiveKit, and streams the local webcam. It also renders remote participants automatically.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>LiveKit One‑to‑One Demo</title>
<script src="https://unpkg.com/livekit-client@latest/dist/livekit-client.umd.min.js"></script>
<style>
video { width: 300px; margin: 5px; }
#videos { display: flex; flex-wrap: wrap; }
</style>
</head>
<body>
<div id="videos"></div>
<script>
const { connect, createLocalVideoTrack } = LiveKit;
async function start() {
// 1️⃣ Fetch token from our Flask server
const resp = await fetch('http://localhost:5000/token?user=alice&room=demo');
const { token, room } = await resp.json();
// 2️⃣ Connect to LiveKit server
const lkRoom = await connect('ws://localhost:7881', token, {
adaptiveStream: true,
});
// 3️⃣ Publish local webcam
const videoTrack = await createLocalVideoTrack();
await lkRoom.localParticipant.publishTrack(videoTrack);
document.getElementById('videos').appendChild(videoTrack.attach());
// 4️⃣ Render remote participants
lkRoom.on('participantConnected', participant => {
participant.on('trackSubscribed', track => {
document.getElementById('videos').appendChild(track.attach());
});
});
}
start().catch(console.error);
</script>
</body>
</html>
Open this file in two browser tabs (or two different devices) and you’ll see a live video chat with no extra configuration. The code demonstrates the three essential steps: token acquisition, room connection, and track publishing.
Pro Tip: When deploying to production, always serve the token endpoint over HTTPS and enforce short token lifetimes (e.g., 15 minutes). This limits the window for token replay attacks.
Example 2: Screen Sharing with Server‑Side Recording
Why Recording Matters
Many businesses need to archive meetings for compliance or training. LiveKit’s built‑in recording service captures the raw media streams and stores them in a configurable bucket (e.g., AWS S3). The following example shows how to enable screen sharing on the client and trigger a server‑side recording session via the REST API.
Client Code for Screen Share
// Assume we already have a connected LiveKit room instance named `lkRoom`
async function startScreenShare() {
// Prompt the user to pick a screen/window/tab
const screenTrack = await LiveKit.createLocalScreenTracks({
resolution: { width: 1280, height: 720 },
frameRate: 15,
});
// Publish the screen track as a separate video source
await lkRoom.localParticipant.publishTrack(screenTrack[0]);
// Optional: replace webcam video with a placeholder
const placeholder = document.createElement('div');
placeholder.textContent = 'Screen sharing active';
placeholder.style.width = '300px';
placeholder.style.height = '200px';
placeholder.style.background = '#333';
placeholder.style.color = '#fff';
placeholder.style.display = 'flex';
placeholder.style.alignItems = 'center';
placeholder.style.justifyContent = 'center';
document.body.appendChild(placeholder);
}
// Call the function on a button click
document.getElementById('shareBtn').addEventListener('click', startScreenShare);
The `createLocalScreenTracks` helper automatically falls back to the appropriate browser API (getDisplayMedia) and returns a video track ready for publishing.
Starting a Recording Session (Python)
LiveKit exposes a simple HTTP endpoint to start and stop recordings. Below is a Flask snippet that initiates a recording for a given room and saves the output to an S3 bucket.
import requests
import os
LIVEKIT_URL = "http://localhost:7880"
API_KEY = "devkey"
API_SECRET = "devsecret"
def start_recording(room_name: str):
endpoint = f"{LIVEKIT_URL}/v1/rooms/{room_name}/recordings"
auth = (API_KEY, API_SECRET)
payload = {
"output": {
"type": "s3",
"bucket": "my-livekit-recordings",
"region": "us-east-1",
"accessKey": os.getenv("AWS_ACCESS_KEY_ID"),
"secretKey": os.getenv("AWS_SECRET_ACCESS_KEY"),
},
"layout": "grid",
}
resp = requests.post(endpoint, json=payload, auth=auth)
resp.raise_for_status()
return resp.json()["recordingId"]
def stop_recording(room_name: str, recording_id: str):
endpoint = f"{LIVEKIT_URL}/v1/rooms/{room_name}/recordings/{recording_id}"
auth = (API_KEY, API_SECRET)
requests.delete(endpoint, auth=auth).raise_for_status()
Call `start_recording("demo")` before the screen share begins, and `stop_recording("demo", rec_id)` when the session ends. The server will stitch together all participant tracks into a single MP4 file stored in your bucket.
Pro Tip: Use the “grid” layout for multi‑person calls; for presenter‑centric meetings, switch to “speaker” layout to highlight the active speaker automatically.
Real‑World Use Cases
Customer Support & Live Chat
Support agents can embed a LiveKit widget directly into a help‑desk portal, allowing customers to share screens, annotate, and co‑browse in real time. Because the server can be self‑hosted behind a corporate firewall, sensitive data never leaves the organization.
Online Education Platforms
Virtual classrooms benefit from LiveKit’s low‑latency video and data channels for quizzes, whiteboard sync, and breakout rooms. The ability to record sessions automatically means students can revisit lectures, and compliance teams can audit content for quality assurance.
Telehealth & Remote Diagnostics
Healthcare providers require HIPAA‑compliant infrastructure. By deploying LiveKit in a VPC with encrypted storage, clinics can run secure video consultations, capture diagnostic images, and store recordings in a compliant bucket for later review.
Scaling & Deployment Strategies
LiveKit’s clustering model uses a gossip protocol to share state across nodes. For a typical SaaS product, you might start with a single node for development, then add additional nodes behind a load balancer as traffic grows. Each node can be autoscaled based on CPU or bandwidth metrics.
When deploying to Kubernetes, the official Helm chart simplifies the process. It provisions a StatefulSet for the server, a Service for external access, and optional sidecars for TURN/STUN. Remember to enable persistent volumes for the recording service if you plan to store files locally.
Security & Compliance Considerations
LiveKit supports end‑to‑end encryption (E2EE) out of the box. By enabling the `e2ee` flag on the client, media is encrypted on the sender side and never decrypted on the server, satisfying strict privacy requirements.
await lkRoom.setE2EEEnabled(true);
In addition to E2EE, you can enforce role‑based access control (RBAC) via token grants. For instance, a “viewer” token can subscribe only, while a “moderator” token can publish and mute participants. Combine this with network policies that restrict TURN traffic to trusted IP ranges for an extra layer of protection.
Pro Tips for Production‑Ready LiveKit Apps
- Monitor SFU health: Export LiveKit metrics to Prometheus and set alerts on CPU usage, packet loss, and participant count.
- Use adaptive streaming: Enable `adaptiveStream` in the client to let LiveKit downgrade video quality on flaky connections, preserving call continuity.
- Leverage data channels: For collaborative features (e.g., shared cursors, chat), send JSON over the built‑in data channel instead of opening separate WebSocket connections.
- Cache tokens client‑side: Store the JWT in memory and refresh it proactively before expiry to avoid abrupt disconnections.
- Graceful shutdown: When scaling down a node, let LiveKit drain existing rooms first to prevent dropped participants.
Conclusion
LiveKit delivers a powerful, open‑source alternative to commercial WebRTC services, giving developers the flexibility to host, scale, and customize real‑time communication pipelines. By understanding its architecture, leveraging the SDKs for quick prototypes, and applying best‑practice security and scaling patterns, you can build everything from simple one‑to‑one chats to enterprise‑grade virtual classrooms and telehealth platforms. The code snippets above provide a solid foundation—now it’s up to you to iterate, experiment, and bring your next real‑time product to life with LiveKit.