npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@timadey/proctor

v1.2.4

Published

AI-powered exam proctoring with MediaPipe - JavaScript library

Readme

🎓 AI Proctoring Engine

License: MIT TypeScript MediaPipe

A comprehensive, modular, and production-ready AI-powered exam proctoring system with advanced behavioral pattern detection. Built with a decoupled architecture for maximum flexibility and extensibility.

🌟 Features

Visual Monitoring

  • Face Detection - Detect no face, single face, or multiple faces
  • Gaze Tracking - Iris-based eye tracking to detect looking away
  • Head Pose Estimation - Track head rotation (yaw, pitch, roll)
  • Mouth Movement Detection - Detect talking and whispering
  • Mouth Covering Detection - Identify attempts to hide mouth
  • Suspicious Object Detection - Detect phones, books, laptops, tablets

Audio Monitoring

  • Talking Detection - Identify normal speech above threshold
  • Whispering Detection - Detect subtle audio below talking threshold
  • Audio Level Tracking - Continuous audio level monitoring
  • Silence Detection - Track periods of silence
  • Duration Tracking - Measure total talking/whispering time

Behavioral Pattern Detection

  • Multi-Modal Analysis - Correlate visual + audio events
  • Time-Windowed Patterns - Detect patterns within configurable windows
  • Suspicious Correlations - Identify cheating behaviors:
    • Looking away + talking + mouth moving
    • Looking left/right + whispering
    • Mouth covered + audio detected
    • Suspicious object + looking away
    • Multiple faces + audio detected
    • Rapid eye movements (reading notes)
    • Repeated tab switches

Browser Telemetry

  • Tab Switch Detection - Monitor when student leaves exam tab
  • Focus Tracking - Detect window focus loss/gain
  • Clipboard Monitoring - Track copy/paste/cut attempts
  • Key Press Detection - Identify suspicious keyboard shortcuts
  • Fullscreen Monitoring - Detect fullscreen exit
  • Mouse Tracking - Monitor mouse leaving window

Architecture

  • Modular Design - Independent, swappable modules
  • Event-Driven - Reactive architecture with event system
  • State Management - Centralized state with subscription system
  • Configurable - Extensive configuration options
  • Extensible - Easy to add custom patterns and modules
  • Model Cache - Models are cached for faster loading

📦 Installation

npm install @timadey/proctor

Or with yarn:

yarn add @timadey/proctor

🚀 Quick Start

import { ProctoringEngine } from '@timadey/proctor';

// 1. Initialize the engine
const engine = ProctoringEngine.getInstance({
    // Enable/disable modules
    enableVisualDetection: true,
    enableAudioMonitoring: true,
    enablePatternDetection: true,
    enableBrowserTelemetry: true,
    
    // Callbacks
    onEvent: (event) => {
        console.log('Proctoring event:', event);
        // Send to your backend
    },
    
    onBehavioralPattern: (pattern) => {
        console.warn('Suspicious pattern detected:', pattern);
        // Alert supervisor
    }
});

// 2. Initialize modules
await engine.initialize();

// 3. Start proctoring
const videoElement = document.getElementById('webcam');
engine.start(videoElement);

// 4. Get session summary anytime
const summary = engine.getSessionSummary();
console.log('Suspicious score:', summary.suspiciousScore);

// 5. Stop when exam ends
engine.stop();

📚 Table of Contents

🏗️ Architecture

The system uses a decoupled, modular architecture where each component operates independently:

┌────────────────────────────────────────┐
│      ProctoringEngine (Orchestrator)    │
│  ┌─────────────┐  ┌──────────────┐    │
│  │EventManager │  │StateManager  │    │
│  └─────────────┘  └──────────────┘    │
└────────────┬───────────────────────────┘
             │
    ┌────────┼────────┬────────┐
    ▼        ▼        ▼        ▼
┌────────┐ ┌────┐ ┌────┐ ┌────────┐
│Visual  │ │Audio│ │Pat │ │Browser │
│Module  │ │Mod  │ │Mod │ │Telemetry│
└────────┘ └────┘ └────┘ └────────┘

