Class

VideoSentimentAnalyzer

VideoSentimentAnalyzer()

VideoSentimentAnalyzer - Real-time emotion detection for video elements using face-api.js

This module provides two classes for analyzing emotions in video streams:

  • VideoSentimentAnalyzer: For analyzing a single video element
  • MultiVideoSentimentManager: For managing emotion analysis across multiple video elements

Features:

  • Lazy loading of face-api.js library (only loads when needed)
  • Real-time emotion detection with configurable intervals
  • Emotion smoothing to reduce flickering
  • Multiple participant detection per video
  • Event-driven architecture for easy integration
  • Configurable confidence thresholds and model parameters
Constructor

# new VideoSentimentAnalyzer()

Examples
// Basic single video analysis
import { VideoSentimentAnalyzer } from 'muziertcclient';

const analyzer = new VideoSentimentAnalyzer({
  detectionInterval: 200,  // Check every 200ms
  minConfidence: 0.6,     // Higher confidence threshold
  smoothing: true         // Enable emotion smoothing
});

// Initialize (loads face-api.js and models)
await analyzer.initialize();

// Listen for emotion events with engagement
analyzer.addEventListener('emotions', (event) => {
  const { videoId, participants, summary, timestamp } = event.detail;
  console.log(`${videoId}: ${summary}`);
  
  participants.forEach(participant => {
    const { dominantEmotion, confidence, boundingBox, engagement } = participant;
    console.log(`Participant ${participant.id}: ${dominantEmotion.emotion} (${Math.round(confidence * 100)}%)`);
    console.log(`Engagement: ${Math.round(engagement * 100)}%`);
  });
});

// Listen for loading events
analyzer.addEventListener('loading', (event) => {
  console.log('Loading:', event.detail.status);
});

analyzer.addEventListener('ready', (event) => {
  console.log('Ready:', event.detail.status);
});

analyzer.addEventListener('error', (event) => {
  console.error('Error:', event.detail.error);
});

// Start analyzing a video element
const videoElement = document.getElementById('localVideo');
await analyzer.analyzeVideo(videoElement, 'local-participant');

// Stop analysis when done
analyzer.stopAnalysis();
// Multiple video management with lazy loading
import { loadMultiVideoSentimentManager } from 'muziertcclient';

// Lazy load the sentiment analysis classes (face-api.js only loads here)
const MultiVideoSentimentManager = await loadMultiVideoSentimentManager();

const manager = new MultiVideoSentimentManager({
  detectionInterval: 150,
  minConfidence: 0.5,
  smoothing: true,
  smoothingFactor: 0.8,
  debug: true,
  engagementWeights: {
    faceDetection: 0.4,      // Emphasize face detection
    eyeOpenness: 0.3,        // Emphasize alertness
    headPose: 0.15,          // De-emphasize head pose
    emotionalExpression: 0.1,
    faceSize: 0.05
  }
});

// Initialize once for all videos
await manager.initialize();

// Listen for emotions from any video
manager.addEventListener('emotions', (event) => {
  const { videoId, participants, summary } = event.detail;
  updateUI(videoId, participants, summary);
});

// Add multiple videos for analysis
await manager.addVideo('participant-1', videoElement1);
await manager.addVideo('participant-2', videoElement2);
await manager.addVideo('participant-3', videoElement3);

// Remove specific video when participant leaves
manager.removeVideo('participant-2');

// Clean up all resources
manager.destroy();
// Integration with WebRTC/video conferencing
import { Client, loadMultiVideoSentimentManager } from 'muziertcclient';

class VideoConferenceWithEmotions {
  constructor() {
    this.client = new Client();
    this.sentimentManager = null;
  }

  async enableEmotionDetection() {
    if (!this.sentimentManager) {
      const MultiVideoSentimentManager = await loadMultiVideoSentimentManager();
      this.sentimentManager = new MultiVideoSentimentManager({
        detectionInterval: 200,
        minConfidence: 0.6
      });
      
      await this.sentimentManager.initialize();
      
      this.sentimentManager.addEventListener('emotions', (event) => {
        this.handleEmotionUpdate(event.detail);
      });
    }
  }

