> ## 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.

# Screen Sharing

> Start, stop, and configure screen sharing in a Daily call, including sharing a pre-captured MediaStream and fine-tuning video quality presets.

Daily lets participants share their screen, an application window, or a browser tab with other call participants.

`startScreenShare()` accepts one of two argument shapes — or no arguments at all:

* **Let Daily handle capture** (`DailyStartScreenShare`) — Daily calls `getDisplayMedia()` for you, optionally using constraints and quality settings you provide. **This is the recommended option for most use cases.**
* **Bring your own stream** (`DailyStartScreenShareFromStream`) — you call `getDisplayMedia()` yourself (or source a stream from Electron's `desktopCapturer`, a canvas, etc.) and hand the resulting `MediaStream` to Daily.

To stop sharing: `call.stopScreenShare()`.

`startScreenShare()` is non-blocking — the browser's native picker opens asynchronously. Listen for [the events below](#events) to know when sharing actually begins or is canceled.

## Let Daily handle capture

<Badge color="green">Recommended</Badge>

Call `startScreenShare()` with no arguments to use Daily's defaults, or pass a `DailyStartScreenShare` object to configure the capture:

<ParamField path="displayMediaOptions" type="DailyDisplayMediaStreamOptions">
  Options forwarded directly to `navigator.mediaDevices.getDisplayMedia()`. Use
  these to request system audio, control surface switching, and more.
</ParamField>

<ParamField path="screenVideoSendSettings" type="DailyVideoSendSettings | DailyScreenVideoSendSettingsPreset">
  Controls the video encoding quality for the shared screen. Accepts either a
  preset string or a custom `DailyVideoSendSettings` object. [See below](#send-settings) for details.
</ParamField>

```javascript theme={null}
// No arguments — Daily uses browser defaults
call.startScreenShare();

// With options
call.startScreenShare({
  displayMediaOptions: {
    audio: true,
    video: { frameRate: { ideal: 30, max: 60 } },
    selfBrowserSurface: 'exclude',
    systemAudio: 'include',
  },
  screenVideoSendSettings: 'detail-optimized',
});
```

### `DailyDisplayMediaStreamOptions`

<ParamField path="audio" type="boolean | MediaTrackConstraints">
  Request system audio in the screen share. Default is browser-dependent.
</ParamField>

<ParamField path="video" type="boolean | MediaTrackConstraints">
  Video constraints for the captured surface.
</ParamField>

<ParamField path="selfBrowserSurface" type="'include' | 'exclude'">
  Whether to include the current browser tab as a selectable source.
</ParamField>

<ParamField path="surfaceSwitching" type="'include' | 'exclude'">
  Whether to show an in-call surface-switching control (Chrome 107+).
</ParamField>

<ParamField path="systemAudio" type="'include' | 'exclude'">
  Whether to offer system audio capture in the picker (Windows/Chrome only).
</ParamField>

## Bring your own stream

If you already have a `MediaStream` — captured with your own `getDisplayMedia()` call, sourced from an Electron `desktopCapturer`, or otherwise constructed — pass it via `DailyStartScreenShareFromStream`. Daily will not call `getDisplayMedia()` in this case.

<ParamField path="mediaStream" type="MediaStream" required>
  A `MediaStream` whose first video track (and optionally first audio track)
  will be used as the screen share source.
</ParamField>

<ParamField path="screenVideoSendSettings" type="DailyVideoSendSettings | DailyScreenVideoSendSettingsPreset">
  Optional quality preset or custom settings for the provided video track. [See below](#send-settings).
</ParamField>

```javascript theme={null}
const stream = await navigator.mediaDevices.getDisplayMedia({ video: true });
call.startScreenShare({ mediaStream: stream });
```

### Screenshare streams vs. custom tracks

If you have a `MediaStream` you want to send, you have two options: pass it to `startScreenShare({ mediaStream: stream })` or send it as a custom track via `startCustomTrack()`. These are not interchangeable — Daily treats screen share video tracks differently from other video tracks, and routing a stream through the wrong path will cause incorrect behavior.

In Chromium-based browsers, a screen share video track becomes `muted` at the browser level whenever its content is not changing — for example, when a shared document is idle. This is normal browser behavior and does not mean the user stopped sharing or there are issues with the track. To avoid incorrectly firing `track-stopped` or transitioning to an `interrupted` state, Daily suppresses the `muted` signal on screen share video tracks. This suppression only applies to tracks sent via `startScreenShare()`.

**Use `startScreenShare({ mediaStream: stream })` when your stream is screen content** — a `getDisplayMedia()` capture, an Electron `desktopCapturer` stream, or any source that may go idle between updates. The muted-suppression handling will prevent spurious interruptions.

**Use `startCustomTrack()` when your stream is not screen content** — a canvas animation, a virtual camera, or any source that should respond normally to the `muted` signal. Routing this through `startScreenShare()` would mask legitimate track state changes.

## Send Settings

Both methods of screensharing allow you to define the quality of the sending stream by either providing a preset or passing a custom `DailyVideoSendSettings` object. `DailyScreenVideoSendSettingsPreset` provides four named presets optimized for different content types:

| Preset                         | Best for                                     |
| ------------------------------ | -------------------------------------------- |
| `'default-screen-video'`       | General purpose (Daily's default)            |
| `'detail-optimized'`           | Text-heavy content — slides, IDEs, documents |
| `'motion-optimized'`           | Video playback, animations, fast UI          |
| `'motion-and-detail-balanced'` | Mixed content such as interactive demos      |

For full control, pass a `DailyVideoSendSettings` object:

```javascript theme={null}
call.startScreenShare({
  screenVideoSendSettings: {
    maxQuality: 'high',
    encodings: {
      low:  { maxBitrate: 500_000, maxFramerate: 5 },
      high: { maxBitrate: 4_000_000, maxFramerate: 30 },
    },
  },
});
```

## Updating screen share state mid-share

Use `updateScreenShare()` to enable or disable the screen video or audio tracks after sharing has started — without stopping and restarting the entire screen share:

```javascript theme={null}
// Mute screen audio while keeping screen video active
call.updateScreenShare({
  screenVideo: { enabled: true },
  screenAudio: { enabled: false },
});

// Re-enable screen audio
call.updateScreenShare({
  screenVideo: { enabled: true },
  screenAudio: { enabled: true },
});
```

## Reading local screen share state

```javascript theme={null}
const isVideoActive = call.localScreenVideo(); // true if screen video is active
const isAudioActive = call.localScreenAudio(); // true if screen audio is active
```

## Events

### `local-screen-share-started`

Fired when the local participant's screen share begins. Useful for updating UI to show a "stop sharing" button.

```javascript theme={null}
call.on('local-screen-share-started', () => {
  stopSharingBtn.hidden = false;
  startSharingBtn.hidden = true;
});
```

### `local-screen-share-stopped`

Fired when screen sharing ends — either because your code called `stopScreenShare()` or because the user clicked the browser's built-in "Stop sharing" button.

```javascript theme={null}
call.on('local-screen-share-stopped', () => {
  stopSharingBtn.hidden = true;
  startSharingBtn.hidden = false;
});
```

### `local-screen-share-canceled`

Fired when the user opens the browser picker but then dismisses it without selecting a source.

```javascript theme={null}
call.on('local-screen-share-canceled', () => {
  console.log('User dismissed the screen share picker.');
});
```

### Detecting remote screen shares

Remote screen share tracks arrive via the standard `track-started` / `track-stopped` events with `type === 'screenVideo'` or `type === 'screenAudio'`:

```javascript theme={null}
call.on('track-started', ({ participant, track, type }) => {
  if (type === 'screenVideo' && !participant.local) {
    const video = document.createElement('video');
    video.srcObject = new MediaStream([track]);
    video.autoplay = true;
    document.getElementById('screen-container').append(video);
  }
});

call.on('track-stopped', ({ participant, type }) => {
  if (type === 'screenVideo' && !participant?.local) {
    document.getElementById('screen-container').innerHTML = '';
  }
});
```

## Complete example

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

const startBtn = document.getElementById('start-share');
const stopBtn  = document.getElementById('stop-share');

startBtn.onclick = () => {
  call.startScreenShare({
    displayMediaOptions: { audio: true, selfBrowserSurface: 'exclude' },
    screenVideoSendSettings: 'detail-optimized',
  });
};

stopBtn.onclick = () => call.stopScreenShare();

call.on('local-screen-share-started', () => {
  startBtn.disabled = true;
  stopBtn.disabled = false;
});

call.on('local-screen-share-stopped', () => {
  startBtn.disabled = false;
  stopBtn.disabled = true;
});

call.on('local-screen-share-canceled', () => {
  console.log('Picker was dismissed.');
});
```