Core Components

  1. ProctoringEngine - Main orchestrator coordinating all modules
  2. VisualDetectionModule - Computer vision and face tracking
  3. AudioMonitoringModule - Audio analysis and detection
  4. PatternDetectionModule - Behavioral pattern recognition
  5. BrowserTelemetryModule - Browser interaction monitoring
  6. EventManager - Centralized event handling and logging
  7. StateManager - Application state management

🧩 Modules

VisualDetectionModule

Handles all computer vision tasks using MediaPipe.

// Access directly if needed
const visualState = engine.visualModule.getState();
console.log('Current gaze:', visualState.currentGazeDirection);
console.log('Number of faces:', visualState.numFaces);
console.log('Mouth moving:', visualState.isMouthMoving);
console.log('Object detected:', visualState.suspiciousObjectDetected);

Features:

  • Face landmarking with 468 facial points
  • Real-time gaze direction (left, right, up, down, center)
  • Head pose angles (yaw, pitch, roll)
  • Mouth aspect ratio for speech detection
  • Object detection for unauthorized materials

AudioMonitoringModule

Monitors audio using Web Audio API.

// Access audio state
const audioState = engine.audioModule.getState();
console.log('Is talking:', audioState.isTalking);
console.log('Is whispering:', audioState.isWhispering);
console.log('Audio level:', audioState.currentAudioLevel, 'dB');
console.log('Total talking time:', audioState.totalTalkingDuration, 'ms');

Features:

  • RMS (Root Mean Square) audio level calculation
  • Talking detection (configurable threshold)
  • Whispering detection (lower threshold)
  • Audio level history tracking
  • Silence duration tracking

PatternDetectionModule

Detects suspicious behavioral patterns through correlation.

// Get pattern summary
const patterns = engine.patternModule.getPatternSummary();
console.log('Suspicious patterns detected:', patterns);

Detected Patterns:

  • suspiciousTriplePattern - Looking away + talking + mouth moving
  • lookingLeftWhispering - Looking left while whispering
  • lookingRightWhispering - Looking right while whispering
  • mouthCoveredWithAudio - Mouth covered while audio detected
  • lookingAwayAndTalking - Looking away while talking
  • objectAndLookingAway - Suspicious object + looking away
  • multipleFacesWithAudio - Multiple people + audio
  • headTurnedTalking - Head turned + talking

BrowserTelemetryModule

Monitors all browser interactions.

// Get telemetry summary
const telemetry = engine.telemetryModule.getSummary();
console.log('Tab switches:', telemetry.tabSwitches);
console.log('Copy attempts:', telemetry.copyAttempts);
console.log('Paste attempts:', telemetry.pasteAttempts);

Monitored Actions:

  • Tab visibility changes
  • Window focus/blur events
  • Copy/paste/cut operations
  • Suspicious keyboard shortcuts (F12, Ctrl+C, etc.)
  • Fullscreen changes
  • Right-click attempts
  • Mouse leaving window

⚙️ Configuration

Complete Configuration Example

const engine = ProctoringEngine.getInstance({
    // ===== Module Toggles =====
    enableVisualDetection: true,
    enableAudioMonitoring: true,
    enablePatternDetection: true,
    enableBrowserTelemetry: true,
    
    // ===== Visual Detection Options =====
    detectionFPS: 10,                     // Frame processing rate (5-30)
    stabilityFrames: 15,                  // Frames before event triggers
    gazeThreshold: 20,                    // Degrees for gaze deviation
    yawThreshold: 25,                     // Degrees for head rotation
    pitchThreshold: 20,                   // Degrees for head tilt
    prolongedGazeAwayDuration: 5000,      // ms for prolonged gaze
    mouthOpenRatioThreshold: 0.15,        // Mouth aspect ratio threshold
    
    // ===== Audio Monitoring Options =====
    talkingThreshold: -45,                // dB for talking detection
    whisperThreshold: -55,                // dB for whisper detection
    audioSampleInterval: 100,             // Audio check interval (ms)
    prolongedTalkingDuration: 3000,       // ms for prolonged talking
    
    // ===== Pattern Detection Options =====
    suspiciousPatternThreshold: 3,        // Events to trigger pattern
    patternDetectionWindow: 10000,        // Time window (ms)
    
    // ===== Callbacks =====
    onEvent: (event) => {
        // Handle individual events
        console.log('Event:', event);
    },
    
    onBehavioralPattern: (pattern) => {
        // Handle detected patterns (critical)
        console.warn('Pattern:', pattern);
    },
    
    onStatusChange: (status) => {
        // Engine status: 'initializing', 'loading-models', 'ready', 'error'
        console.log('Status:', status);
    },
    
    onError: (error) => {
        // Handle errors
        console.error('Error:', error);
    }
});

