Skip to main content

Overview

Fluxer provides enterprise-grade voice and video calling capabilities powered by LiveKit, supporting both direct calls and voice channels with advanced features like screen sharing, regional selection, and adaptive quality.

Architecture

LiveKit Integration

Scalable WebRTC infrastructure with multi-region support

Regional Routing

Automatic or manual selection of voice regions for optimal latency

Permission-Based Access

Granular controls for speak, stream, and video capabilities

Participant Management

Real-time participant state synchronization and control

Voice Channels

Voice channels provide persistent voice rooms within communities where members can join and leave freely.

Connecting to Voice

1

Request Voice Token

const voiceToken = await voiceService.getVoiceToken({
  guildId,
  channelId,
  userId,
  region: 'us-east', // Optional: specific region
  canSpeak: true,
  canStream: true,
  canVideo: true
});

// Response includes:
// - token: JWT for LiveKit authentication
// - endpoint: WebSocket URL for the voice server
// - connectionId: Unique connection identifier
// - tokenNonce: Nonce for token validation
2

Connect to LiveKit

import { Room } from 'livekit-client';

const room = new Room({
  adaptiveStream: true,
  dynacast: true,
  videoCaptureDefaults: {
    resolution: VideoPresets.h720.resolution
  }
});

await room.connect(voiceToken.endpoint, voiceToken.token);
3

Manage Audio/Video

// Enable microphone
await room.localParticipant.setMicrophoneEnabled(true);

// Enable camera
await room.localParticipant.setCameraEnabled(true);

// Start screen sharing
await room.localParticipant.setScreenShareEnabled(true);

Voice Permissions

Voice channels support granular permission control:
// Guild-based voice permissions
const voicePermissions = {
  canSpeak: await hasPermission(Permissions.SPEAK),
  canStream: await hasPermission(Permissions.STREAM),
  canVideo: await hasPermission(Permissions.VIDEO)
};

// Server-side mute/deafen state (from guild member)
if (guildId) {
  const member = await guildRepository.getMember(guildId, userId);
  const mute = member.isMute;
  const deaf = member.isDeaf;
}

Region Selection

Fluxer automatically selects optimal voice regions based on user location and preferences.
// System automatically selects best region based on:
// 1. User's preferred region (if set)
// 2. Channel's RTC region (if set)
// 3. Geographic proximity (latitude/longitude)
// 4. Server availability and capacity

const voiceToken = await voiceService.getVoiceToken({
  channelId,
  userId,
  latitude: '37.7749',
  longitude: '-122.4194'
});
Voice servers are “pinned” to channels once selected, ensuring all participants connect to the same server instance for the duration of the session.

Direct Calls

Direct calls enable one-on-one or group voice/video conversations in DMs and group DMs.

Starting a Call

// Initiate a call in a DM or group DM
const call = await callService.createOrGetCall({
  userId,
  channelId,
  region: 'us-east', // Optional
  ringing: [recipientId1, recipientId2], // Users to ring
  requestCache,
  latitude: '40.7128',
  longitude: '-74.0060'
});

// Response includes:
// - message_id: The call message ID
// - region: Selected voice region
// - ringing: Array of user IDs being rung
// - voice_states: Current participants

Call Eligibility

Fluxer respects user privacy preferences for incoming calls:
Users can configure who can call them:
  • Friends Only: Only accepted friends can ring
  • Friends of Friends: Mutual friend connections can ring (may be silent)
  • Guild Members: Members of shared guilds can ring (may be silent)
  • Everyone: Anyone can call (may be silent)
  • Nobody: No incoming calls allowed
  • Silent Everyone: Calls from non-friends don’t ring but still notify
// Check if user can be rung
const eligibility = await callService.checkCallEligibility({
  userId: callerId,
  channelId
});

if (!eligibility.ringable) {
  // Show silent call UI or prevent call
}
Unclaimed (guest) accounts have limitations:
  • Cannot join one-on-one DM calls
  • Can only join voice channels in guilds they own
if (user.isUnclaimedAccount()) {
  if (channel.type === ChannelTypes.DM) {
    throw new UnclaimedAccountCannotJoinOneOnOneVoiceCallsError();
  }
}

Ringing Recipients

