Skip to content
Cloudflare Docs

Remote Participant

This guide explains how to access participant data, display videos, handle events, and manage participant permissions in your RealtimeKit meetings.

Participant Maps

The data regarding all meeting participants is stored under meeting.participants. These do not include the local user.

The meeting.participants object contains the following maps:

  • joined - All participants currently in the meeting (excluding the local user)
  • waitlisted - All participants waiting to join the meeting
  • active - All participants whose media is subscribed to (participants that should be displayed on screen)
  • pinned - All pinned participants in the meeting

If you're building a video/audio grid, you'd use the active map. To display a list of all participants, use the joined map.

Each participant in these maps is of type RTKParticipant.

Access Participant Maps

JavaScript
// Get all joined participants
const joinedParticipants = meeting.participants.joined;
// Get active participants (those on screen)
const activeParticipants = meeting.participants.active;
// Get pinned participants
const pinnedParticipants = meeting.participants.pinned;
// Get waitlisted participants
const waitlistedParticipants = meeting.participants.waitlisted;

Listen to Participant Map Events

Each participant map emits participantJoined and participantLeft events:

JavaScript
// Listen for when a participant gets pinned
meeting.participants.pinned.on("participantJoined", (participant) => {
console.log(`Participant ${participant.name} got pinned`);
});
// Listen for when a participant gets unpinned
meeting.participants.pinned.on("participantLeft", (participant) => {
console.log(`Participant ${participant.name} got unpinned`);
});

Participant Map Properties

JavaScript
// Number of participants joined in the meeting
console.log(meeting.participants.count);
// Number of pages available in paginated mode
console.log(meeting.participants.pageCount);
// Maximum number of participants in active state
console.log(meeting.participants.maxActiveParticipantsCount);
// ParticipantId of the last participant who spoke
console.log(meeting.participants.lastActiveSpeaker);

Participant View Modes

The view mode indicates whether participants are populated in ACTIVE_GRID mode or PAGINATED mode.

  • ACTIVE_GRID mode - Participants are automatically replaced in meeting.participants.active based on who is speaking or who has their video turned on
  • PAGINATED mode - Participants in meeting.participants.active are fixed. Use setPage() to change the active participants

Set View Mode

JavaScript
// Set the view mode to paginated
await meeting.participants.setViewMode("PAGINATED");
// Set the view mode to active grid
await meeting.participants.setViewMode("ACTIVE_GRID");

Set Page in Paginated Mode

JavaScript
// Switch to second page
await meeting.participants.setPage(2);

Waiting Room Methods

Accept Waiting Room Request

JavaScript
await meeting.participants.acceptWaitingRoomRequest(participantId);

Reject Waiting Room Request

JavaScript
await meeting.participants.rejectWaitingRoomRequest(participantId);

Participant Object

The participant object contains all information related to a particular participant, including their video/audio/screenshare streams, name, and state variables.

Participant Properties

Media Properties:

  • videoEnabled - Set to true if the participant's camera is on
  • audioEnabled - Set to true if the participant is unmuted
  • screenShareEnabled - Set to true if the participant is sharing their screen
  • videoTrack - The video track of the participant
  • audioTrack - The audio track of the participant
  • screenShareTracks - The video and audio tracks of the participant's screen share

Metadata Properties:

  • id - The participantId of the participant (aka peerId)
  • userId - The userId of the participant
  • name - The participant's name
  • picture - The participant's picture (if any)
  • customParticipantId - An arbitrary ID that can be set to identify the participant
  • isPinned - Set to true if the participant is pinned
  • presetName - Name of the preset associated with the participant

Access Participant Object

JavaScript
const participant = meeting.participants.joined.get(participantId);
// Access participant properties
console.log(participant.name);
console.log(participant.videoEnabled);
console.log(participant.audioEnabled);

Listen to Participant Events

Each participant object is an event emitter:

JavaScript
meeting.participants.joined
.get(participantId)
.on("audioUpdate", ({ audioEnabled, audioTrack }) => {
console.log(
"The participant with id",
participantId,
"has toggled their mic to",
audioEnabled,
);
});

Alternatively, listen on the participant map for all participants:

JavaScript
meeting.participants.joined.on(
"audioUpdate",
(participant, { audioEnabled, audioTrack }) => {
console.log(
"The participant with id",
participant.id,
"has toggled their mic to",
audioEnabled,
);
},
);

Host Control Methods

If you have the relevant permissions, you can control participant media:

JavaScript
const participant = meeting.participants.joined.get(participantId);
// Disable a participant's video stream
participant.disableVideo();
// Disable a participant's audio stream
participant.disableAudio();
// Kick a participant from the meeting
participant.kick();

Pin and Unpin Participants

JavaScript
const participant = meeting.participants.joined.get(participantId);
// Pin a participant
await participant.pin();
// Unpin a participant
await participant.unpin();

Display Participant Videos