Selective Module Configuration

// Lightweight mode - only browser telemetry
const lightEngine = ProctoringEngine.getInstance({
    enableVisualDetection: false,
    enableAudioMonitoring: false,
    enablePatternDetection: false,
    enableBrowserTelemetry: true
});

// Heavy mode - full monitoring
const heavyEngine = ProctoringEngine.getInstance({
    enableVisualDetection: true,
    enableAudioMonitoring: true,
    enablePatternDetection: true,
    enableBrowserTelemetry: true,
    detectionFPS: 15  // Higher FPS for more accuracy
});

// Custom mode - visual + patterns only
const customEngine = ProctoringEngine.getInstance({
    enableVisualDetection: true,
    enableAudioMonitoring: false,
    enablePatternDetection: true,
    enableBrowserTelemetry: true
});

Runtime Configuration Updates

// Update configuration during session
engine.updateOptions({
    gazeThreshold: 30,        // More lenient
    detectionFPS: 5           // Reduce CPU usage
});

// Update individual modules
engine.visualModule.updateOptions({
    detectionFPS: 8
});

engine.audioModule.updateOptions({
    talkingThreshold: -40
});

📡 Events

Event Structure

{
    event: 'TALKING_DETECTED',      // Event type
    lv: 8,                          // Severity level (1-10)
    ts: 1703098765432,              // Timestamp (Unix ms)
    source: 'audio',                // Module source
    sessionDuration: 123456,        // Time since session start (ms)
    
    // Event-specific metadata
    duration: 5000,                 // Duration of behavior (ms)
    level: -40,                     // Audio level (dB)
    direction: 'left',              // Direction (for gaze/head)
    severity: 'high',               // Human-readable severity
    extractedFeatures: {}           // The face and hand features extracted from the frame
}

Event Types by Severity

Critical (9-10)

  • NO_FACE - No face detected
  • MULTIPLE_FACES - Multiple people in frame
  • PERSON_LEFT - Student left for extended period
  • SUSPICIOUS_OBJECT - Unauthorized object detected
  • TAB_SWITCHED - Tab switch detected
  • PASTE_ATTEMPT - Paste operation
  • PATTERN_* - Behavioral pattern detected

High (7-8)

  • GAZE_AWAY - Looking away from screen
  • PROLONGED_GAZE_AWAY - Extended gaze away
  • HEAD_TURNED - Head significantly rotated
  • PROLONGED_MOUTH_MOVEMENT - Extended mouth movement
  • TALKING_DETECTED - Speech detected
  • WHISPERING_DETECTED - Whispering detected
  • WINDOW_FOCUS_LOST - Window lost focus
  • EXITED_FULLSCREEN - Fullscreen exited
  • COPY_ATTEMPT - Copy operation

Medium (5-6)

  • MOUTH_MOVING - Mouth movement detected
  • MOUTH_COVERED - Mouth appears covered
  • EYES_OFF_SCREEN - Eyes looking off-screen
  • RIGHT_CLICK - Right-click attempt
  • SUSPICIOUS_KEY_PRESS - Suspicious keyboard shortcut

Low (1-4)

  • MOUSE_LEFT_WINDOW - Mouse left window
  • WINDOW_FOCUS_RESTORED - Focus restored
  • TAB_RETURNED - Returned to tab