  async addParticipant(userId, videoElement) {
    if (this.sentimentManager) {
      await this.sentimentManager.addVideo(userId, videoElement);
    }
  }

  removeParticipant(userId) {
    if (this.sentimentManager) {
      this.sentimentManager.removeVideo(userId);
    }
  }

  handleEmotionUpdate({ videoId, participants, summary }) {
    // Update participant mood indicators
    participants.forEach(participant => {
      const moodElement = document.querySelector(`[data-participant="${videoId}"] .mood-indicator`);
      if (moodElement) {
        moodElement.textContent = participant.dominantEmotion.emotion;
        moodElement.className = `mood-indicator mood-${participant.dominantEmotion.emotion}`;
      }
    });

    // Update room mood summary
    const roomMoodElement = document.querySelector('.room-mood');
    if (roomMoodElement) {
      roomMoodElement.textContent = summary;
    }
  }
}
// Advanced configuration and custom model URLs
const analyzer = new VideoSentimentAnalyzer({
  detectionInterval: 100,           // Detect every 100ms (faster)
  modelUrl: '/models/face-api',     // Custom model path
  minConfidence: 0.7,               // Higher confidence threshold
  smoothing: true,                  // Enable smoothing
  smoothingFactor: 0.6,             // Less aggressive smoothing
});

// Handle all possible events
analyzer.addEventListener('loading', ({ detail }) => {
  showLoadingIndicator(detail.status);
});

analyzer.addEventListener('ready', ({ detail }) => {
  hideLoadingIndicator();
  console.log('Sentiment analysis ready');
});

analyzer.addEventListener('emotions', ({ detail }) => {
  const { videoId, participants, summary, timestamp } = detail;
  
  // Log detailed emotion data
  participants.forEach(participant => {
    const allEmotions = Object.entries(participant.emotions)
      .map(([emotion, score]) => `${emotion}: ${Math.round(score * 100)}%`)
      .join(', ');
    
    console.log(`Participant ${participant.id} emotions: ${allEmotions}`);
    console.log(`Bounding box:`, participant.boundingBox);
  });
});

analyzer.addEventListener('error', ({ detail }) => {
  console.error('Sentiment analysis error:', detail.error);
  showErrorMessage('Emotion detection failed. Please check your camera.');
});

Classes

VideoSentimentAnalyzer

Methods

# async analyzeVideo(videoElement, videoId) → {Promise.<void>}

Start emotion detection on a video element

Parameters:
Name Type Description
videoElement HTMLVideoElement

The video element to analyze

videoId string

Unique identifier for this video

Promise.<void>

# calculateHeadPose()

Calculate head‑pose engagement (≈ looking‑at‑camera score) 1 → eyes centred on camera 0 → head turned / tilted significantly

# async canDetectFaces(videoElementopt) → {Object}

Check if face detection is possible with the current configuration

Parameters:
Name Type Attributes Default Description
videoElement HTMLVideoElement <optional>
null

Optional video element to test

Detection capability status

Object

# cleanupParticipant(participantVideoId)

Clean up all data associated with a specific participant/video Call this when a participant disconnects to prevent memory leaks

Parameters:
Name Type Description
participantVideoId string

The video ID of the participant to clean up

# destroy()

Clean up resources

# async detectEmotions()

Detect emotions in the current video frame Stable IDs + cleanup + safety checks

# getDiagnostics() → {Object}

Get diagnostic information about the analyzer state

Diagnostic information

Object

# getPerformanceStats() → {Object}

Get current performance statistics

Performance statistics

Object

# async initialize() → {Promise.<void>}

Initialize face-api models (lazy loads the library first)

Promise.<void>

# iou()

IoU of two rectangles

# matchFace()

return an id that is stable across frames

# setDebugMode(enabled)

Enable or disable debug mode

Parameters:
Name Type Description
enabled boolean

Whether to enable debug logging

# stopAnalysis()

Stop emotion detection

# validateVideoElement(videoElement)

Validate video element for face detection capability

Parameters:
Name Type Description
videoElement HTMLVideoElement

Video element to validate

If video element is not suitable for detection

Error