Audio Rooms

Build Clubhouse-style audio rooms with speaker and listener roles

Overview

Audio rooms are social audio spaces where participants join as either speakers or listeners. Similar to Clubhouse or Twitter Spaces, audio rooms enable large-scale voice discussions with structured participation. Speakers can broadcast audio to thousands of listeners, while listeners can raise their hand to request speaker access.

Speaker Role

Broadcast audio to all participants in the room

Listener Role

Listen to speakers without broadcasting

Raise Hand

Request to become a speaker dynamically

Common Use Cases

Social Audio Networking

Create social audio spaces where community members can discuss topics, network, and share ideas. Perfect for online communities, interest groups, or professional networking.

Town Hall Meetings

Host company-wide meetings where executives speak and employees listen. Use raise hand feature for Q&A sessions where employees can ask questions.

Expert AMAs (Ask Me Anything)

Bring experts into audio rooms where fans can listen and raise hands to ask questions. Moderators control who gets to speak to the expert.

Moderated Discussions

Host structured discussions with moderators controlling who can speak. Perfect for debates, panel discussions, or educational sessions.

How Audio Rooms Work

Audio rooms use a hierarchical role system to manage participation:

1. Role Hierarchy

Moderator
  • Full control over the room
  • Can promote/demote speakers and listeners
  • Can mute/unmute any participant
  • Can remove participants from the room
  • Can publish audio (also a speaker)
Speaker
  • Can broadcast audio to all participants
  • Can mute/unmute their own microphone
  • Visible in "on stage" area of UI
  • Limited to 10-20 concurrent speakers
Listener
  • Can only listen to speakers
  • Cannot broadcast audio
  • Can raise hand to request speaker access
  • Unlimited concurrent listeners

2. Raise Hand Workflow

When a listener wants to speak:

  1. Listener clicks "Raise Hand" button in the UI
  2. Request is sent to moderators who see a notification
  3. Moderator can accept or deny the request
  4. If accepted, listener is promoted to speaker role
  5. User's microphone is enabled and they can speak
  6. Moderator can demote them back to listener when done

3. Audio Distribution

Speaker audio is mixed on BaxCloud's servers and distributed to all listeners as a single low-latency stream. This means listeners use minimal bandwidth regardless of how many speakers are active, allowing rooms to scale to thousands of participants.

Step-by-Step Implementation

Build an audio room experience

1

Create an Audio Room

Initialize a room with host role

The room creator starts as a host (moderator) with full permissions:

1import { BaxCloudClient } from '@baxcloud/react-sdk';
2
3const createAudioRoom = async () => {
4  const client = new BaxCloudClient({
5    projectId: 'your-project-id',
6    apiKey: 'your-api-key',
7  });
8
9  await client.connect({
10    roomName: 'community-discussion',
11    liveType: 'audio_conference',
12    participant: {
13      userId: 'user-123',
14      name: 'Moderator Jane',
15      avatarUrl: 'https://example.com/avatar.jpg',
16      isHost: true,
17    },
18  });
19
20  // Enable microphone
21  await client.enableMicrophone();
22
23  console.log('Audio room created successfully');
24};
💡

Host Role

The host (moderator) is identified by isHost: true. Hosts can promote listeners to co-hosts (speakers) and manage the room.
2

Join as a Speaker

Join with speaking permissions

Users can join as co-hosts (speakers) if promoted by the host:

1import { BaxCloudClient, BaxCloudRoomController } from '@baxcloud/react-sdk';
2
3const joinAsSpeaker = async () => {
4  const client = new BaxCloudClient({
5    projectId: 'your-project-id',
6    apiKey: 'your-api-key',
7  });
8
9  await client.connect({
10    roomName: 'community-discussion',
11    liveType: 'audio_conference',
12    participant: {
13      userId: 'user-456',
14      name: 'Speaker John',
15      avatarUrl: 'https://example.com/avatar.jpg',
16      isHost: false,
17    },
18  });
19
20  // Enable microphone (after being promoted to co-host)
21  await client.enableMicrophone();
22
23  const controller = BaxCloudRoomController.instance;
24  const participants = controller.getParticipants();
25  console.log('Joined room with', participants.length, 'participants');
26};
3

