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

# Participants

> Understand the DailyParticipant object, how to list participants, and how to update them.

Every person (or bot) in a Daily room is represented as a `DailyParticipant` object. Daily keeps these objects up to date as media states, permissions, and network conditions change.

## The DailyParticipant Object

```typescript theme={null}
interface DailyParticipant {
  // Identity
  user_id: string;
  user_name: string;
  userData?: unknown;
  session_id: string;
  joined_at?: Date;
  local: boolean;
  owner: boolean;
  record: boolean;
  participantType?: DailyParticipantTypeValues;

  // Media tracks
  tracks: DailyParticipantTracks;

  // Network
  networkQualityState?: 'good' | 'warning' | 'bad' | 'unknown';

  // Permissions
  permissions: DailyParticipantPermissions;
  will_eject_at: Date;
}
```

See the [`DailyParticipant` type reference](/reference/daily-js/types/daily-participant) for the full field listing with types and descriptions.

## Listing Participants

### `participants()`

Returns a `DailyParticipantsObject` — a flat map of all participants currently visible to the local client.

```typescript theme={null}
interface DailyParticipantsObject {
  local: DailyParticipant;
  [sessionId: string]: DailyParticipant;
}
```

```typescript theme={null}
const { local, ...remotes } = call.participants();

console.log('My session:', local.session_id);

for (const [sessionId, participant] of Object.entries(remotes)) {
  console.log(participant.user_name, sessionId);
}
```

<Note>
  The `local` key is always present. Remote participants are keyed by their `session_id`.
</Note>

### `participantCounts()`

Returns aggregate counts without full participant data — more accurate than counting participants manually as participant data can come in delayed on join, whereas the counts are provided immediately. Also, hidden participants are not included in `participants()`, but are included in the counts returned by `participantCounts()`.

```typescript theme={null}
interface DailyParticipantCounts {
  present: number; // participants visible via participants()
  hidden: number;  // participants with hasPresence: false
}
```

```typescript theme={null}
const { present, hidden } = call.participantCounts();
console.log(`${present} visible, ${hidden} hidden`);
```

Listen for [`participant-counts-updated`](/reference/daily-js/events/participant-events#participant-counts-updated) to keep this value current.

### `waitingParticipants()`

<Badge color="gray">call object mode only</Badge>

Returns a map of participants waiting in a knock-to-join lobby, keyed by their `id`. Use [`updateWaitingParticipant()`](/reference/daily-js/instance-methods/update-waiting-participant) to admit or deny them.

```typescript theme={null}
const waiting = call.waitingParticipants();
console.log(`${Object.keys(waiting).length} participants waiting`);
```

See the [Waiting room guide](/docs/daily-js/guides/waiting-room) for a full walkthrough including room configuration, event handling, and a complete example.

## Updating Participants

### `updateParticipant(sessionId, options)`

Send a change request for a single participant. Returns the `DailyCall` instance for chaining.

```typescript theme={null}
call.updateParticipant(
  sessionId: string,
  options: DailyParticipantUpdateOptions
): DailyCall
```

<ParamField body="setAudio" type="boolean">
  Mute (`false`) or unmute (`true`) for the participant's microphone. Only admins can mute remote participants' audio. Note: while you can use this to mute/unmute the local participant, `setLocalAudio()` is the preferred method.
</ParamField>

<ParamField body="setVideo" type="boolean">
  Mute (`false`) or unmute (`true`) for the participant's camera. Only admins can mute remote participants' video. Note: while you can use this to mute/unmute the local participant, `setLocalVideo()` is the preferred method.
</ParamField>

<ParamField body="setScreenShare" type="false">
  Stop a remote participant's screen share. Can only be set to `false` (you can only stop, not start, a remote screen share). Requires admin permissions.
</ParamField>

<ParamField body="setSubscribedTracks" type="DailyTrackSubscriptionOptions">
  Control which of this participant's tracks the local client receives. See [Track Subscription](/docs/daily-js/concepts/tracks#track-subscription).
</ParamField>

<ParamField body="eject" type="true">
  Remove the participant from the call. Can only be set to `true`. Requires admin permissions.
</ParamField>

<ParamField body="updatePermissions" type="DailyParticipantPermissionsUpdate">
  Change the participant's `hasPresence`, `canSend`, `canReceive`, or `canAdmin` permissions. Requires admin permissions.
</ParamField>

<ParamField body="styles" type="DailyParticipantCss">
  <Badge color="gray">Prebuilt only</Badge>
  Apply CSS overrides to this participant's video tiles.
</ParamField>

### `updateParticipants(updates)`

Apply updates to multiple participants in a single call. This method takes the same options as `updateParticipant()` but in a map keyed by `session_id`. You can use `'*'` as a wildcard key to apply the same update to all *remote* participants.

```typescript theme={null}
call.updateParticipants({
  [sessionId: string]: DailyParticipantUpdateOptions;
}): DailyCall
```

## Code Examples

<CodeGroup>
  ```typescript Mute a remote participant theme={null}
  // Mute a specific participant's mic (owner only)
  call.updateParticipant(sessionId, { setAudio: false });
  ```

  ```typescript Eject a participant theme={null}
  // Remove a participant from the call (owner only)
  call.updateParticipant(sessionId, { eject: true });
  ```

  ```typescript Mute all remotes theme={null}
  // Mute every remote participant's mic at once (owner only)
  call.updateParticipants('*', { setAudio: false });
  ```

  ```typescript Restrict send permissions theme={null}
  // Prevent a participant from sending video or screen share
  call.updateParticipant(sessionId, {
    updatePermissions: {
      canSend: ['audio'], // audio only
    },
  });
  ```
</CodeGroup>

## Participant Events

React to participant state changes by subscribing to these events:

| Event                         | Fires when                                                       |
| ----------------------------- | ---------------------------------------------------------------- |
| `participant-joined`          | A remote participant enters the room                             |
| `participant-updated`         | Any field on a participant changes (mute, network quality, etc.) |
| `participant-left`            | A remote participant leaves or is ejected                        |
| `participant-counts-updated`  | The aggregate counts returned by `participantCounts()` change    |
| `waiting-participant-added`   | A participant enters the knock-to-join lobby                     |
| `waiting-participant-updated` | A waiting participant's state changes                            |
| `waiting-participant-removed` | A waiting participant leaves the lobby or is admitted            |

```typescript theme={null}
call.on('participant-joined', ({ participant }) => {
  console.log(`${participant.user_name} joined`);
});

call.on('participant-updated', ({ participant }) => {
  const audioState = participant.tracks.audio.state;
  console.log(`${participant.user_name} audio is ${audioState}`);
});

call.on('participant-left', ({ participant, reason }) => {
  console.log(`${participant.user_name} left`, reason ?? 'normally');
});
```
