INTEGRITY Documentation

Remote Participants

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

The participant object contains all information related to a particular participants, including information about the grid and each participants media streams, name, and state variables. It is accessible via meeting.participants.

Properties

Metadata properties

Metadata properties

Metadata properties

Media properties

Media properties

Access participant properties

// 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);

Use the useRealtimeKitSelector hook to access properties:

// Number of participants joined in the meeting
const participantCount = useRealtimeKitSelector((m) => m.participants.count);

// Number of pages available in paginated mode
const pageCount = useRealtimeKitSelector((m) => m.participants.pageCount);

// Maximum number of participants in active state
const maxActiveCount = useRealtimeKitSelector(
	(m) => m.participants.maxActiveParticipantsCount,
);

// ParticipantId of the last participant who spoke
const lastActiveSpeaker = useRealtimeKitSelector(
	(m) => m.participants.lastActiveSpeaker,
);
// Number of participants joined in the meeting
val participantCount = meeting.participants.joined.size

// Access pagination properties
val maxNumberOnScreen = meeting.participants.maxNumberOnScreen
val currentPageNumber = meeting.participants.currentPageNumber
val pageCount = meeting.participants.pageCount
val canGoNextPage = meeting.participants.canGoNextPage
val canGoPreviousPage = meeting.participants.canGoPreviousPage
// Number of participants joined in the meeting
let participantCount = meeting.participants.joined.count

// Access pagination properties
let maxNumberOnScreen = meeting.participants.maxNumberOnScreen
let currentPageNumber = meeting.participants.currentPageNumber
let pageCount = meeting.participants.pageCount
let canGoNextPage = meeting.participants.canGoNextPage
let canGoPreviousPage = meeting.participants.canGoPreviousPage

Use the useRealtimeKitSelector hook to access properties:

// Number of participants joined in the meeting
const participantCount = useRealtimeKitSelector((m) => m.participants.count);

// Number of pages available in paginated mode
const pageCount = useRealtimeKitSelector((m) => m.participants.pageCount);

// Maximum number of participants in active state
const maxActiveCount = useRealtimeKitSelector(
	(m) => m.participants.maxActiveParticipantsCount,
);

// ParticipantId of the last participant who spoke
const lastActiveSpeaker = useRealtimeKitSelector(
	(m) => m.participants.lastActiveSpeaker,
);

Access participant object

You can fetch a participant from the participant maps.

const participant = meeting.participants.joined.get(participantId);

// Access participant properties
console.log(participant.name);
console.log(participant.videoEnabled);
console.log(participant.audioEnabled);
// Get a specific participant
const participant = useRealtimeKitSelector((m) =>
	m.participants.joined.get(participantId),
);

// Access participant properties
const participantName = participant?.name;
const isVideoEnabled = participant?.videoEnabled;
const isAudioEnabled = participant?.audioEnabled;
// Find a participant by peer ID
val participant = meeting.participants.joined.firstOrNull { it.id == participantId }

// Access participant properties
participant?.let {
	println("Participant: ${it.name}")
	println("Video: ${it.videoEnabled}")
	println("Audio: ${it.audioEnabled}")
}
// Find a participant by peer ID
if let participant = meeting.participants.joined.first(where: { $0.id == participantId }) {
	// Access participant properties
	print("Participant: \(participant.name)")
	print("Video: \(participant.videoEnabled)")
	print("Audio: \(participant.audioEnabled)")
}
// Get a specific participant
const participant = useRealtimeKitSelector((m) =>
	m.participants.joined.get(participantId),
);

// Access participant properties
const participantName = participant?.name;
const isVideoEnabled = participant?.videoEnabled;
const isAudioEnabled = participant?.audioEnabled;

Participant Maps

All participants are stored under meeting.participants. These do not include the local user.

The meeting.participants object contains the following maps:

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

Each participant in these maps is of type RTKParticipant.

All participants are stored under meeting.participants. These do not include the local user.

The meeting.participants object contains the following lists:

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

// 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;

Use the useRealtimeKitSelector hook to access participant maps:

import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react";

// Get all joined participants
const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined);

// Get active participants (those on screen)
const activeParticipants = useRealtimeKitSelector((m) => m.participants.active);

// Get pinned participants
const pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned);

// Get waitlisted participants
const waitlistedParticipants = useRealtimeKitSelector(
	(m) => m.participants.waitlisted,
);
// Get all joined participants
val joinedParticipants: List<RtkRemoteParticipant> = meeting.participants.joined

// Get active participants (those on screen)
val activeParticipants: List<RtkRemoteParticipant> = meeting.participants.active

// Get pinned participants
val pinnedParticipants: List<RtkRemoteParticipant> = meeting.participants.pinned

// Get waitlisted participants
val waitlistedParticipants: List<RtkRemoteParticipant> = meeting.participants.waitlisted

// Get screen sharing participants
val screenShareParticipants: List<RtkRemoteParticipant> = meeting.participants.screenShares
// Get all joined participants
let joinedParticipants: [RtkRemoteParticipant] = meeting.participants.joined

// Get active participants (those on screen)
let activeParticipants: [RtkRemoteParticipant] = meeting.participants.active

// Get pinned participants
let pinnedParticipants: [RtkRemoteParticipant] = meeting.participants.pinned

// Get waitlisted participants
let waitlistedParticipants: [RtkRemoteParticipant] = meeting.participants.waitlisted

// Get screen sharing participants
let screenShareParticipants: [RtkRemoteParticipant] = meeting.participants.screenShares

Use the useRealtimeKitSelector hook to access participant maps:

import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native";

// Get all joined participants
const joinedParticipants = useRealtimeKitSelector((m) => m.participants.joined);

// Get active participants (those on screen)
const activeParticipants = useRealtimeKitSelector((m) => m.participants.active);

// Get pinned participants
const pinnedParticipants = useRealtimeKitSelector((m) => m.participants.pinned);

// Get waitlisted participants
const waitlistedParticipants = useRealtimeKitSelector(
	(m) => m.participants.waitlisted,
);

View Modes

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

Set view mode

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

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

Use the useRealtimeKitClient hook to access the meeting object:

import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";

const [meeting] = useRealtimeKitClient();

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

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

Android SDK uses active grid mode by default on page 0. If you switch to the next page, it automatically switches to paginated mode.

iOS SDK uses active grid mode by default on page 0. If you switch to the next page, it automatically switches to paginated mode.

// 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

// Switch to second page
await meeting.participants.setPage(2);
import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";

const [meeting] = useRealtimeKitClient();

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

Monitor view mode

const viewMode = meeting.participants.viewMode;
const currentPage = meeting.participants.currentPage;
const viewMode = useRealtimeKitSelector((m) => m.participants.viewMode);
const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage);

Monitoring view mode is not available on this platform.

const viewMode = useRealtimeKitSelector((m) => m.participants.viewMode);
const currentPage = useRealtimeKitSelector((m) => m.participants.currentPage);

Host Controls

The participant object allows the host several controls. These can be selected while creating the host preset.

Media controls

With the correct permissions, the host can disable media for remote participants.

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();
import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";

const [meeting] = useRealtimeKitClient();
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();
val participant = meeting.participants.joined.firstOrNull { it.id == participantId }

participant?.let { pcpt ->
	// Disable a participant's video stream
	val videoError = pcpt.disableVideo()

	// Disable a participant's audio stream
	val audioError = pcpt.disableAudio()

	// Kick a participant from the meeting
	val kickError = pcpt.kick()
}
if let participant = meeting.participants.joined.first(where: { $0.id == participantId }) {
	// Disable a participant's video stream
	let videoError: HostError? = participant.disableVideo()

	// Disable a participant's audio stream
	let audioError: HostError? = participant.disableAudio()

	// Kick a participant from the meeting
	let kickError: HostError? = participant.kick()
}
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();

Waiting room controls

The waiting room allows the host to control which users can join your meeting and when. They can either choose to accept or reject the request.

