Skip to main content
Participants represent users in a LiveKit room. There are two types: LocalParticipant (the current user) and RemoteParticipant (other users).

Participant Base Class

Both local and remote participants inherit from the Participant class, which provides common properties and functionality.

Common Properties

participant.sid             // Server-assigned participant ID
participant.identity        // Unique participant identity
participant.name            // Participant display name
participant.metadata        // Custom metadata string
participant.attributes      // Key-value attributes [String: String]
participant.kind            // Participant kind (.standard, .agent, etc.)

// Audio state
participant.audioLevel      // Current audio level (0.0 to 1.0)
participant.isSpeaking      // Whether participant is currently speaking
participant.lastSpokeAt     // Date of last speech activity

// Connection
participant.state           // Participant state (.joining, .active, etc.)
participant.connectionQuality  // Connection quality (.excellent, .good, .poor)
participant.joinedAt        // Date participant joined the room

// Permissions
participant.permissions     // Participant permissions

// Tracks
participant.trackPublications  // All track publications [Track.Sid: TrackPublication]
participant.audioTracks        // Audio track publications only
participant.videoTracks        // Video track publications only

Convenience Methods

Check if specific tracks are enabled:
participant.isCameraEnabled()      // true if camera track is unmuted
participant.isMicrophoneEnabled()  // true if microphone track is unmuted
participant.isScreenShareEnabled() // true if screen share track is unmuted

Participant State

Participants go through different states:
public enum ParticipantState {
    case unknown
    case joining    // Participant is joining
    case active     // Participant is active in the room
    case disconnected  // Participant has disconnected
}

LocalParticipant

The LocalParticipant represents the current user. Access it through the room:
let localParticipant = room.localParticipant

Publishing Tracks

Publish camera, microphone, or custom tracks:
// Publish video track
let cameraTrack = LocalVideoTrack.createCameraTrack()
let publication = try await localParticipant.publish(videoTrack: cameraTrack)

// Publish audio track  
let audioTrack = LocalAudioTrack.createTrack()
let publication = try await localParticipant.publish(audioTrack: audioTrack)

Simplified Publishing API

Use convenience methods for common scenarios:
// Enable camera
try await localParticipant.setCamera(enabled: true)

// Enable microphone
try await localParticipant.setMicrophone(enabled: true)

// Enable screen share
try await localParticipant.setScreenShare(enabled: true)

// Disable camera (mutes the track)
try await localParticipant.setCamera(enabled: false)
With custom options:
let captureOptions = CameraCaptureOptions(
    position: .front,
    preferredFormat: .preset1920x1080
)

let publishOptions = VideoPublishOptions(
    preferredCodec: .vp8,
    simulcast: true
)

try await localParticipant.setCamera(
    enabled: true,
    captureOptions: captureOptions,
    publishOptions: publishOptions
)
The simplified API automatically creates tracks, manages their lifecycle, and handles mute/unmute operations.

Publishing Data

Send data messages to other participants:
let data = "Hello, world!".data(using: .utf8)!

// Send to all participants
try await localParticipant.publish(data: data)

// Send to specific participants
let options = DataPublishOptions(
    destinationIdentities: [participant1.identity!, participant2.identity!],
    topic: "chat"
)
try await localParticipant.publish(data: data, options: options)

Updating Metadata

Update participant metadata and attributes:
// Update metadata (requires CanUpdateOwnMetadata permission)
try await localParticipant.set(metadata: "{\"status\": \"online\"}")

// Update name
try await localParticipant.set(name: "John Doe")

// Update attributes
try await localParticipant.set(attributes: ["role": "moderator"])
Updating metadata and name requires the CanUpdateOwnMetadata permission encoded in the JWT token.

Track Subscription Permissions

Control who can subscribe to your published tracks:
// Allow all participants to subscribe to all tracks (default)
try await localParticipant.setTrackSubscriptionPermissions(
    allParticipantsAllowed: true
)

// Restrict subscriptions to specific participants/tracks
let permissions = [
    ParticipantTrackPermission(
        participantIdentity: "user123",
        allTracksAllowed: true
    ),
    ParticipantTrackPermission(
        participantIdentity: "user456",
        allowedTrackSids: [cameraPublication.sid]
    )
]

try await localParticipant.setTrackSubscriptionPermissions(
    allParticipantsAllowed: false,
    trackPermissions: permissions
)

Local Track Publications

Access published local tracks:
localParticipant.localAudioTracks  // [LocalTrackPublication]
localParticipant.localVideoTracks  // [LocalTrackPublication]

RemoteParticipant

Remote participants represent other users in the room. Access them through the room:
// Get all remote participants
let remoteParticipants = room.remoteParticipants  // [Identity: RemoteParticipant]

// Get specific participant by identity
if let participant = room.remoteParticipants[identity] {
    print("Found participant: \(participant.name ?? "unknown")")
}

