LiveKit is an open source project that provides scalable, multi-user conferencing over WebRTC. It's designed to give you everything you need to build real-time audio and/or video experiences in your applications.
Head to our playground to try it live. Build a Zoom-like conferencing app in under 100 lines of code!
WebRTC is a powerful protocol that now has ubiquitous support across all major browsers and mobile platforms. However, building apps with it isn't simple; it requires an understanding of core protocol concepts and developers are responsible for complexities like signaling and coordinating connections between peers. As a peer-to-peer protocol, scaling WebRTC to large numbers of peers also becomes challenging.
While hosted solutions like Twilio and Agora exist, they can be costly, have limited flexibility and as proprietary products, create vendor lock-in. Other open source solutions also exist, but they have a steep learning curve and are daunting to customize and deploy.
We set out to build a free and open source implementation of WebRTC rooms that's easily embeddable within any app. LiveKit provides an opinionated, end-to-end real time communications solution with first-party SDKs for all the major software platforms.
LiveKit is written in Go, using WebRTC implementation from Pion.
LiveKit is horizontally-scalable. You can run it on one node or 100, with an identical configuration. Nodes use peer-to-peer routing via Redis to ensure clients in the same room are connected to the same node.
LiveKit has no external dependencies when running in single-node. Redis is required for a distributed setup spanning multiple nodes.
import {Participant,Room,RoomEvent,Track,} from 'livekit-client'const url = 'wss://your_host'const token = 'jwt_token'const room = new Room({adaptiveStream: true,dynacast: true,})// set up listenersroom.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {attachTrack(track, participant)})await room.connect()await room.localParticipant.setMicrophoneEnabled(true)const localPub = await room.localParticipant.setCameraEnabled(true)if (localPub) {attachTrack(localPub.track!, room.localParticipant)}function attachTrack(track: Track, participant: Participant) {// creates a new audio or video elementconst element = track.attach()// find the target element for participant...target.appendChild(element)}
import { LiveKitRoom } from 'livekit-react'import { Room } from 'livekit-client'export const RoomPage = () => {const url = 'wss://your_host'const token = 'your_token'return (<div className="roomContainer"><LiveKitRoom url={url} token={token} onConnected={room => onConnected(room)}/></div>)}function onConnected(room: Room) {// publish both camera and mic with a single permission requestroom.localParticipant.enableCameraAndMicrophone()}
import LiveKitimport UIKitclass RoomViewController: UIViewController {lazy var room = Room(delegate: self)lazy var remoteVideoView: VideoView = {let videoView = VideoView()view.addSubview(videoView)// additional initialization ...return videoView}()lazy var localVideoView: VideoView = {let videoView = VideoView()view.addSubview(videoView)// additional initialization ...return videoView}()override func viewDidLoad() {super.viewDidLoad()view.backgroundColor = .whitelet url: String = "ws://your_host"let token: String = "your_jwt_token"room.connect(url, token).then { room in// Publish camera & microom.localParticipant?.setCamera(enabled: true)room.localParticipant?.setMicrophone(enabled: true)}.catch { error in// failed to connect}}}extension RoomViewController: RoomDelegate {func room(_ room: Room, localParticipant: LocalParticipant, didPublish publication: LocalTrackPublication) {guard let track = publication?.track as? VideoTrack else {return}DispatchQueue.main.async {localVideoView.track = track}}func room(_ room: Room, participant: RemoteParticipant, didSubscribe publication: RemoteTrackPublication, track: Track) {guard let track = track as? VideoTrack else {return}// simple example that renders a single remote participantDispatchQueue.main.async {remoteVideoView.track = track}}}
class MainActivity : AppCompatActivity(), RoomListener {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)...val url = "wss://your_host";val token = "your_token"launch {val room = LiveKit.connect(applicationContext,url,token,roomOptions = RoomOptions(adaptiveStream = true, dynacast = true),)val localParticipant = room.localParticipantlocalParticipant.setMicrophoneEnabled(true)localParticipant.setCameraEnabled(true)attachVideo(videoTrack, localParticipant)}}override fun onTrackSubscribed(track: Track,publication: RemoteTrackPublication,participant: RemoteParticipant,room: Room) {if (track is VideoTrack) {attachVideo(track, participant)}}private fun attachVideo(videoTrack: VideoTrack, participant: Participant) {// viewBinding.renderer is a `org.webrtc.SurfaceViewRenderer` in your// layoutvideoTrack.addRenderer(viewBinding.renderer)}}
final roomOptions = RoomOptions(adaptiveStream: true,dynacast: true,)final room = await LiveKitClient.connect(url, token, roomOptions: roomOptions);try {// video will fail when running in ios simulatorawait room.localParticipant.setCameraEnabled(true);} catch (error) {print('Could not publish video, error: $error');}await room.localParticipant.setMicrophoneEnabled(true);
import (lksdk "github.com/livekit/server-sdk-go")// Go SDK is a more advanced SDK that gives you programmatic access as a client// enabling you to publish and record audio/video/data to the room.func main() {url := "<host>"apiKey := "api-key"apiSecret := "api-secret"roomName := "myroom"identity := "botuser"room := lksdk.CreateRoom()room.Callback.OnTrackSubscribed = trackSubscribederr := room.Join(url, lksdk.ConnectInfo{APIKey: apiKey,APISecret: apiSecret,RoomName: roomName,ParticipantIdentity: identity,})if err != nil {panic(err)}...room.Disconnect()}func trackSubscribed(track *webrtc.TrackRemote, publication lksdk.TrackPublication, rp *lksdk.RemoteParticipant) {}
var room = new Room();yield return room.Connect(url, token);room.TrackSubscribed += (track, publication, participant) =>{if (publication.Kind == TrackKind.Video){var video = track.Attach() as HTMLVideoElement;video.VideoReceived += tex =>{RawImage.texture = tex;}}};yield return room.LocalParticipant.EnableCameraAndMicrophone();