Join as a Listener

Join in listen-only mode

Most users join as listeners who can only receive audio:

1import { BaxCloudClient, BaxCloudRoomController } from '@baxcloud/react-sdk';
2
3const joinAsListener = async () => {
4  const client = new BaxCloudClient({
5    projectId: 'your-project-id',
6    apiKey: 'your-api-key',
7  });
8
9  await client.connect({
10    roomName: 'community-discussion',
11    liveType: 'audio_conference',
12    participant: {
13      userId: 'user-789',
14      name: 'Listener Alice',
15      avatarUrl: 'https://example.com/avatar.jpg',
16      isHost: false,
17    },
18  });
19
20  const controller = BaxCloudRoomController.instance;
21  const participants = controller.getParticipants();
22  console.log('Joined as listener. Room has', participants.length, 'participants');
23};
💡

Listener Experience

Listeners cannot publish audio, which saves bandwidth and allows rooms to scale to thousands of participants. They receive a mixed audio stream containing all active speakers.
4

Implement Raise Hand

Allow listeners to request speaker access

Add raise hand functionality for listeners:

1import { BaxCloudRoomController } from '@baxcloud/react-sdk';
2
3const controller = BaxCloudRoomController.instance;
4
5// Listener raises hand
6const raiseHand = async () => {
7  try {
8    await controller.raiseHand();
9    console.log('Hand raised, waiting for moderator approval');
10  } catch (error) {
11    console.error('Failed to raise hand:', error);
12  }
13};
14
15// Lower hand (cancel request)
16const lowerHand = async () => {
17  try {
18    await controller.lowerHand();
19    console.log('Hand lowered');
20  } catch (error) {
21    console.error('Failed to lower hand:', error);
22  }
23};
5

Handle Raise Hand Requests

Hosts approve or deny speaker requests

Hosts receive and manage raise hand requests:

1import { BaxCloudRoomController } from '@baxcloud/react-sdk';
2
3const controller = BaxCloudRoomController.instance;
4
5// Host listens for raise hand events
6controller.onHandRaised((user) => {
7  console.log(`${user.name} raised their hand`);
8  
9  // Show notification to host
10  // Show UI to accept/deny
11});
12
13// Accept and promote to co-host (speaker)
14const acceptSpeaker = async (userId: string) => {
15  try {
16    await controller.promoteToCoHost(userId);
17    console.log('Promoted to co-host');
18  } catch (error) {
19    console.error('Failed to promote:', error);
20  }
21};
22
23// Lower hand (deny request)
24const denySpeaker = async (userId: string) => {
25  try {
26    await controller.lowerHand(userId);
27    console.log('Lowered hand');
28  } catch (error) {
29    console.error('Failed to lower hand:', error);
30  }
31};
32
33// Get all raised hands
34const raisedHands = controller.getRaisedHands();
⚠️

Host Permissions

Only hosts (isHost: true) can promote listeners to co-hosts and manage raise hand requests.
6

Implement Moderation Features

Manage participants and co-hosts

Hosts have controls to manage the room and participants:

1import { BaxCloudRoomController } from '@baxcloud/react-sdk';
2
3const controller = BaxCloudRoomController.instance;
4
5// Get all participants
6const participants = controller.getParticipants();
7
8// Get co-hosts (speakers)
9const coHosts = controller.getCoHosts();
10
11// Demote co-host back to listener
12const demoteCoHost = async (userId: string) => {
13  try {
14    await controller.demoteFromCoHost(userId);
15    console.log('Demoted from co-host');
16  } catch (error) {
17    console.error('Failed to demote:', error);
18  }
19};
20
21// Get all raised hands
22const raisedHands = controller.getRaisedHands();
23
24// Monitor participants
25const updateParticipantList = () => {
26  const allParticipants = controller.getParticipants();
27  const hosts = allParticipants.filter(p => p.isHost);
28  const speakers = allParticipants.filter(p => coHosts.includes(p.userId));
29  const listeners = allParticipants.filter(p => !p.isHost && !coHosts.includes(p.userId));
30  
31  console.log('Hosts:', hosts.length);
32  console.log('Speakers:', speakers.length);
33  console.log('Listeners:', listeners.length);
34};
7

