Fluxer’s voice system is built on LiveKit, a scalable WebRTC infrastructure that provides real-time audio, video, and screen sharing capabilities.
Architecture Overview
The voice system consists of:
Client Request
User requests to join a voice channel
Server Selection
Fluxer selects optimal LiveKit server based on region and availability
Token Generation
Server generates a LiveKit token with appropriate permissions
WebRTC Connection
Client connects to LiveKit server using token
Media Streaming
Real-time audio/video streaming via WebRTC
Voice Regions
Fluxer supports multiple voice regions for optimal latency:
Region Selection
- Explicit Region - User/channel has a preferred region set
- Automatic Region - System selects based on latency and availability
- Fallback Region - Alternative region if preferred is unavailable
// Region preference resolution
const regionPreference = resolveVoiceRegionPreference({
preferredRegionId: 'us-west',
accessibleRegions: availableRegions,
availableRegions: allRegions,
defaultRegionId: 'us-east'
});
Region Features
Unique region identifier (e.g., us-west, eu-central)
Whether the user can access this region
Available LiveKit servers in this region
Getting Voice Token
Request Token
POST /channels/{channel_id}/voice/token
Request Body:
{
"channel_id": "123456789",
"guild_id": "987654321",
"region": "us-west",
"latitude": "37.7749",
"longitude": "-122.4194"
}
Parameters:
Guild ID (required for guild channels)
User’s latitude for region selection
User’s longitude for region selection
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"endpoint": "wss://voice-us-west.fluxer.app",
"connection_id": "conn_abc123xyz",
"token_nonce": "550e8400-e29b-41d4-a716-446655440000"
}
LiveKit access token (JWT)
WebSocket URL for LiveKit server
Unique connection identifier
Unique nonce for this token
Voice Permissions
LiveKit tokens include permissions:
interface VoicePermissions {
canSpeak: boolean; // Microphone audio
canStream: boolean; // Screen sharing
canVideo: boolean; // Camera video
}
Permission Calculation
Permissions are based on:
- Guild Permissions - User’s role permissions
- Channel Overwrites - Channel-specific overrides
- Voice State - Server mute/deafen status
// Check voice permissions
const hasSpeak = hasPermission(permissions, Permissions.SPEAK);
const hasStream = hasPermission(permissions, Permissions.STREAM);
Voice State Management
Update Voice State
PATCH /guilds/{guild_id}/voice-states/@me
Request Body:
{
"channel_id": "123456789",
"self_mute": true,
"self_deaf": false
}
Voice channel ID (null to disconnect)
Voice State Object
{
"user_id": "123456789",
"guild_id": "987654321",
"channel_id": "111222333",
"session_id": "abc123def456",
"self_mute": false,
"self_deaf": false,
"self_video": false,
"self_stream": false,
"mute": false,
"deaf": false,
"suppress": false
}
Server Pinning
Fluxer pins voice rooms to specific LiveKit servers to maintain session continuity:
// Pin room to server
await voiceRoomStore.pinRoomServer(
guildId,
channelId,
regionId,
serverId,
endpoint
);
// Get pinned server
const pinned = await voiceRoomStore.getPinnedRoomServer(
guildId,
channelId
);
Pin Lifecycle
- First User Joins - Room is pinned to a server
- Users Connect - All users connect to pinned server
- Last User Leaves - Pin is removed after timeout
- Region Change - Pin is removed and new server selected
LiveKit Integration
LiveKit tokens are JWTs containing:
{
"sub": "user_123456789_conn_abc123",
"name": "Username",
"video": {
"room": "guild_987654321_channel_111222333",
"roomJoin": true,
"canPublish": true,
"canSubscribe": true,
"canPublishData": true
},
"metadata": {
"user_id": "123456789",
"guild_id": "987654321",
"channel_id": "111222333"
},
"exp": 1704067200
}
Room Naming
LiveKit rooms use standardized naming:
// Guild voice channel
const roomName = `guild_${guildId}_channel_${channelId}`;
// DM voice channel
const roomName = `dm_${channelId}`;
// Group DM voice channel
const roomName = `group_${channelId}`;
Participant Identity
Participants are identified by:
const identity = `user_${userId}_${connectionId}`;
This format allows:
- Multiple connections per user (multiple devices)
- Easy user identification
- Connection tracking
Voice Operations
Disconnect User
await voiceService.disconnectParticipant({
guildId,
channelId,
userId,
connectionId
});
Update Participant
await voiceService.updateParticipant({
guildId,
channelId,
userId,
mute: true,
deaf: false
});
Disconnect Entire Channel
const result = await voiceService.disconnectChannel({
guildId,
channelId
});
console.log(`Disconnected ${result.disconnectedCount} participants`);
Update Permissions
await voiceService.updateParticipantPermissions({
guildId,
channelId,
userId,
connectionId,
canSpeak: false,
canStream: true,
canVideo: true
});
Unclaimed Account Restrictions
Unclaimed accounts have limited voice access:
Unclaimed accounts cannot join 1-on-1 voice calls.throw new UnclaimedAccountCannotJoinOneOnOneVoiceCallsError();
Unclaimed accounts can only join guild voice channels they own.if (channel.type === ChannelTypes.GUILD_VOICE) {
const isOwner = guild?.ownerId === userId;
if (!isOwner) {
throw new UnclaimedAccountCannotJoinVoiceChannelsError();
}
}
Unclaimed accounts can join group DM voice calls.
Voice Channel Capacity
Voice channels have user limits:
{
"code": "VOICE_CHANNEL_FULL",
"message": "Voice channel is full"
}
HTTP Status: 400 Bad Request
RTC Region Updates
Users with UPDATE_RTC_REGION permission can change a channel’s voice region:
PATCH /channels/{channel_id}
Request Body:
{
"rtc_region": "us-west"
}
Changing the region disconnects all current voice users. They must reconnect to use the new region.
Voice Quality Settings
LiveKit supports adaptive bitrate and quality settings:
// Client-side quality configuration
const room = new Room({
adaptiveStream: true,
dynacast: true,
videoCaptureDefaults: {
resolution: VideoPresets.h720.resolution,
facingMode: 'user'
},
audioCaptureDefaults: {
echoCancellation: true,
noiseSuppression: true,
autoGainControl: true
}
});
Monitoring and Metrics
List Participants
const result = await liveKitService.listParticipants({
guildId,
channelId,
regionId: 'us-west',
serverId: 'server-1'
});
if (result.status === 'success') {
console.log(`${result.participants.length} users in channel`);
}
Participant Object
{
"identity": "user_123456789_conn_abc123",
"state": "ACTIVE",
"tracks": [
{
"sid": "TR_abc123",
"type": "AUDIO",
"muted": false
}
],
"metadata": "{\"user_id\":\"123456789\"}",
"joined_at": 1704067200
}
Error Handling
Voice Errors
try {
const token = await getVoiceToken(channelId);
} catch (error) {
if (error.code === 'UNKNOWN_VOICE_REGION') {
// Invalid or unavailable region
} else if (error.code === 'VOICE_CHANNEL_FULL') {
// Channel at capacity
} else if (error.code === 'FEATURE_TEMPORARILY_DISABLED') {
// No available voice servers
}
}
Server Unavailability
When no voice servers are available:
{
"code": "FEATURE_TEMPORARILY_DISABLED",
"message": "Feature is temporarily disabled"
}
HTTP Status: 503 Service Unavailable
Best Practices
Handle Disconnections
Implement reconnection logic with exponential backoff for network interruptions.
Monitor Quality
Track connection quality metrics and adapt quality settings based on network conditions.
Graceful Degradation
Reduce quality (resolution, framerate) when bandwidth is limited rather than disconnecting.
Region Selection
Allow users to manually select regions if automatic selection results in poor quality.
Connection Flow
Client SDK Integration
import { Room } from 'livekit-client';
// Get token from Fluxer API
const { token, endpoint } = await getVoiceToken(channelId);
// Connect to LiveKit
const room = new Room();
await room.connect(endpoint, token);
// Enable microphone
await room.localParticipant.setMicrophoneEnabled(true);
// Listen for participants
room.on('participantConnected', (participant) => {
console.log(`${participant.identity} joined`);
});
// Listen for tracks
room.on('trackSubscribed', (track, publication, participant) => {
if (track.kind === 'audio') {
const audioElement = track.attach();
document.body.appendChild(audioElement);
}
});