🎯 Behavioral Patterns

Patterns are critical alerts indicating high probability of cheating.

Pattern: Suspicious Triple Pattern

When: Student is looking away + talking + mouth moving simultaneously
Severity: 10 (Critical)
Interpretation: Likely communicating with someone off-screen

onBehavioralPattern: (pattern) => {
    if (pattern.pattern === 'suspiciousTriplePattern') {
        // This is extremely suspicious
        alertSupervisor('Student likely cheating');
        flagExamForReview();
    }
}

Pattern: Looking Left/Right + Whispering

When: Student looking to side while whispering
Severity: 8 (High)
Interpretation: Possibly communicating with nearby person

Pattern: Mouth Covered + Audio

When: Mouth covered but audio detected
Severity: 9 (High)
Interpretation: Attempting to hide speaking

Pattern: Object + Looking Away

When: Suspicious object detected + looking away from screen
Severity: 9 (High)
Interpretation: Using unauthorized materials

Pattern: Multiple Faces + Audio

When: Multiple people + audio detected
Severity: 10 (Critical)
Interpretation: Multiple people taking exam together

Custom Pattern Detection

Add your own patterns:

// Add custom pattern
engine.patternModule.patterns.myCustomPattern = {
    name: 'myCustomPattern',
    severity: 8,
    events: [],
    count: 0,
    lastTriggered: 0,
    check: (visualState, audioState) => {
        // Your custom logic
        return visualState.numFaces === 0 && 
               audioState.isTalking;
    }
};

📖 API Reference

ProctoringEngine

getInstance(options)

Get singleton instance.

const engine = ProctoringEngine.getInstance(options);

async initialize()

Initialize all enabled modules.

await engine.initialize();

start(videoElement)

Start proctoring with video element.

const video = document.getElementById('webcam');
engine.start(video);

stop()

Stop proctoring.

engine.stop();

updateOptions(options)

Update configuration at runtime.

engine.updateOptions({ detectionFPS: 5 });

getSessionSummary()

Get comprehensive session summary.

const summary = engine.getSessionSummary();
/*
{
    sessionDuration: 1800000,
    sessionStartTime: 1703098765432,
    sessionEndTime: 1703100565432,
    totalEvents: 45,
    eventCounts: {...},
    eventsBySeverity: {...},
    patterns: {...},
    visualState: {...},
    audioState: {...},
    suspiciousScore: 127
}
*/

getLogs()

Get all event logs.

const logs = engine.getLogs();

clearLogs()

Clear all logs and patterns.

engine.clearLogs();

calculateSuspiciousScore()

Calculate overall suspicious score (0-1000).

const score = engine.calculateSuspiciousScore();
// 0-50: Normal
// 51-100: Some suspicious activity
// 101-200: Concerning behavior
// 201+: High probability of cheating

destroy()

Cleanup and destroy engine.

engine.destroy();

StateManager

subscribe(callback)

Subscribe to state changes.

const unsubscribe = engine.stateManager.subscribe((state) => {
    console.log('State updated:', state);
});

// Unsubscribe later
unsubscribe();

getCompleteState()

Get complete current state.

const state = engine.stateManager.getCompleteState();

getVisualState()

Get visual detection state.

const visual = engine.stateManager.getVisualState();

getAudioState()

Get audio monitoring state.

const audio = engine.stateManager.getAudioState();

EventManager

getAllEvents()

Get all recorded events.

const events = engine.eventManager.getAllEvents();

getEventsByType(type)

Get events of specific type.

const tabSwitches = engine.eventManager.getEventsByType('TAB_SWITCHED');

getEventsBySeverity(minSeverity)

Get events above severity threshold.

const critical = engine.eventManager.getEventsBySeverity(9);

getSummary()

Get event summary statistics.

const summary = engine.eventManager.getSummary();

💡 Examples

Basic Exam Proctoring

import { ProctoringEngine } from './ProctoringEngine.js';