You can also automate this flow so that users join the meeting automatically when the host joins the meeting, using presets.

Accept waiting room request

await meeting.participants.acceptWaitingRoomRequest(participantId);
import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";

const [meeting] = useRealtimeKitClient();

await meeting.participants.acceptWaitingRoomRequest(participantId);
meeting.participants.acceptWaitingRoomRequest(participantId)
meeting.participants.acceptWaitingRoomRequest(id: participantId)
await meeting.participants.acceptWaitingRoomRequest(participantId);

Reject waiting room request

await meeting.participants.rejectWaitingRoomRequest(participantId);
import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";

const [meeting] = useRealtimeKitClient();

await meeting.participants.rejectWaitingRoomRequest(participantId);
meeting.participants.rejectWaitingRoomRequest(participantId)
meeting.participants.rejectWaitingRoomRequest(participantId)
await meeting.participants.rejectWaitingRoomRequest(participantId);

Pin participants

The host can choose to pin or unpin participants to the grid.

const participant = meeting.participants.joined.get(participantId);

// Pin a participant
await participant.pin();

// Unpin a participant
await participant.unpin();
import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";

const [meeting] = useRealtimeKitClient();
const participant = meeting.participants.joined.get(participantId);

// Pin a participant
await participant.pin();

// Unpin a participant
await participant.unpin();
val participant = meeting.participants.joined.firstOrNull { it.id == participantId }

participant?.let { pcpt ->
	// Pin a participant
	val pinError = pcpt.pin()

	// Unpin a participant
	val unpinError = pcpt.unpin()
}
if let participant = meeting.participants.joined.first(where: { $0.id == participantId }) {
	// Pin a participant
	let pinError: HostError? = participant.pin()

	// Unpin a participant
	let unpinError: HostError? = participant.unpin()
}
const participant = meeting.participants.joined.get(participantId);

// Pin a participant
await participant.pin();

// Unpin a participant
await participant.unpin();

Update participant permissions

The host can modify the permissions for a participant. Permissions for a participant are defined by their preset.

Updating participant permissions is not available on this platform.

First, find the participant(s) you want to update.

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

Use the updatePermissions method to modify the permissions for the participant.

// Allow file upload permissions in public chat
const newPermissions = {
	chat: {
		public: {
			files: true,
		},
	},
};

meeting.participants.updatePermissions(participantIds, newPermissions);

The following permissions can be modified:

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;
		};
	};
}

Display participant videos

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

<video class="participant-video" id="participant-video"></video>
// 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):

meeting.self.registerVideoElement(videoElement, true);

Clean up when the video element is no longer needed:

participant.deregisterVideoElement(videoElement);

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

import { useRealtimeKitClient } from "@cloudflare/realtimekit-react";

const [meeting] = useRealtimeKitClient();

// 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);

// Clean up when the video element is no longer needed
participant.deregisterVideoElement(videoElement);

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

meeting.self.registerVideoElement(videoElement, true);

Call participant.getVideoView() which returns a View that renders the participant's video stream:

// Get video view of a given participant
val videoView = participant.getVideoView()

// Get screen share video view
val screenShareView = participant.getScreenShareVideoView()

Call participant.getVideoView() which returns a UIView that renders the participant's video stream:

// Get video view of a given participant
let videoView = participant.getVideoView()

// Get screen share video view
let screenShareView = participant.getScreenShareVideoView()

Use useRealtimeKitSelector to get the video track and render it with RTCView:

import { useRealtimeKitSelector } from "@cloudflare/realtimekit-react-native";
import { MediaStream, RTCView } from "@cloudflare/react-native-webrtc";

function VideoView() {
	const { videoTrack } = useRealtimeKitSelector((m) =>
		m.participants.active.toArray(),
	)[0];

	const stream = new MediaStream(undefined);
	stream.addTrack(videoTrack);

	return (
		<RTCView
			objectFit="cover"
			style={{ flex: 1 }}
			streamURL={stream.toURL()}
			mirror={true}
			zOrder={1}
		/>
	);
}