Video Calling Guide - nself-org/nchat GitHub Wiki
Version: 0.4.0 Last Updated: January 30, 2026
nself-chat v0.4.0 introduces comprehensive HD video calling with support for up to 50 participants, including advanced features like background blur, virtual backgrounds, screen sharing, and adaptive bitrate streaming.
- HD Video Calling: Support for 180p, 360p, 720p, and 1080p resolutions
- Multi-Participant: Up to 50 participants in a single call
- Grid View: Automatic grid layout for all participants
- Speaker View: Main speaker with thumbnails
- Screen Sharing: Share your screen with all participants
- Picture-in-Picture: Continue working while in a call
- Adaptive Bitrate: Automatically adjusts quality based on network conditions
- Simulcast: Sends multiple quality layers for SFU selection
-
Quality Profiles:
- 180p: Low (320x180, 15fps, 150kbps) - for poor connections
- 360p: Medium (640x360, 24fps, 400kbps) - balanced quality
- 720p: HD (1280x720, 30fps, 1.5Mbps) - default, high quality
- 1080p: Full HD (1920x1080, 30fps, 3Mbps) - best quality
- Background Blur: Light, medium, or strong blur
- Virtual Backgrounds: Replace background with images or colors
- 8 Preset Backgrounds: Office, library, beach, mountains, space, etc.
- Custom Images: Upload your own background images
- Edge Smoothing: Adjustable edge detection for natural-looking effects
From Channel:
import { VideoCallButton } from '@/components/calls/VideoCallButton'
;<VideoCallButton channelId="channel-id" type="video" />From Direct Message:
import { useVideoCall } from '@/hooks/use-video-call'
const { startCall } = useVideoCall({
userId: currentUser.id,
userName: currentUser.name,
})
// Start 1-on-1 video call
await startCall(targetUserId, targetUserName)When you receive an incoming video call:
- Accept: Click "Accept" to join with video enabled
- Decline: Click "Decline" to reject the call
- Audio Only: Toggle video off before accepting
Mute/Unmute Audio:
- Click the microphone button
- Keyboard shortcut:
M - Status: Red icon when muted
Toggle Video:
- Click the camera button
- Keyboard shortcut:
V - Shows avatar when video is off
End Call:
- Click the red phone button
- Keyboard shortcut:
Esc
Screen Sharing:
const { startScreenShare, stopScreenShare, isScreenSharing } = useVideoCall(options)
// Start sharing
await startScreenShare()
// Stop sharing
stopScreenShare()Picture-in-Picture:
const { enterPictureInPicture, exitPictureInPicture } = useVideoCall(options)
// Enter PiP mode
await enterPictureInPicture(videoElement)
// Exit PiP mode
await exitPictureInPicture()Grid View:
- All participants in equal-sized tiles
- Automatic grid calculation
- Up to 4 tiles per row
Speaker View:
- Active speaker in main view
- Other participants in thumbnails
- Automatically switches on voice detection
Pinned View:
- Pin a specific participant to main view
- Other participants in thumbnails
- Click pin icon on any tile
import { useBackgroundEffects } from '@/hooks/use-background-effects'
const { setEffectType, setBlurStrength, applyToStream } = useBackgroundEffects()
// Enable blur
setEffectType('blur')
setBlurStrength('medium') // 'light', 'medium', or 'strong'
// Apply to video stream
const processedStream = await applyToStream(originalStream)const { setVirtualBackground, selectPresetBackground } = useBackgroundEffects()
// Use preset background
await selectPresetBackground('office-1')
// Use custom image
await setVirtualBackground('image', 'https://example.com/background.jpg')
// Use solid color
await setVirtualBackground('color', '#3b82f6')Professional:
- Modern Office
- Conference Room
- Library
Scenic:
- Beach
- Mountains
- City Skyline
Fun:
- Space
- Abstract patterns
The system automatically adjusts video quality based on:
- Network Bandwidth: Available upload/download speed
- Packet Loss: Reduces quality if >5% packet loss
- RTT (Round-Trip Time): Latency to other participants
- CPU Usage: Performance on your device
const { setVideoQuality } = useVideoCall(options)
// Set quality manually
await setVideoQuality('720p') // '180p', '360p', '720p', '1080p'Simulcast automatically sends 3 quality layers:
- High: Full resolution (e.g., 720p)
- Medium: Half resolution (e.g., 360p)
- Low: Quarter resolution (e.g., 180p)
The SFU server selects the appropriate layer for each participant based on their network conditions.
-
Check Permissions:
- Browser settings > Camera access
- Allow camera permission for the site
-
Device Selection:
const { selectCamera, availableCameras } = useVideoCall(options) // List cameras console.log(availableCameras) // Select specific camera await selectCamera(deviceId)
-
Browser Support:
- Chrome 74+
- Firefox 66+
- Safari 12.1+
- Edge 79+
-
Check Network:
- Minimum: 500kbps upload/download
- Recommended: 2Mbps+ for HD quality
- Use speedtest.net
-
Reduce Quality:
setVideoQuality('360p') // Lower quality for poor connections
-
Close Other Apps:
- Close bandwidth-heavy applications
- Limit number of participants
- Disable background effects
- Use Headphones: Prevents microphone from picking up speaker audio
- Enable Echo Cancellation: Automatic (enabled by default)
- Mute When Not Speaking: Reduces ambient noise
-
Browser Compatibility:
- Requires WebGL support
- Check: Visit get.webgl.org
-
Performance:
- Background effects require additional CPU
- May reduce frame rate on slower devices
- Disable effects if experiencing lag
const {
// State
isInCall,
isCallConnected,
isMuted,
isVideoEnabled,
isScreenSharing,
callDuration,
localStream,
remoteStreams,
// Actions
startCall,
acceptCall,
declineCall,
endCall,
toggleMute,
toggleVideo,
startScreenShare,
stopScreenShare,
enterPictureInPicture,
exitPictureInPicture,
// Device Selection
selectCamera,
selectMicrophone,
selectSpeaker,
availableCameras,
availableMicrophones,
availableSpeakers,
} = useVideoCall({
userId: 'user-id',
userName: 'User Name',
userAvatarUrl: 'https://...',
defaultVideoQuality: '720p',
onCallStarted: (callId) => {},
onCallEnded: (callId, reason) => {},
onError: (error) => {},
})const {
effectType,
blurStrength,
setEffectType,
setBlurStrength,
setVirtualBackground,
applyToStream,
stopProcessing,
presetBackgrounds,
selectPresetBackground,
} = useBackgroundEffects({
onError: (error) => {},
})const { mode, tiles, mainTile, thumbnails, setMode, pinParticipant, setSpeakingParticipant } =
useVideoLayout({
containerRef,
participantIds,
initialMode: 'speaker',
})- Use Wired Connection: Ethernet is more stable than WiFi
- Close Unnecessary Tabs: Reduces CPU and memory usage
- Good Lighting: Helps with video compression
- Stable Position: Reduces motion blur and bandwidth usage
- Check Background: Use background blur/virtual background
- Mute When Not Speaking: Prevents accidental audio leaks
- Turn Off Video: When not needed to save bandwidth
- Limit Participants: Fewer participants = better performance
- Lower Quality: Use 360p or 480p for large calls
- Disable Effects: Turn off background blur for slower devices
- Close Other Apps: Especially video streaming services
| Shortcut | Action |
|---|---|
M |
Toggle mute |
V |
Toggle video |
S |
Start/stop screen share |
Esc |
End call |
G |
Switch to grid view |
P |
Switch to speaker view |
F |
Toggle fullscreen |
- Chrome 74+ (Windows, macOS, Linux)
- Firefox 66+ (Windows, macOS, Linux)
- Safari 12.1+ (macOS)
- Edge 79+ (Windows, macOS)
- Chrome Mobile 74+ (Android)
- Safari iOS 12.1+ (iOS)
- Electron (Windows, macOS, Linux)
- Capacitor (iOS, Android)
- Tauri (Windows, macOS, Linux)
- Signaling: WebSocket (Socket.io)
- Media: WebRTC PeerConnection
- Codec: VP8/VP9 or H.264
- Audio Codec: Opus
- Minimum: 500kbps upload/download
- Recommended: 2Mbps+ for HD
- Ports: UDP 49152-65535 (STUN/TURN)
- End-to-End Encryption: Via DTLS-SRTP (WebRTC default)
- Signaling Encryption: WSS (WebSocket Secure)
- No Recording: Calls are not recorded by default