> ## Documentation Index
> Fetch the complete documentation index at: https://docs.daily.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Live Streaming

> Use startLiveStreaming(), updateLiveStreaming(), and stopLiveStreaming() to control live streaming from daily-js.

<Card title="Live streaming overview" icon="signal-stream" href="/docs/guides/features/live-streaming">
  New to Daily live streaming? Start here — covers prerequisites, supported RTMP providers and URL formats, and HLS streaming.
</Card>

Only **meeting owners** or **streaming admins** (participants with `canAdmin: ['streaming']`) can start, update, or stop a live stream. See [prerequisites](/docs/guides/features/live-streaming#prerequisites) in the overview for how to create the right meeting token.

<Note>
  All streaming — RTMP and HLS — goes through `startLiveStreaming()`. For RTMP, pass an ingest URL directly. For HLS, configure [`streaming_endpoints`](/docs/guides/features/live-streaming/hls) on the room first, then pass the endpoint name. The rest of this guide applies to both.
</Note>

## Starting a live stream

[`startLiveStreaming()`](/reference/daily-js/instance-methods/start-live-streaming) begins streaming to one or more RTMP endpoints:

```javascript theme={null}
// Stream to a single RTMP URL
call.startLiveStreaming({
  rtmpUrl: 'rtmps://live.example.com/app/stream-key',
});

// Stream to multiple destinations at once
call.startLiveStreaming({
  endpoints: [
    { endpoint: 'rtmps://a.ingest.example.com/live/key-1' },
    { endpoint: 'rtmps://b.ingest.example.com/live/key-2' },
  ],
});
```

## Options

<ParamField path="rtmpUrl" type="string | string[]">
  One or more RTMP/RTMPS ingest URLs. Mutually exclusive with `endpoints`.
</ParamField>

<ParamField path="endpoints" type="DailyStreamingEndpoint[]">
  An array of endpoint objects `{ endpoint: string }`. Use this instead of `rtmpUrl` when you need to dynamically add or remove destinations mid-stream with [`addLiveStreamingEndpoints()`](/reference/daily-js/instance-methods/add-live-streaming-endpoints) and [`removeLiveStreamingEndpoints()`](/reference/daily-js/instance-methods/remove-live-streaming-endpoints).
</ParamField>

<ParamField path="width" type="number">
  Output width in pixels. Default: `1280`. Can only be set at start.
</ParamField>

<ParamField path="height" type="number">
  Output height in pixels. Default: `720`. Can only be set at start.
</ParamField>

<ParamField path="fps" type="number">
  Frames per second. Default: `30`.
</ParamField>

<ParamField path="videoBitrate" type="number">
  Video bitrate in kbps.
</ParamField>

<ParamField path="audioBitrate" type="number">
  Audio bitrate in kbps.
</ParamField>

<ParamField path="minIdleTimeOut" type="number">
  Seconds of idle time before the stream automatically stops. Default: `60`.
</ParamField>

<ParamField path="maxDuration" type="number">
  Maximum stream duration in seconds.
</ParamField>

<ParamField path="backgroundColor" type="string">
  Background color for empty areas in the composed frame. CSS hex string. Can only be set at start.
</ParamField>

<ParamField path="instanceId" type="string">
  Identifier for this streaming instance. Required when running multiple simultaneous streams. See [multiple simultaneous streams](#multiple-simultaneous-streams).
</ParamField>

<ParamField path="layout" type="DailyLiveStreamingLayoutConfig">
  Participant tile layout for the composed output. See [`DailyStreamingLayoutConfig`](/reference/daily-js/types/daily-streaming-layout-config) for the full type reference, or [layout options](#layout-options) below for usage examples.
</ParamField>

## Layout options

Live streaming supports the same layout presets as recording — `default`, `single-participant`, `active-participant`, `portrait`, and `custom` — except `audio-only`, which is recording-only. See [`DailyStreamingLayoutConfig`](/reference/daily-js/types/daily-streaming-layout-config) for field-level documentation on each preset. For screenshots, see the [live streaming overview](/docs/guides/features/live-streaming#live-streaming-layout-options).

### `default`

```javascript theme={null}
call.startLiveStreaming({
  rtmpUrl: 'rtmps://live.example.com/app/key',
  layout: {
    preset: 'default',
    max_cam_streams: 6,
    participants: { sort: 'active' },
  },
});
```

### `single-participant`

```javascript theme={null}
call.startLiveStreaming({
  rtmpUrl: 'rtmps://live.example.com/app/key',
  layout: {
    preset: 'single-participant',
    session_id: presenterSessionId,
  },
});
```

### `active-participant`

```javascript theme={null}
call.startLiveStreaming({
  rtmpUrl: 'rtmps://live.example.com/app/key',
  layout: { preset: 'active-participant' },
});
```

### `portrait`

```javascript theme={null}
call.startLiveStreaming({
  rtmpUrl: 'rtmps://live.example.com/app/key',
  layout: {
    preset: 'portrait',
    variant: 'inset',  // 'vertical' (default) or 'inset'
  },
});
```

### `custom`

Use Daily's VCS baseline composition to fully control the layout programmatically — modes, overlays, labels, participant ordering, and more. The [`startLiveStreaming()` reference](/reference/daily-js/types/daily-streaming-layout-config#vcs-baseline-composition) documents all available `composition_params`.

```javascript theme={null}
call.startLiveStreaming({
  rtmpUrl: 'rtmps://live.example.com/app/key',
  layout: {
    preset: 'custom',
    composition_id: 'daily:baseline',
    composition_params: {
      mode: 'dominant',
      'videoSettings.showParticipantLabels': true,
    },
  },
});
```

### `participants`

Several presets accept a `participants` field to filter which participants' video and audio are included in the stream. See [`DailyStreamingLayoutConfig`](/reference/daily-js/types/daily-streaming-layout-config#participants) for the full field list.

<Note>
  You must resend `participants` on every `updateLiveStreaming()` call — it is not persisted from the previous call. If omitted on update, all participants will be included.
</Note>

## Updating layout mid-stream

Call [`updateLiveStreaming()`](/reference/daily-js/instance-methods/update-live-streaming) to switch layouts without interrupting the stream:

```javascript theme={null}
call.updateLiveStreaming({
  layout: { preset: 'active-participant' },
});
```

## Adding and removing endpoints

You can add new RTMP destinations to a running stream or remove existing ones without stopping the stream.

<Warning>
  `addLiveStreamingEndpoints()` and `removeLiveStreamingEndpoints()` only work when the stream was started with the `endpoints` parameter — not `rtmpUrl`. If you need to add or remove destinations mid-stream, use `endpoints` when calling `startLiveStreaming()`.
</Warning>

```javascript theme={null}
// Add a backup destination mid-stream
call.addLiveStreamingEndpoints({
  endpoints: [{ endpoint: 'rtmps://backup.cdn.example.com/live/key-3' }],
});

// Remove a destination (stream continues to other destinations)
call.removeLiveStreamingEndpoints({
  endpoints: [{ endpoint: 'rtmps://a.ingest.example.com/live/key-1' }],
});
```

<Note>
  Do not use `removeLiveStreamingEndpoints()` to remove all endpoints — use `stopLiveStreaming()` instead. Attempting to remove the last endpoint will fail and trigger a `live-streaming-warning` nonfatal error.
</Note>

## Stopping a live stream

[`stopLiveStreaming()`](/reference/daily-js/instance-methods/stop-live-streaming) ends all active streaming endpoints:

```javascript theme={null}
call.stopLiveStreaming();
```

## Multiple simultaneous streams

Pass a unique `instanceId` to run parallel streams — for example, a high-quality CDN stream and a lower-bitrate social stream simultaneously:

```javascript theme={null}
// High-quality CDN stream
call.startLiveStreaming({
  instanceId: 'cdn',
  rtmpUrl: 'rtmps://cdn.example.com/live/key',
  width: 1920,
  height: 1080,
  videoBitrate: 6000,
  layout: { preset: 'active-participant' },
});

// Social media stream at a lower bitrate
call.startLiveStreaming({
  instanceId: 'social',
  rtmpUrl: 'rtmps://live.twitch.tv/app/stream-key',
  width: 1280,
  height: 720,
  videoBitrate: 2500,
  layout: { preset: 'active-participant' },
});

// Update only the CDN stream's layout
call.updateLiveStreaming({
  instanceId: 'cdn',
  layout: { preset: 'default', max_cam_streams: 9 },
});

// Stop each instance independently
call.stopLiveStreaming({ instanceId: 'cdn' });
call.stopLiveStreaming({ instanceId: 'social' });
```

For full details including billing and `max_streaming_instances_per_room` configuration, see the [multi-instance guide](/docs/guides/features/live-streaming/multi-instance).

## Live streaming events

Full payload details are in the [live streaming events reference](/reference/daily-js/events/live-streaming-events).

### `live-streaming-started`

Fired when the stream is ready and the first video frame has been delivered to the ingest server.

```javascript theme={null}
call.on('live-streaming-started', ({ instanceId, layout }) => {
  streamingBadge.hidden = false;
});
```

### `live-streaming-updated`

Fired when the first video frame is published to the RTMP server (`state: 'connected'`), when the connection is interrupted (`state: 'interrupted'`), or when it reconnects (`state: 'connected'`). If streaming was started with `endpoints`, the payload also includes the `endpoint` object.

```javascript theme={null}
call.on('live-streaming-updated', ({ endpoint, state }) => {
  if (state === 'interrupted') {
    showReconnectingBanner();
  } else if (state === 'connected') {
    hideReconnectingBanner();
  }
});
```

### `live-streaming-stopped`

```javascript theme={null}
call.on('live-streaming-stopped', () => {
  streamingBadge.hidden = true;
});
```

### `live-streaming-error`

```javascript theme={null}
call.on('live-streaming-error', ({ errorMsg }) => {
  console.error('Streaming error:', errorMsg);
});
```

## Complete example

```javascript theme={null}
const call = Daily.createCallObject();
await call.join({
  url: 'https://your-domain.daily.co/room',
  token: 'OWNER_OR_STREAMING_ADMIN_TOKEN',
});

const RTMP_URL = 'rtmps://live.example.com/app/your-stream-key';

document.getElementById('go-live').onclick = () => {
  call.startLiveStreaming({
    rtmpUrl: RTMP_URL,
    width: 1280,
    height: 720,
    fps: 30,
    videoBitrate: 3000,
    layout: { preset: 'active-participant' },
  });
};

document.getElementById('stop-live').onclick = () => {
  call.stopLiveStreaming();
};

call.on('live-streaming-started', () => {
  document.getElementById('go-live').disabled = true;
  document.getElementById('stop-live').disabled = false;
});

call.on('live-streaming-updated', ({ state }) => {
  const status = document.getElementById('stream-status');
  status.textContent = state === 'connected' ? '● Live' : '⚠ Reconnecting…';
});

call.on('live-streaming-stopped', () => {
  document.getElementById('go-live').disabled = false;
  document.getElementById('stop-live').disabled = true;
});

call.on('live-streaming-error', ({ errorMsg }) => {
  alert(`Stream error: ${errorMsg}`);
});
```