// Ring specific users or all recipients
await callService.ringCallRecipients({
  userId,
  channelId,
  recipients: [userId1, userId2], // Optional: specific users
  requestCache,
  latitude,
  longitude
});

// Stop ringing
await callService.stopRingingCallRecipients({
  userId,
  channelId,
  recipients: [userId1] // Optional: specific users
});
Calls automatically open DM channels for participants if they don’t already have them open, dispatching CHANNEL_CREATE events.

Call Management

// Change call region mid-call
await callService.updateCall({
  userId,
  channelId,
  region: 'eu-central'
});

Participant Management

Manage participants in real-time during voice sessions.

Updating Participant State

// Update self mute/deafen state
await voiceService.updateVoiceState({
  guildId,
  channelId,
  userId,
  connectionId,
  mute: true,  // Self-mute
  deaf: false  // Not deafened
});

// Server mute/deafen (moderator action)
await voiceService.updateParticipant({
  guildId,
  channelId,
  userId: targetUserId,
  mute: true,  // Server mute
  deaf: false
});

Disconnecting Participants

// Disconnect a specific user from voice
await voiceService.disconnectParticipant({
  guildId,
  channelId,
  userId: targetUserId,
  connectionId
});

Screen Sharing and Streaming

Fluxer supports screen sharing and video streaming with permission controls.
// Enable screen sharing
await room.localParticipant.setScreenShareEnabled(true, {
  audio: true, // Include system audio
  video: {
    frameRate: 30,
    resolution: {
      width: 1920,
      height: 1080
    }
  }
});

// Monitor stream state
room.on(RoomEvent.TrackPublished, (publication, participant) => {
  if (publication.kind === Track.Kind.Video) {
    if (publication.source === Track.Source.ScreenShare) {
      // User started screen sharing
    }
  }
});

Gateway Events

Voice state changes are synchronized via WebSocket events:
{
  "t": "VOICE_STATE_UPDATE",
  "d": {
    "guild_id": "123456789",
    "channel_id": "987654321",
    "user_id": "111222333",
    "session_id": "abc123",
    "self_mute": false,
    "self_deaf": false,
    "mute": false,
    "deaf": false,
    "self_video": true,
    "self_stream": false
  }
}
Provides connection details for voice server
{
  "t": "VOICE_SERVER_UPDATE",
  "d": {
    "token": "eyJhbGc...",
    "guild_id": "123456789",
    "endpoint": "wss://voice.example.com"
  }
}
Dispatched when a call starts in a DM/group DM
Dispatched when call state changes (region, ringing users)
Dispatched when a call ends

Permission Requirements

CONNECT
Permission
required
Required to connect to voice channels
SPEAK
Permission
required
Required to transmit audio in voice channels
VIDEO
Permission
Required to transmit video
STREAM
Permission
Required to screen share or stream
MUTE_MEMBERS
Permission
Required to server-mute other users
DEAFEN_MEMBERS
Permission
Required to server-deafen other users
MOVE_MEMBERS
Permission
Required to disconnect users or move them between channels

Best Practices

1

Handle Region Changes Gracefully

When users change regions, existing connections need to be re-established. Implement smooth transitions with loading states.
2

Implement Connection Recovery

Voice connections can drop due to network issues. Use LiveKit’s reconnection events to automatically restore connections.
3

Monitor Participant States

Subscribe to LiveKit room events to track when participants join, leave, mute, or start streaming.
4

Respect Privacy Settings

Always check call eligibility before ringing users to respect their privacy preferences.
5

Optimize for Mobile

Use adaptive streaming and lower video resolutions on mobile devices to preserve battery and bandwidth.

Troubleshooting

  • Verify the user has CONNECT permission
  • Check if the voice region is accessible
  • Ensure the LiveKit server is operational
  • Validate the voice token hasn’t expired
  • Confirm microphone/camera permissions in browser
  • Check if user has SPEAK or VIDEO permissions
  • Verify device selection in client settings
  • Look for server-mute/deafen state
  • Try switching to a closer voice region
  • Reduce video resolution or disable video
  • Check network bandwidth and latency
  • Use adaptive streaming features

Communities

Learn about guild voice channels

Messaging

Combine voice with text chat

LiveKit Documentation

Official LiveKit documentation

API Reference

Complete API documentation

Build docs developers (and LLMs) love