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

# Media events

> Events for media track lifecycle — when tracks start, stop, and when audio level and face detection data is available.

These events fire when media tracks start or stop, when screen sharing changes, and when audio level and face detection information is available. Support varies per event — see the badge on each section.

***

## track-started

<Badge color="red">{"✗"} Prebuilt</Badge> <Badge color="green">{"✓"} Custom</Badge>

Fires when a participant's media track begins. Use this to attach the track to a media element or update your UI when a new audio, video, or screen share track becomes available.

<ResponseField name="action" type="string">
  Always `"track-started"`.
</ResponseField>

<ResponseField name="callClientId" type="string">
  The ID of the call client instance that emitted this event.
</ResponseField>

<ResponseField name="participant" type="DailyParticipant">
  The participant whose track started. See [`participants()`](/reference/daily-js/instance-methods/participants) for the full shape.
</ResponseField>

<ResponseField name="track" type="MediaStreamTrack">
  The [MediaStreamTrack](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) that started.
</ResponseField>

<ResponseField name="type" type="string">
  The track type. One of `'audio'`, `'video'`, `'screenAudio'`, `'screenVideo'`, or a custom track name.
</ResponseField>

```json theme={null}
// Example event object
{
  "action": "track-started",
  "callClientId": "17225364729060.9442072768918943",
  "participant": {
    "session_id": "f2190c86-169a-464f-c0a4-ee1d5c00af58",
    "user_id": "f2190c86-169a-464f-c0a4-ee1d5c00af58",
    "user_name": "",
    "local": true,
    "owner": false,
    "record": false,
    "networkQualityState": "good",
    "tracks": {
      "audio": { "state": "playable", "subscribed": true },
      "video": { "state": "playable", "subscribed": true },
      "screenVideo": { "state": "off", "subscribed": false },
      "screenAudio": { "state": "off", "subscribed": false }
    },
    "joined_at": "2024-06-01T12:00:00.000Z",
    "will_eject_at": "2024-06-01T13:00:00.000Z"
  },
  "track": {
    "contentHint": "",
    "enabled": true,
    "id": "60d47b9f-6b07-4039-930a-8b752c6dbd26",
    "kind": "video",
    "label": "FaceTime HD Camera (Built-in) (05ac:8514)",
    "muted": false,
    "onended": null,
    "onmute": null,
    "onunmute": null,
    "readyState": "live",
    "managedByDaily": true
  },
  "type": "video"
}
```

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

***

## track-stopped

<Badge color="red">{"✗"} Prebuilt</Badge> <Badge color="green">{"✓"} Custom</Badge>

Fires when a participant's media track stops. Use this to remove a media element or update your UI when a track is no longer active.

<ResponseField name="action" type="string">
  Always `"track-stopped"`.
</ResponseField>

<ResponseField name="callClientId" type="string">
  The ID of the call client instance that emitted this event.
</ResponseField>

<ResponseField name="participant" type="DailyParticipant">
  The participant whose track stopped. See [`participants()`](/reference/daily-js/instance-methods/participants) for the full shape.
</ResponseField>

<ResponseField name="track" type="MediaStreamTrack">
  The [MediaStreamTrack](https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack) that stopped.
</ResponseField>

<ResponseField name="type" type="string">
  The track type. One of `'audio'`, `'video'`, `'screenAudio'`, `'screenVideo'`, or a custom track name.
</ResponseField>

```json theme={null}
// Example event object
{
  "action": "track-stopped",
  "callClientId": "17225364729060.9442072768918943",
  "participant": {
    "session_id": "f2190c86-169a-464f-c0a4-ee1d5c00af58",
    "user_id": "f2190c86-169a-464f-c0a4-ee1d5c00af58",
    "user_name": "",
    "local": true,
    "owner": false,
    "record": false,
    "networkQualityState": "good",
    "tracks": {
      "audio": { "state": "playable", "subscribed": true },
      "video": { "state": "off", "subscribed": false },
      "screenVideo": { "state": "off", "subscribed": false },
      "screenAudio": { "state": "off", "subscribed": false }
    },
    "joined_at": "2024-06-01T12:00:00.000Z",
    "will_eject_at": "2024-06-01T13:00:00.000Z"
  },
  "track": {
    "contentHint": "",
    "enabled": true,
    "id": "60d47b9f-6b07-4039-930a-8b752c6dbd26",
    "kind": "video",
    "label": "FaceTime HD Camera (Built-in) (05ac:8514)",
    "muted": false,
    "onended": null,
    "onmute": null,
    "onunmute": null,
    "readyState": "ended",
    "managedByDaily": true
  },
  "type": "video"
}
```

