Group Video Conferencing System Development
Group video conferencing—3–50+ participants, host microphone/camera management, hand raise, breakout rooms, chat, screen sharing, recording. Requires well-thought-out architecture on both WebRTC infrastructure and UI.
Infrastructure Selection by Scale
| Participants | Recommended Solution |
|---|---|
| 2–10 | LiveKit (SFU), Daily.co |
| 10–50 | LiveKit with Simulcast, 100ms |
| 50–1000 | LiveKit Broadcast, Agora, Amazon Chime |
| 1000+ | HLS streaming, not WebRTC |
LiveKit—Recommended Foundation
npm install livekit-server-sdk # server
npm install @livekit/components-react livekit-client # client
Creating Conference:
import { RoomServiceClient, AccessToken, RoomOptions } from 'livekit-server-sdk';
const svc = new RoomServiceClient(
process.env.LIVEKIT_URL!,
process.env.LIVEKIT_API_KEY!,
process.env.LIVEKIT_API_SECRET!
);
async function createConference(conferenceId: string, options: {
maxParticipants?: number;
enableRecording?: boolean;
}): Promise<void> {
await svc.createRoom({
name: `conf-${conferenceId}`,
maxParticipants: options.maxParticipants ?? 50,
emptyTimeout: 300, // 5 min before closing empty room
metadata: JSON.stringify({ conferenceId, createdAt: new Date().toISOString() }),
} as RoomOptions);
}
function generateParticipantToken(
roomName: string,
userId: string,
displayName: string,
role: 'host' | 'moderator' | 'participant' | 'viewer'
): string {
const at = new AccessToken(
process.env.LIVEKIT_API_KEY!,
process.env.LIVEKIT_API_SECRET!,
{ identity: userId, name: displayName, ttl: 4 * 60 * 60 }
);
at.addGrant({
roomJoin: true,
room: roomName,
canPublish: role !== 'viewer',
canSubscribe: true,
canPublishData: true,
roomAdmin: role === 'host',
hidden: false,
});
return at.toJwt();
}
Participant Management from Server:
// Mute specific participant
app.post('/api/conferences/:roomName/mute/:participantId', authenticate, async (req, res) => {
const conference = await db.conferences.findByRoomName(req.params.roomName);
if (conference.hostId !== req.user.id) return res.status(403).end();
await svc.mutePublishedTrack(
req.params.roomName,
req.params.participantId,
'microphone-track',
true
);
res.json({ ok: true });
});
React Conference Component
import {
LiveKitRoom,
VideoConference,
useLocalParticipant,
useParticipants,
Chat,
} from '@livekit/components-react';
function GroupConference({ token, roomName }: { token: string; roomName: string }) {
return (
<LiveKitRoom
token={token}
serverUrl={process.env.NEXT_PUBLIC_LIVEKIT_URL}
video={true}
audio={true}
>
<VideoConference />
<Chat />
</LiveKitRoom>
);
}
Breakout Rooms
async function createBreakoutRooms(mainRoomName: string, groups: string[][]) {
const breakoutRooms = await Promise.all(
groups.map((group, i) =>
createConference(`${mainRoomName}-breakout-${i}`, { maxParticipants: group.length + 2 })
)
);
for (let i = 0; i < groups.length; i++) {
for (const participantId of groups[i]) {
const token = generateParticipantToken(
`conf-${mainRoomName}-breakout-${i}`,
participantId,
'',
'participant'
);
await svc.sendData(
`conf-${mainRoomName}`,
Buffer.from(JSON.stringify({ type: 'breakout_invite', token, roomIndex: i })),
[participantId]
);
}
}
}
Timeline
Basic group conferencing with LiveKit + React components—1 week. With breakout rooms, hand raise, recording, and participant management—2–3 weeks.







