Floor Access Control
Floor access control is used in Lecture Mode sessions where only the moderator can publish audio and video by default. Participants who want to speak must request the floor, and the moderator decides whether to grant or deny that request. This page covers all the methods and events needed to implement a full floor access workflow for both participants and moderators.
In Lecture Mode, the room has a single presenter (the moderator) and an audience (participants). The floor access workflow follows this flow:
- A participant calls
requestFloor()— this signals to the moderator that they want to speak. - The moderator receives a
floor-requestedevent. The moderator can view all pending requests viaroom.raisedHands. - The moderator calls
grantFloor(clientId)to allow the participant to publish, ordenyFloor(clientId)to decline. - If granted, the participant receives a
floor-grantedevent and can begin publishing audio/video. - When the participant is done, they call
finishFloor()to voluntarily give up the floor. - Alternatively, the moderator can call
releaseFloor(clientId)to take the floor back at any time.
Moderators can also invite a participant to the floor without waiting for a request — useful when the moderator wants to give a specific attendee a turn to speak.
roomMode: "lecture"). In Group Mode, all participants can publish freely. You can switch room modes at runtime using room.switchRoomMode().
The EnxRoom object exposes two read-only arrays that help the moderator track the state of floor requests at any time:
room.raisedHands
An array of clientId strings for all participants who have requested the floor and are waiting for a decision. Inspect this list to display a queue of pending speakers in your UI.
// Show pending floor requests in the moderator UI
console.log("Pending requests:", room.raisedHands);
// e.g. ["client-abc", "client-xyz"]
room.approvedHands
An array of clientId strings for participants who have been granted floor access and are currently allowed to publish.
// Show active speakers
console.log("Active speakers:", room.approvedHands);
Request Floor Access
A participant calls this method to raise their hand and signal to the moderator that they want to speak. The participant cannot publish until the moderator grants the floor.
room.requestFloor(function(response) {
if (response.result === 0) {
console.log("Floor request sent — waiting for moderator approval");
} else {
console.error("Request failed:", response.error);
}
});
When the request is received by the moderator, the moderator's room fires a floor-requested event:
// Moderator side
room.on("floor-requested", function(event) {
console.log("Floor requested by:", event.clientId, event.name);
// Show approval UI to moderator
});
| Error Code | Meaning |
|---|---|
| 4101 | Not applicable in current room mode (must be Lecture Mode) |
| 1702 | Floor already requested — cannot send a duplicate request |
Cancel a Floor Request
If a participant changes their mind after raising their hand, they can cancel the pending floor request before the moderator acts on it.
room.cancelFloor(function(response) {
if (response.result === 0) {
console.log("Floor request cancelled");
}
});
The moderator's room fires a floor-cancelled event:
// Moderator side
room.on("floor-cancelled", function(event) {
console.log("Floor request cancelled by:", event.clientId);
// Remove from pending list in UI
});
| Error Code | Meaning |
|---|---|
| 4101 | Not applicable in current room mode |
| 1707 | No active floor request to cancel |
Finish Floor (Participant Gives Up the Floor)
Once a participant is done speaking, they call finishFloor() to voluntarily return the floor. This allows the next participant in the queue to be granted access.
room.finishFloor(function(response) {
if (response.result === 0) {
console.log("Floor returned — you can no longer publish");
// Stop publishing the local stream
}
});
All moderators receive a floor-finished event:
// Moderator side
room.on("floor-finished", function(event) {
console.log("Participant finished floor:", event.clientId);
});
When a moderator invites a participant to the floor (using inviteToFloor()), the participant receives a floor-invited event and must respond — either accepting or rejecting the invitation.
Accept a Floor Invitation
The participant calls this method to accept the moderator's invitation and begin publishing.
// Participant receives invitation
room.on("floor-invited", function(event) {
console.log("You have been invited to speak");
// Accept the invitation
room.acceptInviteFloorRequest(function(response) {
if (response.result === 0) {
console.log("Floor invitation accepted — you can now publish");
}
});
});
All moderators receive a floor-accepted event confirming the participant accepted:
// Moderator side
room.on("floor-accepted", function(event) {
console.log("Participant accepted floor invite:", event.clientId);
});
Reject a Floor Invitation
The participant can decline the floor invitation if they don't want to speak.
room.rejectInviteFloor(function(response) {
if (response.result === 0) {
console.log("Floor invitation declined");
}
});
All moderators receive a floor-rejected event:
// Moderator side
room.on("floor-rejected", function(event) {
console.log("Participant rejected floor invite:", event.clientId);
});
Grant Floor Access
The moderator uses this method to approve a participant's floor request. Once granted, the participant can publish their audio and video into the session.
// Moderator grants floor to a specific participant
room.grantFloor(clientId, function(response) {
if (response.result === 0) {
console.log("Floor granted to:", clientId);
} else {
console.error("Grant failed:", response.error);
}
});
The participant who was granted access receives a floor-granted event:
// Participant side
room.on("floor-granted", function(event) {
console.log("Floor granted — you can now publish");
// Call room.publish() to start streaming
});
| Error Code | Meaning |
|---|---|
| 4101 | Not applicable in current room mode |
| 1711 | Specified client not found in session |
| 1703 | Participant has not requested the floor |
| 1173 | Maximum number of floor holders already active |
| 1131 | Session is locked — no new publishers allowed |
| 1710 | Participant already has floor access |
Deny a Floor Request
The moderator calls this to decline a participant's request to speak. The participant remains in the audience.
room.denyFloor(clientId, function(response) {
if (response.result === 0) {
console.log("Floor request denied for:", clientId);
}
});
The participant receives a floor-denied event:
// Participant side
room.on("floor-denied", function(event) {
console.log("Floor request was denied by the moderator");
});
| Error Code | Meaning |
|---|---|
| 4101 | Not applicable in current room mode |
| 1711 | Specified client not found in session |
Release a Floor (Take Back Access)
The moderator can revoke floor access from a participant at any time, even if the participant has not finished speaking. This is useful for moderating sessions with a strict time allocation per speaker.
room.releaseFloor(clientId, function(response) {
if (response.result === 0) {
console.log("Floor released from:", clientId);
}
});
The participant whose floor was released receives a release-floor event:
// Participant side
room.on("release-floor", function(event) {
console.log("The moderator has ended your floor access");
// Stop publishing
});
Invite a Participant to the Floor
Instead of waiting for a participant to raise their hand, the moderator can proactively invite any participant to speak. The participant will receive the invitation and must explicitly accept or reject it.
room.inviteToFloor(clientId, function(response) {
if (response.result === 0) {
console.log("Floor invitation sent to:", clientId);
} else {
console.error("Invitation failed:", response.error);
}
});
The invited participant receives a floor-invited event:
// Participant side
room.on("floor-invited", function(event) {
console.log("You are invited to speak by the moderator");
// Show Accept / Decline UI
});
| Error Code | Meaning |
|---|---|
| 5086 | Specified client not found |
| 5097 | Client already has floor access |
| 5006 | Maximum floor holders already active |
| 5045 | An invitation is already pending for this client |
Cancel a Floor Invitation
If the moderator changes their mind before the participant responds, they can cancel the pending invitation.
room.cancelFloorInvite(clientId, function(response) {
if (response.result === 0) {
console.log("Floor invitation cancelled for:", clientId);
}
});
The participant receives a floor-invite-cancelled event:
// Participant side
room.on("floor-invite-cancelled", function(event) {
console.log("The floor invitation was cancelled by the moderator");
// Hide the Accept / Decline UI
});
The following events are emitted on the EnxRoom object during floor access workflows. Some events fire on the participant's room, others on the moderator's room, and some on both.
| Event | Received By | Description |
|---|---|---|
floor-requested |
Moderator | A participant has requested the floor. event.clientId identifies who. |
floor-cancelled |
Moderator | A participant has cancelled their floor request before receiving a decision. |
floor-granted |
Participant | The moderator has granted floor access — participant may now publish. |
floor-denied |
Participant | The moderator has denied the floor request. |
floor-finished |
Moderator | The participant voluntarily returned the floor. |
release-floor |
Participant | The moderator has revoked the participant's floor access. |
floor-invited |
Participant | The moderator has invited this participant to speak. |
floor-invite-cancelled |
Participant | The moderator cancelled a pending floor invitation. |
floor-accepted |
Moderator | An invited participant accepted the floor invitation. |
floor-rejected |
Moderator | An invited participant declined the floor invitation. |
Here is a combined example showing how a moderator and a participant would wire up the full floor access flow together:
// ── PARTICIPANT SIDE ────────────────────────────────────────────────────────
// Step 1: Request the floor
document.getElementById("raise-hand-btn").addEventListener("click", function() {
room.requestFloor(function(response) {
if (response.result === 0) {
console.log("Hand raised — waiting for moderator approval");
document.getElementById("status").innerText = "Waiting for approval...";
}
});
});
// Step 2: Listen for floor grant or denial
room.on("floor-granted", function(event) {
console.log("Floor granted! Publishing now...");
room.publish(localStream, {}, function(res, error) {
if (!error) console.log("Publishing started");
});
});
room.on("floor-denied", function(event) {
console.log("Floor request was denied");
document.getElementById("status").innerText = "Request denied";
});
// Step 3: Finish speaking
document.getElementById("done-btn").addEventListener("click", function() {
room.finishFloor(function(response) {
if (response.result === 0) {
console.log("Floor returned");
room.unpublish(localStream, function() {
console.log("Unpublished");
});
}
});
});
// Listen for moderator revoking the floor
room.on("release-floor", function(event) {
console.log("Floor revoked by moderator");
room.unpublish(localStream, function() {
console.log("Unpublished");
});
});
// ── MODERATOR SIDE ──────────────────────────────────────────────────────────
// Listen for incoming requests
room.on("floor-requested", function(event) {
console.log("Floor requested by:", event.clientId, event.name);
// Add to moderator's queue UI
addToQueue(event.clientId, event.name);
});
// Grant the floor
function grantSpeaker(clientId) {
room.grantFloor(clientId, function(response) {
if (response.result === 0) {
console.log("Granted:", clientId);
}
});
}
// Deny the floor
function denySpeaker(clientId) {
room.denyFloor(clientId, function(response) {
if (response.result === 0) {
console.log("Denied:", clientId);
}
});
}
// Take back the floor mid-speech
function revokeFloor(clientId) {
room.releaseFloor(clientId, function(response) {
if (response.result === 0) {
console.log("Floor released from:", clientId);
}
});
}
// Proactively invite someone to speak
function inviteSpeaker(clientId) {
room.inviteToFloor(clientId, function(response) {
if (response.result === 0) {
console.log("Invitation sent to:", clientId);
}
});
}