Guides / Recording & Livestreaming / Composite Recording


RoomComposite egress

One common requirement when recording a room is to capture all of the participants and interactions that take place. This can be challenging in a multi-user application, where different users may be joining, leaving, turning their cameras on and off. It may also be desirable for the recording to look as close to the actual application experience as possible, capturing the richness and interactivity of your application.

A RoomComposite egress uses a web app to create the composited view, rendering the output with an instance of headless Chromium. In most cases, your existing LiveKit application can be used as a compositing template with few modifications.

Default layouts

We provide a few default compositing layouts that works out of the box. They'll be used by default if a custom template URL is not passed in. These templates are deployed to (source).

While it's a great starting point, you can easily create your own layout using standard web technologies that you are already familiar with.

gridGrid layout
speakerSpeaker layout
single-speakerSingle speaker layout

Additionally, you can use a -light suffix to change background color to white. i.e. grid-light.

Starting a RoomComposite

The following example starts a recording for archival to S3. It uses an EncodedFileOutput for the egress.

const egressClient = new EgressClient(
const output = {
fileType: EncodedFileType.MP4,
filepath: 'livekit-demo/room-composite-test.mp4',
s3: {
accessKey: 'aws-access-key',
secret: 'aws-access-secret',
region: 'aws-region',
bucket: 'my-bucket'
const info = await egressClient.startRoomCompositeEgress('my-room', output, 'speaker-dark');
const egressID = info.egressId;
  • fileRequest.Output.File.Output can be left empty if one of s3, azure, or gcp is supplied with your config.
  • fileRequest.Output.File.Filepath can be left empty, and a unique filename will be generated based on the date and room name.

Stream to RTMP

To stream to RTMP, you would use a StreamOutput. When multiple RTMP URLs are specified, LiveKit would multi-cast to all of them at the same time. Stream URLs can be changed even after the egress has started with UpdateStream

const output: StreamOutput = {
protocol: StreamProtocol.RTMP,
urls: [
const info = await egressClient.startRoomCompositeEgress('my-room', output, 'grid');

Stream to HLS

As an alternative to generating a single media file, it is possible to have the Egress service generate segments by using the SegmentedFileOutput output. The Egress service will split the output in media segments of equal duration (6s by default), and generate a manifest listing all the generated segments.

Currently, only HTTP Live Streaming compatible segments (using the MPEG TS file format) and manifests are supported.

If one of s3, azure, or gcp is supplied with the config or request, each segment will be uploaded with an updated manifest as soon as it is generated. This allows playback of the exported media while the export is still ongoing.

const output: SegmentedFileOutput = {
filenamePrefix: 'livekit-demo/room-composite-test-',
playlistName: 'room-composite-test.m3u8',
segmentDuration: 6,
protocol: SegmentedFileProtocol.HLS_PROTOCOL,
s3: {
accessKey: 'aws-access-key',
secret: 'aws-access-secret',
region: 'aws-region',
bucket: 'my-bucket'
const info = await egressClient.startRoomCompositeEgress('my-room', output, 'grid');
  • segmentsRequest.Output.Segments.FilenamePrefix and segmentsRequest.Output.Segments.PlaylistName can be left empty, and unique filenames will be generated based on the date and room name.
  • The playlist file will always be created in the same directory as the segments, as indicated in segmentsRequest.Output.Segments.FilenamePrefix, regardless of any potential directory prefix added to segmentsRequest.Output.Segments.PlaylistName.

Audio-only composite

If your application is audio-only, you can export a mixed audio file containing audio from all participants in the room. To start an audio-only composite, pass audio_only=true when starting an Egress.

const info = await egressClient.startRoomCompositeEgress(
undefined, // EncodingOptionsPreset | EncodingOptions
true, // audioOnly