Observing Remote Participants

Monitor when participants join or leave:
class MyRoomDelegate: RoomDelegate {
    func room(_ room: Room, participantDidJoin participant: RemoteParticipant) {
        print("\(participant.identity!) joined")
        
        // Add delegate to receive participant events
        participant.delegates.add(delegate: self)
    }
    
    func room(_ room: Room, participantDidDisconnect participant: RemoteParticipant) {
        print("\(participant.identity!) disconnected")
    }
}

Remote Track Publications

Remote participants publish tracks that you can subscribe to:
func room(_ room: Room, participant: RemoteParticipant, didPublishTrack publication: RemoteTrackPublication) {
    print("\(participant.identity!) published \(publication.source) track")
    
    // Auto-subscribe is enabled by default
    // Manually subscribe if needed:
    // try await publication.set(subscribed: true)
}

func room(_ room: Room, participant: RemoteParticipant, didSubscribeTrack publication: RemoteTrackPublication) {
    print("Subscribed to \(publication.name)")
    
    if let videoTrack = publication.track as? VideoTrack {
        videoTrack.add(videoRenderer: videoView)
    } else if let audioTrack = publication.track as? AudioTrack {
        // Audio tracks are automatically played
    }
}

Participant Events

Implement ParticipantDelegate to receive participant-specific events:
@objc protocol ParticipantDelegate {
    // Metadata events
    optional func participant(_ participant: Participant, didUpdateMetadata metadata: String)
    optional func participant(_ participant: Participant, didUpdateName name: String)
    optional func participant(_ participant: Participant, didUpdateAttributes attributes: AttributesDiff)
    
    // Speaking events
    optional func participant(_ participant: Participant, didUpdateIsSpeaking isSpeaking: Bool)
    
    // Connection events
    optional func participant(_ participant: Participant, didUpdateState state: ParticipantState)
    optional func participant(_ participant: Participant, didUpdateConnectionQuality quality: ConnectionQuality)
    
    // Permission events
    optional func participant(_ participant: LocalParticipant, didUpdatePermissions permissions: ParticipantPermissions)
    
    // Track events
    optional func participant(_ participant: Participant, didPublishTrack publication: TrackPublication)
    optional func participant(_ participant: Participant, didUnpublishTrack publication: TrackPublication)
    optional func participant(_ participant: RemoteParticipant, didSubscribeTrack publication: RemoteTrackPublication)
    optional func participant(_ participant: RemoteParticipant, didUnsubscribeTrack publication: RemoteTrackPublication)
    
    // Track state events
    optional func participant(_ participant: Participant, trackPublication: TrackPublication, didUpdateIsMuted isMuted: Bool)
    optional func participant(_ participant: RemoteParticipant, trackPublication: RemoteTrackPublication, didUpdateStreamState streamState: StreamState)
}

Example Usage

class ParticipantObserver: ParticipantDelegate {
    func participant(_ participant: Participant, didUpdateIsSpeaking isSpeaking: Bool) {
        if isSpeaking {
            print("\(participant.identity!) started speaking")
            // Update UI to highlight speaking participant
        }
    }
    
    func participant(_ participant: Participant, didUpdateConnectionQuality quality: ConnectionQuality) {
        switch quality {
        case .poor:
            print("\(participant.identity!) has poor connection")
        case .excellent:
            print("\(participant.identity!) has excellent connection")
        default:
            break
        }
    }
}

Participant Permissions

Access participant permissions:
let permissions = participant.permissions

if permissions.canPublish {
    print("Can publish tracks")
}

if permissions.canSubscribe {
    print("Can subscribe to tracks")
}

if permissions.canPublishData {
    print("Can send data messages")
}

// Check specific source permissions
let canPublishSources = permissions.canPublishSources  // [String]
if canPublishSources.contains(Track.Source.camera.rawValue) {
    print("Can publish camera")
}

Active Speakers

Get the list of currently active speakers:
let activeSpeakers = room.activeSpeakers  // [Participant]

for speaker in activeSpeakers {
    print("\(speaker.identity!) is speaking (level: \(speaker.audioLevel))")
}
Monitor active speaker changes:
func room(_ room: Room, didUpdateSpeakingParticipants participants: [Participant]) {
    // Update UI to show active speakers
    for participant in participants {
        if participant.isSpeaking {
            highlightParticipant(participant)
        }
    }
}

Best Practices

  1. Add delegates early: Attach participant delegates when participants join to avoid missing events
  2. Handle disconnections: Clean up UI state when participants disconnect
  3. Check permissions: Verify permissions before attempting operations
  4. Monitor connection quality: Provide user feedback for poor connections
  5. Use identity for identification: Don’t rely on array indices, use participant identity as the unique identifier

Build docs developers (and LLMs) love