class SimpleProctor {
    constructor() {
        this.engine = ProctoringEngine.getInstance({
            onEvent: (e) => console.log('Event:', e.event),
            onBehavioralPattern: (p) => alert(`Warning: ${p.pattern}`)
        });
    }

    async start() {
        // Get camera
        const stream = await navigator.mediaDevices.getUserMedia({
            video: true
        });
        const video = document.getElementById('video');
        video.srcObject = stream;
        await video.play();

        // Start proctoring
        await this.engine.initialize();
        this.engine.start(video);
    }

    stop() {
        const summary = this.engine.getSessionSummary();
        console.log('Final score:', summary.suspiciousScore);
        this.engine.stop();
    }
}

const proctor = new SimpleProctor();
await proctor.start();

Advanced Integration with Backend

class AdvancedProctor {
    constructor(examId, studentId) {
        this.examId = examId;
        this.studentId = studentId;
        this.ws = null;  // WebSocket connection
        
        this.engine = ProctoringEngine.getInstance({
            onEvent: (event) => this.handleEvent(event),
            onBehavioralPattern: (pattern) => this.handlePattern(pattern)
        });
    }

    async start() {
        // Connect to backend via WebSocket
        this.ws = new WebSocket('wss://api.example.com/proctoring');
        
        // Setup camera
        const stream = await navigator.mediaDevices.getUserMedia({
            video: { width: 1280, height: 720 }
        });
        const video = document.getElementById('video');
        video.srcObject = stream;
        await video.play();

        // Initialize and start
        await this.engine.initialize();
        this.engine.start(video);

        // Subscribe to state for real-time updates
        this.engine.stateManager.subscribe((state) => {
            this.sendStateUpdate(state);
        });

        // Periodic summaries
        this.summaryInterval = setInterval(() => {
            this.sendSummary();
        }, 30000);  // Every 30 seconds
    }

    handleEvent(event) {
        // Send event to backend via REST
        fetch('/api/proctoring/event', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                examId: this.examId,
                studentId: this.studentId,
                event: event
            })
        });

        // Send via WebSocket for real-time monitoring
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify({
                type: 'EVENT',
                data: event
            }));
        }

        // Show to student if critical
        if (event.lv >= 8) {
            this.showWarning(event.event);
        }
    }

    handlePattern(pattern) {
        // Critical alert - send immediately
        fetch('/api/proctoring/critical', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                examId: this.examId,
                studentId: this.studentId,
                pattern: pattern,
                timestamp: Date.now()
            }),
            keepalive: true  // Ensure delivery
        });

        // Alert supervisor via WebSocket
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify({
                type: 'CRITICAL_PATTERN',
                data: pattern
            }));
        }

        // Show strong warning to student
        this.showCriticalWarning(pattern.pattern);
    }

    sendStateUpdate(state) {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify({
                type: 'STATE_UPDATE',
                data: {
                    examId: this.examId,
                    studentId: this.studentId,
                    state: state
                }
            }));
        }
    }

    sendSummary() {
        const summary = this.engine.getSessionSummary();
        
        fetch('/api/proctoring/summary', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                examId: this.examId,
                studentId: this.studentId,
                summary: summary
            })
        });
    }

    showWarning(eventType) {
        const warnings = {
            'TALKING_DETECTED': 'Please remain quiet during the exam.',
            'TAB_SWITCHED': 'Do not switch tabs during the exam.',
            'MULTIPLE_FACES': 'Multiple people detected. Only you should be visible.',
            'PERSON_LEFT': 'You have left the exam area.',
        };

        const message = warnings[eventType] || 'Suspicious activity detected.';
        const warning = document.getElementById('warning');
        warning.textContent = message;
        warning.classList.add('show');
        
        setTimeout(() => {
            warning.classList.remove('show');
        }, 5000);
    }

    showCriticalWarning(patternName) {
        const modal = document.getElementById('critical-modal');
        modal.querySelector('.message').textContent = 
            `Critical violation detected: ${patternName}. This exam may be flagged for review.`;
        modal.classList.add('show');
        
        setTimeout(() => {
            modal.classList.remove('show');
        }, 10000);
    }

    async stop() {
        // Clear interval
        if (this.summaryInterval) {
            clearInterval(this.summaryInterval);
        }

        // Get final data
        const summary = this.engine.getSessionSummary();
        const logs = this.engine.getLogs();

        // Send final report
        const finalData = {
            examId: this.examId,
            studentId: this.studentId,
            summary: summary,
            logs: logs,
            endTime: Date.now()
        };

        // Use sendBeacon for reliability during unload
        const blob = new Blob([JSON.stringify(finalData)], {
            type: 'application/json'
        });
        navigator.sendBeacon('/api/proctoring/finalize', blob);

        // Close WebSocket
        if (this.ws) {
            this.ws.close();
        }

        // Destroy engine
        this.engine.destroy();
    }
}