```javascript theme={null}
call.on('track-stopped', ({ participant, track, type }) => {
  if (type === 'video' && !participant.local) {
    const videoEl = document.getElementById(`video-${participant.session_id}`);
    if (videoEl) videoEl.remove();
  }
});
```

***

## local-screen-share-started

<Badge color="red">{"✗"} Prebuilt</Badge> <Badge color="green">{"✓"} Custom</Badge>

Fires when the local participant's screen share has started. Use this to update your UI to reflect that sharing is active.

<ResponseField name="action" type="string">
  Always `"local-screen-share-started"`.
</ResponseField>

<ResponseField name="callClientId" type="string">
  The ID of the call client instance that emitted this event.
</ResponseField>

```json theme={null}
// Example event object
{
  "action": "local-screen-share-started",
  "callClientId": "17225364729060.9442072768918943"
}
```

```javascript theme={null}
call.on('local-screen-share-started', () => {
  document.getElementById('share-btn').textContent = 'Stop sharing';
  document.getElementById('share-indicator').classList.remove('hidden');
});
```

***

## local-screen-share-stopped

<Badge color="red">{"✗"} Prebuilt</Badge> <Badge color="green">{"✓"} Custom</Badge>

Fires when the local participant's screen share has stopped. Use this to reset any screen-share UI state.

<ResponseField name="action" type="string">
  Always `"local-screen-share-stopped"`.
</ResponseField>

<ResponseField name="callClientId" type="string">
  The ID of the call client instance that emitted this event.
</ResponseField>

```json theme={null}
// Example event object
{
  "action": "local-screen-share-stopped",
  "callClientId": "17225364729060.9442072768918943"
}
```

```javascript theme={null}
call.on('local-screen-share-stopped', () => {
  document.getElementById('share-btn').textContent = 'Share screen';
  document.getElementById('share-indicator').classList.add('hidden');
});
```

***

## local-screen-share-canceled

<Badge color="red">{"✗"} Prebuilt</Badge> <Badge color="green">{"✓"} Custom</Badge>

Fires when the user dismisses the browser's screen picker before ever starting a screen share.