Register Video Element

To play a participant's video track on a <video> element:

<video class="participant-video" id="participant-video"></video>
JavaScript
// Get the video element
const videoElement = document.getElementById("participant-video");
// Get the participant
const participant = meeting.participants.joined.get(participantId);
// Register the video element
participant.registerVideoElement(videoElement);

For local user preview (video not sent to other users):

JavaScript
meeting.self.registerVideoElement(videoElement, true);

Deregister Video Element

Clean up when the video element is no longer needed:

JavaScript
participant.deregisterVideoElement(videoElement);

Participant Events

View Mode Change

Triggered when the view mode changes:

JavaScript
meeting.participants.on(
"viewModeChanged",
({ viewMode, currentPage, pageCount }) => {
console.log("view mode changed", viewMode);
},
);

Page Change

Triggered when the page changes in paginated mode:

JavaScript
meeting.participants.on(
"pageChanged",
({ viewMode, currentPage, pageCount }) => {
console.log("page changed", currentPage);
},
);

Active Speaker

Triggered when a participant starts speaking:

JavaScript
meeting.participants.on("activeSpeaker", (participant) => {
console.log(`${participant.id} is currently speaking`);
});

Participant Joined

Triggered when any participant joins the meeting:

JavaScript
meeting.participants.joined.on("participantJoined", (participant) => {
console.log(`A participant with id "${participant.id}" has joined`);
});

Participant Left

Triggered when any participant leaves the meeting:

JavaScript
meeting.participants.joined.on("participantLeft", (participant) => {
console.log(`A participant with id "${participant.id}" has left the meeting`);
});

Participant Pinned

Triggered when a participant is pinned:

JavaScript
meeting.participants.joined.on("pinned", (participant) => {
console.log(`Participant with id "${participant.id}" was pinned`);
});

Participant Unpinned

Triggered when a participant is unpinned:

JavaScript
meeting.participants.joined.on("unpinned", (participant) => {
console.log(`Participant with id "${participant.id}" was unpinned`);
});

Video Update

Triggered when any participant starts/stops video:

JavaScript
meeting.participants.joined.on("videoUpdate", (participant) => {
console.log(
`A participant with id "${participant.id}" updated their video track`,
);
if (participant.videoEnabled) {
// Use participant.videoTrack
} else {
// Handle stop video
}
});

Audio Update

Triggered when any participant starts/stops audio:

JavaScript
meeting.participants.joined.on("audioUpdate", (participant) => {
console.log(
`A participant with id "${participant.id}" updated their audio track`,
);
if (participant.audioEnabled) {
// Use participant.audioTrack
} else {
// Handle stop audio
}
});

Screen Share Update

Triggered when any participant starts/stops screen share:

JavaScript
meeting.participants.joined.on("screenShareUpdate", (participant) => {
console.log(
`A participant with id "${participant.id}" updated their screen share`,
);
if (participant.screenShareEnabled) {
// Use participant.screenShareTracks
} else {
// Handle stop screen share
}
});

Network Quality Score

Monitor participant network quality:

JavaScript
meeting.participants.joined.on(
"mediaScoreUpdate",
({ participantId, kind, isScreenshare, score, scoreStats }) => {
if (kind === "video") {
console.log(
`Participant ${participantId}'s ${isScreenshare ? "screenshare" : "video"} quality score is`,
score,
);
}
if (kind === "audio") {
console.log(
`Participant ${participantId}'s audio quality score is`,
score,
);
}
if (score < 5) {
console.log(`Participant ${participantId}'s media quality is poor`);
}
},
);

Picture-in-Picture

Picture-in-Picture API allows you to render meeting.participants.active participant's video as a floating tile outside of the current webpage's context.

Check if Supported

JavaScript
const isSupported = meeting.participants.pip.isSupported();

Enable Picture-in-Picture

JavaScript
await meeting.participants.pip.enable();

Disable Picture-in-Picture

JavaScript
await meeting.participants.pip.disable();

Update Participant Permissions

Permissions for a participant are defined by the preset, but can be updated during a meeting by calling updatePermissions for remote participants.

Find Target Participants

JavaScript
const participantIds = meeting.participants.joined
.toArray()
.filter((e) => e.name.startsWith("John"))
.map((p) => p.id);

Update Permissions

JavaScript
// Allow file upload permissions in public chat
const newPermissions = {
chat: {
public: {
files: true,
},
},
};
meeting.participants.updatePermissions(participantIds, newPermissions);

Available Permission Fields

TypeScript
interface UpdatedPermissions {
polls?: {
canCreate?: boolean;
canVote?: boolean;
};
plugins?: {
canClose?: boolean;
canStart?: boolean;
};
chat?: {
public?: {
canSend?: boolean;
text?: boolean;
files?: boolean;
};
private?: {
canSend?: boolean;
text?: boolean;
files?: boolean;
};
};
}