Skip to main content
This guide shows you how to create conferences, manage participants, and handle conference lifecycle events using the JitsiConference class.

Creating a Conference

1

Establish a connection

First, ensure you have an active connection:
const connection = new JitsiMeetJS.JitsiConnection(null, null, options);
connection.connect();
2

Create conference instance

Once connected, create a conference:
const conferenceOptions = {
    openBridgeChannel: true,
    p2p: {
        enabled: true,
        preferredCodec: 'VP9',
        backToP2PDelay: 5
    },
    startAudioMuted: 10,
    startVideoMuted: 10
};

const conference = connection.initJitsiConference(
    'room-name',
    conferenceOptions
);
3

Register event listeners

Attach handlers for conference events:
conference.on(JitsiMeetJS.events.conference.CONFERENCE_JOINED, onConferenceJoined);
conference.on(JitsiMeetJS.events.conference.USER_JOINED, onUserJoined);
conference.on(JitsiMeetJS.events.conference.USER_LEFT, onUserLeft);
conference.on(JitsiMeetJS.events.conference.TRACK_ADDED, onTrackAdded);
conference.on(JitsiMeetJS.events.conference.TRACK_REMOVED, onTrackRemoved);
4

Join the conference

Join the conference room:
conference.join();

Conference Configuration

Core Options

interface IConferenceOptions {
    config: {
        // P2P configuration
        p2p?: {
            enabled?: boolean;
            backToP2PDelay?: number;           // Delay before switching back to P2P (default: 5s)
            codecPreferenceOrder?: string[];    // e.g., ['VP9', 'VP8', 'H264']
            preferredCodec?: string;
        };
        
        // Start muted policies
        startAudioMuted?: number;              // Mute audio after N participants
        startVideoMuted?: number;              // Mute video after N participants
        
        // Quality settings
        channelLastN?: number;                 // Number of video streams to receive (-1 for all)
        startLastN?: number;                   // Initial lastN value
        
        // E2EE
        e2eping?: {
            enabled?: boolean;
        };
        
        // Statistics
        statisticsId?: string;                 // User identifier for stats
        statisticsDisplayName?: string;        // Display name for stats
    };
}

Video Quality Configuration

const conferenceOptions = {
    videoQuality: {
        codecPreferenceOrder: ['VP9', 'VP8', 'H264'],
        preferredCodec: 'VP9',
        screenshareCodec: 'VP9',
        enableAdaptiveMode: true,
        disabledCodec: 'H264'
    }
};

Event Handling

function onConferenceJoined() {
    console.log('Conference joined!');
    console.log('Participant ID:', conference.myUserId());
    
    // Add local tracks
    localTracks.forEach(track => {
        conference.addTrack(track);
    });
}

Managing Participants

Get Participant Information

// Get all participants
const participants = conference.getParticipants();

// Get specific participant
const participant = conference.getParticipantById(participantId);

// Get participant properties
const displayName = participant.getDisplayName();
const role = participant.getRole();
const status = participant.getStatus();

Participant Roles

// Check if participant is moderator
if (participant.isModerator()) {
    console.log('Participant is a moderator');
}

// Check if participant is hidden (e.g., recorder)
if (participant.isHidden()) {
    console.log('Participant is hidden');
}

// Grant moderator role (requires moderator privileges)
conference.grantOwner(participantId);

// Kick participant (requires moderator privileges)
conference.kickParticipant(participantId);

Managing Tracks

Add Local Tracks

// Create local tracks
JitsiMeetJS.createLocalTracks({
    devices: ['audio', 'video']
}).then(tracks => {
    tracks.forEach(track => {
        // Add track to conference
        conference.addTrack(track).catch(error => {
            console.error('Failed to add track:', error);
        });
    });
});

Remove Tracks

// Remove specific track
conference.removeTrack(localVideoTrack).then(() => {
    localVideoTrack.dispose();
}).catch(error => {
    console.error('Failed to remove track:', error);
});

Replace Tracks

// Replace video track (e.g., switch camera)
conference.replaceTrack(oldVideoTrack, newVideoTrack).then(() => {
    console.log('Track replaced successfully');
    oldVideoTrack.dispose();
}).catch(error => {
    console.error('Failed to replace track:', error);
});

Audio/Video Moderation

Mute/Unmute Participants

// Mute participant's audio (requires moderator)
conference.muteParticipant(participantId, MediaType.AUDIO);

// Request participant to unmute
conference.sendCommandOnce('unmute-request', {
    attributes: { participantId }
});

Handle Mute Events

conference.on(
    JitsiMeetJS.events.conference.TRACK_MUTE_CHANGED,
    (track) => {
        if (track.isMuted()) {
            console.log('Track muted:', track.getType());
        } else {
            console.log('Track unmuted:', track.getType());
        }
    }
);

Dominant Speaker

Track Dominant Speaker

conference.on(
    JitsiMeetJS.events.conference.DOMINANT_SPEAKER_CHANGED,
    (participantId) => {
        console.log('Dominant speaker:', participantId);
        
        const participant = conference.getParticipantById(participantId);
        if (participant) {
            console.log('Name:', participant.getDisplayName());
        }
    }
);

Last N

Control how many video streams to receive:
// Receive up to 5 video streams
conference.setLastN(5);

// Receive all video streams
conference.setLastN(-1);

// Get current lastN value
const lastN = conference.getLastN();

Properties and Commands

Set Local Properties

// Set display name
conference.setDisplayName('John Doe');

// Set custom property
conference.setLocalParticipantProperty('region', 'us-west');
conference.setLocalParticipantProperty('avatarUrl', 'https://...');

Send Commands

// Send custom command
conference.sendCommand('custom-action', {
    attributes: { data: 'value' },
    value: 'message'
});

// Send command once
conference.sendCommandOnce('one-time-action', {
    attributes: { key: 'value' }
});

// Receive commands
conference.addCommandListener('custom-action', (data, from) => {
    console.log('Command from:', from);
    console.log('Data:', data);
});

Leaving a Conference

1

Remove local tracks

localTracks.forEach(track => {
    conference.removeTrack(track);
    track.dispose();
});
2

Leave the conference

conference.leave().then(() => {
    console.log('Left conference successfully');
}).catch(error => {
    console.error('Error leaving conference:', error);
});
3

Clean up listeners

conference.off(JitsiMeetJS.events.conference.CONFERENCE_JOINED, onConferenceJoined);
conference.off(JitsiMeetJS.events.conference.USER_JOINED, onUserJoined);
// ... remove other listeners

Advanced Features

Sender Video Constraints

Set constraints for video encoding:
conference.setSenderVideoConstraint(720); // Set max resolution to 720p

Receiver Video Constraints

Control received video quality per participant:
const constraints = {
    'participant-id-1': { maxHeight: 720 },
    'participant-id-2': { maxHeight: 180 }
};

conference.setReceiverConstraints(constraints);

Speaker Statistics

Get speaking time statistics:
conference.on(
    JitsiMeetJS.events.conference.SPEAKER_STATS_RECEIVED,
    (stats) => {
        // stats is an object with participant IDs as keys
        Object.keys(stats).forEach(participantId => {
            const { displayName, totalDominantSpeakerTime } = stats[participantId];
            console.log(`${displayName} spoke for ${totalDominantSpeakerTime}ms`);
        });
    }
);

Next Steps

Handling Media Tracks

Learn about track management

Screen Sharing

Enable screen sharing in your app

Build docs developers (and LLMs) love