Try Live
Add Docs
Rankings
Pricing
Enterprise
Docs
Install
Theme
Install
Docs
Pricing
Enterprise
More...
More...
Try Live
Rankings
Create API Key
Add Docs
Daily JS
https://github.com/daily-co/daily-js
Admin
Daily JS is a JavaScript library for integrating video calling functionality into web applications
...
Tokens:
18,560
Snippets:
131
Trust Score:
8.7
Update:
2 weeks ago
Context
Skills
Chat
Benchmark
92.3
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# Daily JS Daily JS is a comprehensive JavaScript library for adding real-time video and audio calling capabilities to web applications using the Daily.co platform. It provides both a low-level Call Object API for building custom video experiences and a pre-built UI option for rapid integration. The library handles WebRTC complexities including peer connections, media tracks, screen sharing, recording, transcription, and participant management across web browsers and React Native environments. The library offers extensive configuration options for video quality, simulcast encoding, background processing (blur/images), and noise cancellation. It supports various meeting features including waiting rooms (knocking), live streaming to RTMP endpoints, cloud recording, real-time transcription, and dial-out capabilities for SIP/PSTN calls. Events-based architecture allows developers to respond to participant changes, track updates, network quality fluctuations, and meeting state transitions. ## Creating a Call Object Creates an instance of the Daily call object for custom video call implementations without the pre-built UI. The `createCallObject()` factory method is the primary way to initialize a Daily call instance when building custom video experiences. It returns a DailyCall instance that you can configure with options before joining a meeting. ```javascript import Daily from '@daily-co/daily-js'; // Basic call object creation const callObject = Daily.createCallObject(); // Call object with configuration options const callObject = Daily.createCallObject({ url: 'https://your-domain.daily.co/room-name', token: 'your-meeting-token', userName: 'John Doe', startVideoOff: false, startAudioOff: false, dailyConfig: { micAudioMode: 'music', preferH264: true, } }); // Check browser support before creating const browserInfo = Daily.supportedBrowser(); if (browserInfo.supported) { const call = Daily.createCallObject(); } // browserInfo: { supported: true, mobile: false, name: 'Chrome', version: '120.0', supportsScreenShare: true, supportsVideoProcessing: true } ``` ## Creating an Embedded Frame Creates an iframe-based Daily call with pre-built UI embedded in your web page. The `createFrame()` method creates an iframe element containing Daily's pre-built video call interface. You can customize its appearance and position within a parent element or use default styling. ```javascript import Daily from '@daily-co/daily-js'; // Create frame in document body with default positioning const callFrame = Daily.createFrame({ url: 'https://your-domain.daily.co/room-name', showLeaveButton: true, showFullscreenButton: true, showParticipantsBar: true, theme: { colors: { accent: '#0066FF', accentText: '#FFFFFF', background: '#1A1A1A', mainAreaBg: '#121212', } } }); // Create frame in specific container element const container = document.getElementById('call-container'); const callFrame = Daily.createFrame(container, { url: 'https://your-domain.daily.co/room-name', iframeStyle: { width: '100%', height: '100%', border: 'none', }, customTrayButtons: { myButton: { iconPath: 'https://example.com/icon.svg', label: 'Custom Action', tooltip: 'Perform custom action' } } }); ``` ## Joining a Meeting Joins a Daily video call room and returns participant information on success. The `join()` method connects to a Daily room URL with optional configuration. It returns a Promise that resolves with all current participants when the join succeeds, or rejects with an error if the join fails. ```javascript const callObject = Daily.createCallObject(); // Basic join try { const participants = await callObject.join({ url: 'https://your-domain.daily.co/room-name' }); console.log('Joined meeting with participants:', participants); // { local: { session_id: 'abc123', user_name: 'John', local: true, ... }, 'xyz789': { ... } } } catch (error) { console.error('Failed to join:', error); } // Join with token and user settings const participants = await callObject.join({ url: 'https://your-domain.daily.co/private-room', token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...', userName: 'Meeting Participant', userData: { role: 'presenter', company: 'Acme Inc' }, startVideoOff: false, startAudioOff: true, receiveSettings: { base: { video: { layer: 2 }, screenVideo: { layer: 2 } } } }); // Listen for join event callObject.on('joined-meeting', (event) => { console.log('Successfully joined:', event.participants); }); callObject.on('error', (event) => { console.error('Join failed:', event.errorMsg, event.error); }); ``` ## Managing Participants Access and update meeting participants including their media states and permissions. The `participants()` method returns an object containing all participants keyed by session ID, with a special 'local' key for the local user. Use `updateParticipant()` to modify participant settings. ```javascript // Get all participants const participants = callObject.participants(); const localParticipant = participants.local; const remoteParticipants = Object.values(participants).filter(p => !p.local); // Access participant properties console.log('Local participant:', { sessionId: localParticipant.session_id, userName: localParticipant.user_name, videoState: localParticipant.tracks.video.state, audioState: localParticipant.tracks.audio.state, isOwner: localParticipant.owner }); // Update a remote participant's subscribed tracks callObject.updateParticipant(remoteSessionId, { setSubscribedTracks: { video: true, audio: true, screenVideo: true, screenAudio: false } }); // Mute a remote participant (requires owner permissions) callObject.updateParticipant(remoteSessionId, { setAudio: false, setVideo: false }); // Update participant permissions callObject.updateParticipant(remoteSessionId, { updatePermissions: { hasPresence: true, canSend: ['audio', 'video'], canAdmin: ['participants'] } }); // Eject a participant callObject.updateParticipant(remoteSessionId, { eject: true }); // Get participant counts const counts = callObject.participantCounts(); console.log(`Present: ${counts.present}, Hidden: ${counts.hidden}`); ``` ## Controlling Local Media Toggle and configure local audio and video tracks including device selection. The local media methods allow you to mute/unmute your camera and microphone, select specific devices, and configure input processing like background blur or noise cancellation. ```javascript // Check current local media state const isVideoOn = callObject.localVideo(); const isAudioOn = callObject.localAudio(); const isScreenShareVideoOn = callObject.localScreenVideo(); const isScreenShareAudioOn = callObject.localScreenAudio(); // Toggle local audio/video callObject.setLocalAudio(false); // Mute microphone callObject.setLocalVideo(true); // Turn on camera // Mute and force discard the audio track callObject.setLocalAudio(false, { forceDiscardTrack: true }); // Set input devices await callObject.setInputDevicesAsync({ audioDeviceId: 'microphone-device-id', videoDeviceId: 'camera-device-id' }); // Set output device (speaker) await callObject.setOutputDeviceAsync({ outputDeviceId: 'speaker-device-id' }); // Get current device info const devices = await callObject.getInputDevices(); console.log('Camera:', devices.camera); console.log('Microphone:', devices.mic); console.log('Speaker:', devices.speaker); // Enumerate all available devices const { devices: allDevices } = await callObject.enumerateDevices(); const cameras = allDevices.filter(d => d.kind === 'videoinput'); const microphones = allDevices.filter(d => d.kind === 'audioinput'); const speakers = allDevices.filter(d => d.kind === 'audiooutput'); // Cycle through cameras const { device } = await callObject.cycleCamera(); console.log('Switched to camera:', device.label); ``` ## Input Settings and Processors Configure video background effects and audio noise cancellation processors. The input settings API allows you to apply video processors (blur, background images) and audio processors (noise cancellation) to your local media tracks. ```javascript // Apply background blur await callObject.updateInputSettings({ video: { processor: { type: 'background-blur', config: { strength: 0.8 } } } }); // Apply background image await callObject.updateInputSettings({ video: { processor: { type: 'background-image', config: { source: 'https://example.com/background.jpg' } } } }); // Enable noise cancellation await callObject.updateInputSettings({ audio: { processor: { type: 'noise-cancellation' } } }); // Remove all processors await callObject.updateInputSettings({ video: { processor: { type: 'none' } }, audio: { processor: { type: 'none' } } }); // Get current input settings const settings = await callObject.getInputSettings(); console.log('Video processor:', settings.video?.processor); console.log('Audio processor:', settings.audio?.processor); // Use custom MediaStreamTrack as video source const customTrack = myCustomVideoStream.getVideoTracks()[0]; await callObject.updateInputSettings({ video: { settings: { customTrack: customTrack } } }); ``` ## Screen Sharing Start and stop screen sharing with configurable quality settings. The screen sharing API allows participants to share their screen, window, or browser tab with other meeting participants, including optional audio capture. ```javascript // Start basic screen share callObject.startScreenShare(); // Start screen share with options callObject.startScreenShare({ displayMediaOptions: { video: { width: { max: 1920 }, height: { max: 1080 }, frameRate: { max: 30 } }, audio: true, selfBrowserSurface: 'include', surfaceSwitching: 'include' }, screenVideoSendSettings: 'motion-optimized' // or 'detail-optimized' }); // Start screen share with custom media stream const displayStream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }); callObject.startScreenShare({ mediaStream: displayStream }); // Stop screen share callObject.stopScreenShare(); // Update screen share settings while sharing callObject.updateScreenShare({ screenVideo: { enabled: true }, screenAudio: { enabled: false } }); // Listen for screen share events callObject.on('local-screen-share-started', (event) => { console.log('Screen share started'); }); callObject.on('local-screen-share-stopped', (event) => { console.log('Screen share stopped'); }); callObject.on('local-screen-share-canceled', (event) => { console.log('Screen share was canceled by user'); }); ``` ## Event Handling Subscribe to meeting events to react to participant and media changes. Daily uses an event-driven architecture. Use `on()`, `once()`, and `off()` methods to manage event listeners for meeting state changes, participant updates, and media track events. ```javascript // Subscribe to events callObject.on('joining-meeting', () => { console.log('Connecting to meeting...'); }); callObject.on('joined-meeting', (event) => { console.log('Joined meeting with participants:', event.participants); }); callObject.on('left-meeting', () => { console.log('Left the meeting'); }); // Participant events callObject.on('participant-joined', (event) => { console.log('New participant:', event.participant.user_name); }); callObject.on('participant-updated', (event) => { const p = event.participant; console.log(`${p.user_name} updated - video: ${p.tracks.video.state}`); }); callObject.on('participant-left', (event) => { console.log('Participant left:', event.participant.user_name, event.reason); }); // Track events callObject.on('track-started', (event) => { const { participant, track, type } = event; if (type === 'video' && !participant.local) { // Attach remote video track to video element const videoEl = document.getElementById(`video-${participant.session_id}`); videoEl.srcObject = new MediaStream([track]); } }); callObject.on('track-stopped', (event) => { console.log(`Track stopped: ${event.type} from ${event.participant?.user_name}`); }); // Error handling callObject.on('error', (event) => { console.error('Fatal error:', event.error); }); callObject.on('nonfatal-error', (event) => { console.warn('Non-fatal error:', event.type, event.errorMsg); }); // Remove specific listener const handler = (event) => console.log(event); callObject.on('participant-joined', handler); callObject.off('participant-joined', handler); // One-time listener callObject.once('joined-meeting', (event) => { console.log('Joined (only fires once)'); }); ``` ## Recording Start and manage cloud or local recording of meetings. The recording API allows meeting owners to record calls to the cloud, as raw tracks, or locally in the browser. You can configure recording layout and quality settings. ```javascript // Start cloud recording with default layout callObject.startRecording(); // Start recording with custom options callObject.startRecording({ type: 'cloud', // 'cloud', 'raw-tracks', 'local', or 'cloud-audio-only' width: 1920, height: 1080, fps: 30, videoBitrate: 5000, layout: { preset: 'default', max_cam_streams: 9 } }); // Start with custom layout callObject.startRecording({ layout: { preset: 'active-participant' } }); // Update recording layout while recording callObject.updateRecording({ layout: { preset: 'single-participant', session_id: 'participant-session-id' } }); // Stop recording callObject.stopRecording(); // Listen for recording events callObject.on('recording-started', (event) => { console.log('Recording started:', event.recordingId); }); callObject.on('recording-stopped', (event) => { console.log('Recording stopped'); }); callObject.on('recording-error', (event) => { console.error('Recording error:', event.errorMsg); }); // For local recording, handle data chunks callObject.on('recording-data', (event) => { const { data, finished } = event; // data is a Uint8Array chunk recordingChunks.push(data); if (finished) { const blob = new Blob(recordingChunks, { type: 'video/webm' }); const url = URL.createObjectURL(blob); } }); ``` ## Live Streaming Stream meetings to RTMP endpoints like YouTube, Twitch, or custom destinations. The live streaming API allows broadcasting your meeting to multiple RTMP endpoints simultaneously with configurable layouts and quality settings. ```javascript // Start live streaming to RTMP URL callObject.startLiveStreaming({ rtmpUrl: 'rtmp://live.twitch.tv/app/your-stream-key', width: 1920, height: 1080, fps: 30, videoBitrate: 4500, layout: { preset: 'default', max_cam_streams: 4 } }); // Stream to multiple endpoints callObject.startLiveStreaming({ endpoints: [ { endpoint: 'rtmp://live.twitch.tv/app/key1' }, { endpoint: 'rtmp://a.rtmp.youtube.com/live2/key2' } ], layout: { preset: 'active-participant' } }); // Update live stream layout callObject.updateLiveStreaming({ layout: { preset: 'portrait', variant: 'inset' } }); // Add endpoint to active stream callObject.addLiveStreamingEndpoints({ endpoints: [{ endpoint: 'rtmp://new-endpoint.com/stream' }] }); // Remove endpoint from active stream callObject.removeLiveStreamingEndpoints({ endpoints: [{ endpoint: 'rtmp://old-endpoint.com/stream' }] }); // Stop live streaming callObject.stopLiveStreaming(); // Live streaming events callObject.on('live-streaming-started', (event) => { console.log('Live stream started'); }); callObject.on('live-streaming-updated', (event) => { console.log('Endpoint state:', event.endpoint, event.state); }); callObject.on('live-streaming-stopped', () => { console.log('Live stream stopped'); }); callObject.on('live-streaming-error', (event) => { console.error('Streaming error:', event.errorMsg); }); ``` ## Transcription Start and manage real-time meeting transcription powered by Deepgram. The transcription API provides real-time speech-to-text transcription of meeting audio with support for multiple languages and configuration options. ```javascript // Start transcription with default settings callObject.startTranscription(); // Start transcription with custom options callObject.startTranscription({ language: 'en', model: 'nova-2', profanity_filter: true, punctuate: true, endpointing: 500, // milliseconds of silence to end utterance redact: ['pci', 'ssn'], includeRawResponse: true }); // Update transcription participants callObject.updateTranscription({ participants: ['session-id-1', 'session-id-2'] }); // Stop transcription callObject.stopTranscription(); // Listen for transcription messages callObject.on('transcription-message', (event) => { console.log(`${event.participantId}: ${event.text}`); console.log('Timestamp:', event.timestamp); console.log('Track type:', event.trackType); // 'cam-audio', 'screen-audio', etc. }); callObject.on('transcription-started', (event) => { console.log('Transcription started:', event.instanceId); console.log('Language:', event.language); console.log('Model:', event.model); }); callObject.on('transcription-stopped', (event) => { console.log('Transcription stopped by:', event.updatedBy); }); callObject.on('transcription-error', (event) => { console.error('Transcription error:', event.errorMsg); }); ``` ## Network Quality and Testing Monitor network conditions and run connection quality tests. Daily provides APIs to monitor real-time network quality during calls and run pre-call tests to assess connection quality before joining. ```javascript // Get current network stats const stats = await callObject.getNetworkStats(); console.log('Network state:', stats.networkState); // 'good', 'warning', 'bad', 'unknown' console.log('Stats:', stats.stats.latest); // { recvBitsPerSecond, sendBitsPerSecond, videoRecvPacketLoss, ... } // Listen for network quality changes callObject.on('network-quality-change', (event) => { console.log('Network state:', event.networkState); console.log('Reasons:', event.networkStateReasons); console.log('Quality:', event.quality); }); // Run pre-call quality test (before joining) const callObject = Daily.createCallObject(); await callObject.preAuth({ url: 'https://domain.daily.co/room' }); const results = await callObject.testCallQuality(); console.log('Test result:', results.result); // 'good', 'warning', 'bad', 'failed' console.log('Data:', results.data); // { maxRoundTripTime, avgRoundTripTime, avgSendPacketLoss, avgAvailableOutgoingBitrate } // Stop test early if needed callObject.stopTestCallQuality(); // Test websocket connectivity const wsResults = await callObject.testWebsocketConnectivity(); console.log('Websocket test:', wsResults.result); console.log('Passed regions:', wsResults.passedRegions); console.log('Failed regions:', wsResults.failedRegions); // CPU load monitoring const cpuStats = await callObject.getCpuLoadStats(); console.log('CPU state:', cpuStats.cpuLoadState); // 'low' or 'high' console.log('Reason:', cpuStats.cpuLoadStateReason); callObject.on('cpu-load-change', (event) => { console.log('CPU load state changed:', event.cpuLoadState); }); ``` ## App Messages Send custom messages between meeting participants. The app message API allows participants to send arbitrary data to other participants in the meeting for features like chat, reactions, or custom signaling. ```javascript // Send message to all participants callObject.sendAppMessage({ type: 'chat', text: 'Hello everyone!' }); // Send message to specific participant callObject.sendAppMessage( { type: 'reaction', emoji: '👍' }, 'target-session-id' ); // Send message to multiple participants callObject.sendAppMessage( { type: 'notification', title: 'Important update' }, ['session-id-1', 'session-id-2'] ); // Receive messages callObject.on('app-message', (event) => { console.log('Message from:', event.fromId); console.log('Data:', event.data); if (event.data.type === 'chat') { displayChatMessage(event.fromId, event.data.text); } else if (event.data.type === 'reaction') { showReaction(event.fromId, event.data.emoji); } }); // Note: Message size is limited (default 4KB, configurable per domain) // Large data should be chunked or sent via external channels ``` ## Room and Meeting Information Access room configuration and meeting session details. The room API provides access to the current room's configuration, domain settings, and meeting session information. ```javascript // Get room info (after joining) const roomInfo = await callObject.room(); console.log('Room name:', roomInfo.name); console.log('Room ID:', roomInfo.id); console.log('Room config:', roomInfo.config); // { max_participants, enable_screenshare, enable_chat, enable_knocking, ... } // Get room info including config defaults const fullInfo = await callObject.room({ includeRoomConfigDefaults: true }); // Get meeting session summary const session = callObject.meetingSessionSummary(); console.log('Session ID:', session.id); // Get meeting session state (includes custom data) const state = callObject.meetingSessionState(); console.log('Topology:', state.topology); // 'sfu', 'peer', or 'none' console.log('Session data:', state.data); // Set meeting session data (shared with all participants) callObject.setMeetingSessionData( { agenda: 'Project review', startTime: Date.now() }, 'shallow-merge' // or 'replace' ); // Listen for session state updates callObject.on('meeting-session-state-updated', (event) => { console.log('Session state updated:', event.meetingSessionState); }); // Get geographic region const geo = await callObject.geo(); console.log('Current region:', geo.current); // Get network topology const topology = await callObject.getNetworkTopology(); console.log('Topology:', topology.topology); // 'sfu' or 'peer' ``` ## Waiting Room (Knocking) Manage participants waiting to join a meeting with knocking enabled. When a room has `enable_knocking` enabled, participants without owner tokens wait in a lobby until admitted. Owners can view and manage waiting participants. ```javascript // Get all waiting participants (owner only) const waiting = callObject.waitingParticipants(); console.log('Waiting:', Object.values(waiting)); // [{ id: 'abc123', name: 'John Doe', awaitingAccess: { level: 'full' } }] // Grant access to a waiting participant await callObject.updateWaitingParticipant('waiting-participant-id', { grantRequestedAccess: true }); // Deny access (don't grant) await callObject.updateWaitingParticipant('waiting-participant-id', { grantRequestedAccess: false }); // Batch update multiple waiting participants await callObject.updateWaitingParticipants({ 'participant-id-1': { grantRequestedAccess: true }, 'participant-id-2': { grantRequestedAccess: true } }); // Listen for waiting participant events callObject.on('waiting-participant-added', (event) => { console.log('New participant waiting:', event.participant.name); }); callObject.on('waiting-participant-updated', (event) => { console.log('Waiting participant updated:', event.participant); }); callObject.on('waiting-participant-removed', (event) => { console.log('Waiting participant removed:', event.participant.name); }); // For non-owners: request access if in lobby const accessState = callObject.accessState(); if (accessState.access.level === 'lobby') { const result = await callObject.requestAccess({ access: { level: 'full' }, name: 'John Doe' }); console.log('Access granted:', result.granted); } ``` ## Audio Level Monitoring Monitor local and remote participant audio levels for visualizations. The audio level observer APIs provide real-time audio level data useful for building audio visualization features like speaking indicators. ```javascript // Start monitoring local audio level await callObject.startLocalAudioLevelObserver(100); // interval in ms // Check if observer is running const isRunning = callObject.isLocalAudioLevelObserverRunning(); // Get current local audio level (0.0 to 1.0) const level = callObject.getLocalAudioLevel(); // Listen for local audio level updates callObject.on('local-audio-level', (event) => { updateMicrophoneMeter(event.audioLevel); }); // Stop local audio level observer callObject.stopLocalAudioLevelObserver(); // Start monitoring remote participants' audio levels await callObject.startRemoteParticipantsAudioLevelObserver(100); // Get remote audio levels const remoteLevels = callObject.getRemoteParticipantsAudioLevel(); // { 'session-id-1': 0.5, 'session-id-2': 0.0, ... } // Listen for remote audio level updates callObject.on('remote-participants-audio-level', (event) => { for (const [sessionId, level] of Object.entries(event.participantsAudioLevel)) { updateSpeakingIndicator(sessionId, level > 0.1); } }); // Stop remote audio level observer callObject.stopRemoteParticipantsAudioLevelObserver(); // Active speaker detection (built-in) const activeSpeaker = callObject.getActiveSpeaker(); console.log('Current active speaker:', activeSpeaker.peerId); callObject.on('active-speaker-change', (event) => { console.log('New active speaker:', event.activeSpeaker.peerId); }); ``` ## Custom Tracks Send additional custom audio or video tracks beyond the standard camera and microphone. Custom tracks allow you to send additional media streams such as screen regions, processed video, or audio from files alongside your standard tracks. ```javascript // Get a custom video track (e.g., from canvas) const canvas = document.getElementById('my-canvas'); const canvasStream = canvas.captureStream(30); const customVideoTrack = canvasStream.getVideoTracks()[0]; // Start sending custom track const trackName = await callObject.startCustomTrack({ track: customVideoTrack, trackName: 'canvas-video', mode: 'speech' // or 'music' for audio tracks }); console.log('Custom track started with name:', trackName); // Start custom audio track const audioContext = new AudioContext(); const destination = audioContext.createMediaStreamDestination(); const customAudioTrack = destination.stream.getAudioTracks()[0]; await callObject.startCustomTrack({ track: customAudioTrack, trackName: 'custom-audio', mode: { bitrate: 128000, stereo: true } }); // Stop custom track await callObject.stopCustomTrack('canvas-video'); // Access custom tracks on participants const participant = callObject.participants()['remote-session-id']; if (participant.tracks['canvas-video']?.state === 'playable') { const customTrack = participant.tracks['canvas-video'].track; videoElement.srcObject = new MediaStream([customTrack]); } // Subscribe to custom tracks callObject.updateParticipant(sessionId, { setSubscribedTracks: { video: true, audio: true, custom: { 'canvas-video': true, 'custom-audio': true } } }); ``` ## Dial-Out (SIP/PSTN) Dial out to phone numbers or SIP endpoints from a Daily meeting. The dial-out API allows meeting participants to call phone numbers (PSTN) or SIP endpoints, bringing external callers into the video meeting. ```javascript // Dial out to phone number const session = await callObject.startDialOut({ phoneNumber: '+14155551234', displayName: 'Video Meeting', callerId: '+18005551000', // Your verified caller ID codecs: { audio: ['OPUS', 'PCMU'] } }); console.log('Dial-out session:', session.sessionId); // Dial out to phone with extension await callObject.startDialOut({ phoneNumber: '+14155551234', extension: '1234', waitBeforeExtensionDialSec: 5 }); // Dial out to SIP endpoint await callObject.startDialOut({ sipUri: 'sip:user@domain.com', displayName: 'Daily Meeting', video: true, // Enable video for SIP codecs: { audio: ['OPUS'], video: ['H264', 'VP8'] } }); // Stop dial-out await callObject.stopDialOut({ sessionId: 'dial-out-session-id' }); // Send DTMF tones await callObject.sendDTMF({ sessionId: 'dial-out-session-id', tones: '1234#', method: 'auto', // 'sip-info', 'telephone-event', or 'auto' digitDurationMs: 500 }); // Transfer call await callObject.sipCallTransfer({ sessionId: 'dial-out-session-id', toEndPoint: '+14155559999', callerId: '+18005551000' }); // Dial-out events callObject.on('dialout-connected', (event) => { console.log('Call connected:', event.sessionId); }); callObject.on('dialout-answered', (event) => { console.log('Call answered:', event.sessionId); }); callObject.on('dialout-stopped', (event) => { console.log('Call ended:', event.sessionId); }); callObject.on('dialout-error', (event) => { console.error('Dial-out error:', event.errorMsg); }); ``` ## Leaving and Cleanup Properly leave meetings and destroy call instances. The `leave()` method disconnects from the current meeting while `destroy()` cleans up all resources. Always destroy call objects when done to prevent memory leaks. ```javascript // Leave the meeting (can rejoin later) await callObject.leave(); // Check meeting state const state = callObject.meetingState(); // 'new', 'loading', 'loaded', 'joining-meeting', 'joined-meeting', 'left-meeting', 'error' // Destroy the call object (cannot be reused) await callObject.destroy(); // Check if destroyed const isDestroyed = callObject.isDestroyed(); // Listen for left-meeting event callObject.on('left-meeting', () => { console.log('Left the meeting'); cleanupUI(); }); // Handle page unload window.addEventListener('beforeunload', async () => { await callObject.leave(); await callObject.destroy(); }); // Get call instance by ID (useful for managing multiple instances) const existingCall = Daily.getCallInstance('call-client-id'); if (existingCall && !existingCall.isDestroyed()) { await existingCall.leave(); } ``` Daily JS provides a complete solution for building video calling applications with support for one-on-one calls, group meetings, webinars, and broadcast scenarios. The library handles the complexity of WebRTC connections, codec negotiation, and bandwidth adaptation while exposing a clean JavaScript API. It works seamlessly with major frameworks like React, Vue, and Angular, and includes first-class React Native support for mobile applications. For production deployments, Daily JS integrates with Daily's infrastructure which provides global media routing, TURN servers, and SFU (Selective Forwarding Unit) capabilities for scalable multi-party calls. The pre-built UI option allows rapid integration for standard use cases, while the Call Object API enables fully custom video experiences with complete control over rendering and UI design.