// Usage
const proctor = new AdvancedProctor('exam-123', 'student-456');
await proctor.start();

// On exam submit
document.getElementById('submit-btn').addEventListener('click', async () => {
    await proctor.stop();
    // Submit exam answers...
});

// On page unload
window.addEventListener('beforeunload', () => {
    proctor.stop();
});

React Integration

import React, { useEffect, useRef, useState } from 'react';
import { ProctoringEngine } from './ProctoringEngine';

function ExamProctoring({ examId, studentId }) {
    const videoRef = useRef(null);
    const engineRef = useRef(null);
    
    const [status, setStatus] = useState('initializing');
    const [events, setEvents] = useState([]);
    const [score, setScore] = useState(0);
    const [warning, setWarning] = useState('');

    useEffect(() => {
        let mounted = true;

        const initialize = async () => {
            try {
                // Setup camera
                const stream = await navigator.mediaDevices.getUserMedia({
                    video: { width: 1280, height: 720 }
                });
                
                if (videoRef.current) {
                    videoRef.current.srcObject = stream;
                    await videoRef.current.play();
                }

                // Initialize engine
                const engine = ProctoringEngine.getInstance({
                    onEvent: (event) => {
                        if (mounted) {
                            setEvents(prev => [event, ...prev].slice(0, 20));
                        }
                    },
                    onBehavioralPattern: (pattern) => {
                        if (mounted) {
                            setWarning(`⚠️ ${pattern.pattern} detected`);
                            setTimeout(() => setWarning(''), 5000);
                        }
                    },
                    onStatusChange: (newStatus) => {
                        if (mounted) setStatus(newStatus);
                    }
                });

                engineRef.current = engine;
                await engine.initialize();
                
                if (videoRef.current) {
                    engine.start(videoRef.current);
                }

                // Update score periodically
                const interval = setInterval(() => {
                    if (engineRef.current && mounted) {
                        const summary = engineRef.current.getSessionSummary();
                        setScore(summary.suspiciousScore);
                    }
                }, 5000);

                return () => {
                    clearInterval(interval);
                };

            } catch (error) {
                console.error('Initialization failed:', error);
                if (mounted) setStatus('error');
            }
        };

        initialize();

        return () => {
            mounted = false;
            if (engineRef.current) {
                engineRef.current.stop();
                engineRef.current.destroy();
            }
        };
    }, [examId, studentId]);

    return (
        <div className="proctoring-container">
            <video 
                ref={videoRef} 
                autoPlay 
                playsInline 
                muted
                className="proctoring-video"
            />
            
            <div className="proctoring-info">
                <div className="status">
                    Status: <span className={status}>{status}</span>
                </div>
                <div className="score">
                    Suspicious Score: <span>{score}</span>
                </div>
            </div>

            {warning && (
                <div className="warning-banner">
                    {warning}
                </div>
            )}

            <div className="event-log">
                <h3>Recent Events</h3>
                {events.map((event, i) => (
                    <div key={i} className={`event severity-${event.lv}`}>
                        {new Date(event.ts).toLocaleTimeString()} - {event.event}
                    </div>
                ))}
            </div>
        </div>
    );
}

export default ExamProctoring;

🌐 Browser Support