<Note>
  Detection of this user action only works in Chrome. On Safari and Firefox, this action is reported as the user blocking the permission and a [nonfatal `screen-share-error` event](/reference/daily-js/events/error-events#screen-share-error) is sent instead.
</Note>

<ResponseField name="action" type="string">
  Always `"local-screen-share-canceled"`.
</ResponseField>

<ResponseField name="callClientId" type="string">
  The ID of the call client instance that emitted this event.
</ResponseField>

```json theme={null}
// Example event object
{
  "action": "local-screen-share-canceled",
  "callClientId": "17225364729060.9442072768918943"
}
```

```javascript theme={null}
call.on('local-screen-share-canceled', () => {
  // User closed the picker — reset the button back to its idle state.
  document.getElementById('share-btn').disabled = false;
  document.getElementById('share-btn').textContent = 'Share screen';
});
```

***

## local-audio-level

<Badge color="red">{"✗"} Prebuilt</Badge> <Badge color="green">{"✓"} Custom</Badge>

Fires at the frequency specified when [starting the local audio level observer](/reference/daily-js/instance-methods/start-local-audio-level-observer), providing the local participant's current audio level. Use this to drive a volume indicator in your UI.

<ResponseField name="action" type="string">
  Always `"local-audio-level"`.
</ResponseField>

<ResponseField name="callClientId" type="string">
  The ID of the call client instance that emitted this event.
</ResponseField>

<ResponseField name="audioLevel" type="number">
  The local participant's current audio level, from `0.0` (silent) to `1.0` (maximum).
</ResponseField>

```json theme={null}
// Example event object
{
  "action": "local-audio-level",
  "callClientId": "17225364729060.9442072768918943",
  "audioLevel": 0.149356
}
```

```javascript theme={null}
call.on('local-audio-level', ({ audioLevel }) => {
  const bar = document.getElementById('local-volume-bar');
  bar.style.width = `${Math.round(audioLevel * 100)}%`;
});
```

***

## remote-participants-audio-level

<Badge color="red">{"✗"} Prebuilt</Badge> <Badge color="green">{"✓"} Custom</Badge>

Fires at the frequency specified when [starting the remote participants audio level observer](/reference/daily-js/instance-methods/start-remote-participants-audio-level-observer), providing audio levels for all remote participants. Use this to drive per-participant volume indicators or highlight the active speaker.

<ResponseField name="action" type="string">
  Always `"remote-participants-audio-level"`.
</ResponseField>

<ResponseField name="callClientId" type="string">
  The ID of the call client instance that emitted this event.
</ResponseField>

<ResponseField name="participantsAudioLevel" type="object">
  A map of participant `session_id` to audio level (`0.0`–`1.0`). Only remote participants are included.
</ResponseField>

```json theme={null}
// Example event object
{
  "action": "remote-participants-audio-level",
  "callClientId": "17225364729060.9442072768918943",
  "participantsAudioLevel": {
    "24ebb6ed-315f-40c3-acb7-a917c4ea6202": 0.1,
    "cc3d2760-442e-4cfa-afc1-4fd387253f72": 0.0223872113856834
  }
}
```

```javascript theme={null}
call.on('remote-participants-audio-level', ({ participantsAudioLevel }) => {
  let activeSpeakerId = null;
  let maxLevel = 0;

  for (const [sessionId, level] of Object.entries(participantsAudioLevel)) {
    const bar = document.getElementById(`volume-${sessionId}`);
    if (bar) bar.style.width = `${Math.round(level * 100)}%`;

    if (level > maxLevel) {
      maxLevel = level;
      activeSpeakerId = sessionId;
    }
  }

  if (activeSpeakerId) {
    highlightActiveSpeaker(activeSpeakerId);
  }
});
```

***

## face-counts-updated

<Badge color="red">{"✗"} Prebuilt</Badge> <Badge color="green">{"✓"} Custom</Badge>

Fires when the face count in the local camera view has changed. Use this to identify how many people are visible in frame at a given time.

This event is only emitted while the `face-detection` video processor is active. See [`updateInputSettings()`](/reference/daily-js/instance-methods/update-input-settings#video-processor) for details on enabling it.

<ResponseField name="action" type="string">
  Always `"face-counts-updated"`.
</ResponseField>

<ResponseField name="callClientId" type="string">
  The ID of the call client instance that emitted this event.
</ResponseField>

<ResponseField name="faceCounts" type="number">
  The number of faces currently detected in the local camera view.
</ResponseField>

```json theme={null}
// Example event object
{
  "action": "face-counts-updated",
  "callClientId": "17225364729060.9442072768918943",
  "faceCounts": 1
}
```

```javascript theme={null}
call.on('face-counts-updated', ({ faceCounts }) => {
  const label = document.getElementById('face-count-label');
  label.textContent = `${faceCounts} ${faceCounts === 1 ? 'person' : 'people'} in frame`;
});
```

***

## See also

<CardGroup>
  <Card title="Types" icon="t" iconType="solid">
    * [DailyParticipant](/reference/daily-js/types/daily-participant)
    * [DailyTrackState](/reference/daily-js/types/daily-track-state)
  </Card>

  <Card title="Methods" icon="code" iconType="solid">
    * [setLocalVideo()](/reference/daily-js/instance-methods/set-local-video)
    * [setLocalAudio()](/reference/daily-js/instance-methods/set-local-audio)
    * [startScreenShare()](/reference/daily-js/instance-methods/start-screen-share)
    * [stopScreenShare()](/reference/daily-js/instance-methods/stop-screen-share)
  </Card>

  <Card title="Events" icon="bolt" iconType="solid">
    * [Participant events](/reference/daily-js/events/participant-events)
  </Card>

  <Card title="Guides" icon="book-open" iconType="solid">
    * [Audio and Video](/docs/daily-js/guides/audio-video)
    * [Events](/docs/daily-js/concepts/events)
    * [Tracks and Media](/docs/daily-js/concepts/tracks)
  </Card>
</CardGroup>