Build the UI Layout

Create speaker and listener sections

Design a clear UI that separates speakers from listeners:

1import { BaxCloudRoomController } from '@baxcloud/react-sdk';
2import { useState, useEffect } from 'react';
3
4function AudioRoomUI() {
5  const controller = BaxCloudRoomController.instance;
6  const [participants, setParticipants] = useState([]);
7  const [raisedHands, setRaisedHands] = useState([]);
8
9  useEffect(() => {
10    const updateUI = () => {
11      const allParticipants = controller.getParticipants();
12      const coHosts = controller.getCoHosts();
13      setParticipants(allParticipants);
14      setRaisedHands(controller.getRaisedHands());
15    };
16
17    updateUI();
18    const unsubscribeJoined = controller.onUserJoined(() => updateUI());
19    const unsubscribeLeft = controller.onUserLeft(() => updateUI());
20    const unsubscribeHandRaised = controller.onHandRaised(() => updateUI());
21
22    return () => {
23      unsubscribeJoined();
24      unsubscribeLeft();
25      unsubscribeHandRaised();
26    };
27  }, []);
28
29  const hosts = participants.filter(p => p.isHost);
30  const speakers = participants.filter(p => controller.getCoHosts().includes(p.userId));
31  const listeners = participants.filter(p => !p.isHost && !controller.getCoHosts().includes(p.userId));
32  const isHost = controller.isHost();
33
34  return (
35    <div className="audio-room">
36      <div className="room-header">
37        <h1>Community Discussion</h1>
38        <div>
39          <span>{hosts.length + speakers.length} speakers</span>
40          <span>{listeners.length} listeners</span>
41        </div>
42      </div>
43
44      <div className="speakers-section">
45        <h2>On Stage</h2>
46        {[...hosts, ...speakers].map(speaker => (
47          <div key={speaker.userId} className="speaker-card">
48            <span>{speaker.name}</span>
49            </div>
50          ))}
51      </div>
52
53      {isHost && raisedHands.length > 0 && (
54        <div className="raised-hands-section">
55          <h3>Raised Hands ({raisedHands.length})</h3>
56        </div>
57      )}
58
59      <div className="listeners-section">
60        <h2>Audience ({listeners.length})</h2>
61          {listeners.slice(0, 20).map(listener => (
62          <span key={listener.userId}>{listener.name}</span>
63          ))}
64      </div>
65    </div>
66  );
67}

Best Practices

Clearly show role distinctions

Use visual indicators (badges, colors, sections) to make it obvious who can speak and who is listening. This helps users understand their capabilities.

Limit concurrent speakers

Keep active speakers to 5-10 maximum for best experience. Too many speakers creates chaos and makes discussions hard to follow.

Provide moderator notifications

Alert moderators with sound or visual notifications when hands are raised. This ensures timely responses and keeps the room engaging.

Auto-mute new speakers

When promoting listeners to speakers, start them muted and let them unmute when ready. This prevents accidental background noise.

Show speaking indicators

Highlight speakers when they're actively talking using audio level indicators. This helps listeners follow conversations.

Implement time limits

Consider time limits for speakers (e.g., 2 minutes) to keep discussions moving and give more people a chance to participate.

Troubleshooting

Listener Can't Hear Speakers

  • Check that device volume is turned up
  • Verify speakers are actually unmuted and speaking
  • Check browser console for audio playback errors
  • Ensure browser has permission to play audio

Promoted Speaker Can't Publish Audio

  • Verify microphone permissions are granted
  • Check that role update event was received
  • Ensure unmuteMicrophone() is called after promotion
  • Verify room hasn't reached speaker limit

Raise Hand Not Working

  • Ensure user has 'listener' role (speakers can't raise hands)
  • Check that raiseHand() method was called successfully
  • Verify moderator is listening to 'handRaised' events
  • Check network connection for event delivery

Echo in Audio Room

  • Ensure all speakers use headphones
  • Enable echo cancellation in audio config
  • Check that users don't have multiple tabs open
  • Verify no speakers are in the same physical room