| Browser | Version | Support | |---------|---------|---------| | Chrome | 90+ | ✅ Full | | Firefox | 88+ | ✅ Full | | Safari | 14+ | ✅ Full | | Edge | 90+ | ✅ Full | | Opera | 76+ | ✅ Full |

Requirements

  • WebRTC - For camera access
  • Web Audio API - For microphone access
  • WebGL - For MediaPipe GPU acceleration
  • ES6+ - Modern JavaScript features

Permissions Required

// Camera permission
await navigator.mediaDevices.getUserMedia({ video: true });

// Microphone permission (handled internally)
// Requested automatically by AudioMonitoringModule

⚡ Performance

Optimization Tips

  1. Adjust Detection FPS
// Lower FPS for better performance
engine.updateOptions({ detectionFPS: 5 });
  1. Increase Stability Frames
// Fewer false positives, better performance
engine.updateOptions({ stabilityFrames: 20 });
  1. Selective Modules
// Only enable what you need
ProctoringEngine.getInstance({
    enableVisualDetection: true,
    enableAudioMonitoring: false,  // Disable if not needed
    enablePatternDetection: true,
    enableBrowserTelemetry: true
});
  1. GPU Acceleration Ensure WebGL is enabled for MediaPipe GPU acceleration.

Performance Metrics

| Configuration | CPU Usage | Memory | Accuracy | |---------------|-----------|---------|----------| | Low (5 FPS) | ~10% | ~150MB | Good | | Medium (10 FPS) | ~20% | ~200MB | Better | | High (15 FPS) | ~30% | ~250MB | Best |

Tested on Intel i5, 8GB RAM, Chrome 120

🔒 Security

Data Privacy

  • Client-Side Processing - All detection runs in browser
  • No Cloud Dependencies - MediaPipe models loaded from CDN
  • Secure Transmission - Use HTTPS for backend communication
  • No Recording - Video/audio analyzed in real-time, not stored
  • Configurable - Choose which data to send to backend

Best Practices

  1. Obtain Explicit Consent
// Show consent dialog before starting
const consent = await showConsentDialog();
if (consent) {
    await proctor.start();
}
  1. Use HTTPS
// Always use secure connections
fetch('https://api.example.com/proctoring/event', {
    method: 'POST',
    // ...
});
  1. Implement Data Retention Policies
// Clear logs after exam
window.addEventListener('beforeunload', () => {
    engine.clearLogs();
});
  1. Provide Accommodations
// Adjust for students with disabilities
engine.updateOptions({
    gazeThreshold: 35,  // More lenient
    prolongedGazeAwayDuration: 10000
});

🐛 Troubleshooting

Camera Not Working

// Check permission
navigator.permissions.query({ name: 'camera' })
    .then(result => {
        console.log('Camera permission:', result.state);
        if (result.state === 'denied') {
            alert('Please allow camera access');
        }
    });

Audio Not Detected

// Check microphone permission
navigator.permissions.query({ name: 'microphone' })
    .then(result => {
        console.log('Microphone permission:', result.state);
    });

// Check if audio module initialized
if (!engine.audioModule.isSetup) {
    console.warn('Audio monitoring not available');
}

High CPU Usage

// Reduce detection FPS
engine.updateOptions({ detectionFPS: 5 });

// Disable unnecessary modules
engine.updateOptions({
    enableAudioMonitoring: false
});

Models Not Loading

// Check network connection
// MediaPipe models loaded from CDN
// Ensure CDN is accessible: https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@latest/

🤝 Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Setup

# Clone repository
git clone https://github.com/yourusername/ai-proctoring-engine.git

# Install dependencies
cd ai-proctoring-engine
npm install

# Run tests
npm test

# Build
npm run build

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

📞 Support

🗺️ Roadmap

  • [ ] TypeScript support
  • [ ] Hand tracking for gesture detection
  • [ ] Mobile support
  • [ ] Offline mode with model caching
  • [ ] Dashboard for supervisors
  • [ ] API for third-party integrations
  • [ ] Automated report generation
  • [ ] Multi-language support

Made with ❤️ for fair and secure online examinations