npm package: @rahulturaga/proctoring-monitor
Description: AI-powered proctoring component for React — detects face, eye, hand movements and suspicious behaviour using MediaPipe
Latest version: 1.0.4

# @rahulturaga/proctoring-monitor

AI-powered proctoring component for React — monitors candidates during online exams using MediaPipe for face mesh, iris tracking, hand detection, and behavioural analysis. All processing runs in the browser. No backend required.

---

## Features

- Thumbnail webcam view (120px, non-intrusive)
- Face detection — no face, multiple faces, face moved
- Head turn detection — looking left / right / down
- Iris gaze tracking — eyes looking left / right / up / down
- Saccade detection — rapid eye movements
- Hand detection and movement tracking
- Audio / noise detection with ambient calibration
- Tab switch detection
- Copy / paste / cut detection
- Repetition-based suspicion scoring (single events don't spike score)
- `submitReport()` via ref — returns full JSON report
- `onReport` callback with violations + suspicion analysis

---

## Installation

```bash
npm install @rahulturaga/proctoring-monitor
```

```bash
yarn add @rahulturaga/proctoring-monitor
```

---

## Usage

```tsx
import { useRef } from "react";
import { ProctoringMonitor } from "@rahulturaga/proctoring-monitor";
import type { ProctoringHandle, ProctoringReport } from "@rahulturaga/proctoring-monitor";

export default function ExamPage() {
  const proctoringRef = useRef<ProctoringHandle>(null);

  const handleSubmitExam = () => {
    // call when the exam ends — returns full report JSON
    const report = proctoringRef.current?.submitReport();
    console.log(report);
  };

  return (
    <div>
      <ProctoringMonitor
        ref={proctoringRef}
        onViolation={(entry) => console.log("violation:", entry.type)}
        onSuspicionChange={(result) => console.log("score:", result.score)}
        onReport={(report: ProctoringReport) => {
          // fired automatically when submitReport() is called
          fetch("/api/save-report", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(report),
          });
        }}
      />

      {/* your exam content */}

      <button onClick={handleSubmitExam}>Submit Exam</button>
    </div>
  );
}
```

---

## API Reference

### `ProctoringMonitor` Props

| Prop                | Type                              | Default | Description                                        |
|---------------------|-----------------------------------|---------|----------------------------------------------------|
| `onViolation`       | `(entry: ViolationEntry) => void` | —       | Fires on every new violation                       |
| `onSuspicionChange` | `(result: SuspicionResult) => void` | —     | Fires whenever the suspicion score recalculates    |
| `onReport`          | `(report: ProctoringReport) => void` | —    | Fires when `submitReport()` is called              |
| `cooldownMs`        | `number`                          | `3000`  | Min ms between duplicate violations of same type  |

---

### `ProctoringHandle` (ref)

```ts
const proctoringRef = useRef<ProctoringHandle>(null);

// attach to component
<ProctoringMonitor ref={proctoringRef} ... />

// call when exam ends
const report = proctoringRef.current?.submitReport();
```

| Method           | Returns            | Description                            |
|------------------|--------------------|----------------------------------------|
| `submitReport()` | `ProctoringReport` | Builds and returns the full report JSON. Also fires `onReport`. |

---

### `ProctoringReport` JSON

```json
{
  "generatedAt": "2026-04-08T13:00:00.000Z",
  "durationSeconds": 3600,
  "suspicion": {
    "score": 72,
    "level": "Highly Suspicious",
    "color": "#ea580c",
    "reasons": [
      "Repeated sideways glances (5×) — checking side cheat sheet",
      "Repeated downward gaze (4×) — reading notes on desk"
    ]
  },
  "summary": {
    "total": 14,
    "byType": {
      "looking-left": 3,
      "eyes-looking-down": 4,
      "tab-switch": 2,
      "hand-movement": 3,
      "noise-detected": 2
    }
  },
  "violations": [
    {
      "type": "looking-left",
      "time": "10:30:45",
      "timestamp": 1744113600000,
      "screenshot": "data:image/jpeg;base64,..."
    }
  ]
}
```

---

### Suspicion Levels

| Score   | Level              |
|---------|--------------------|
| 0–24    | Normal             |
| 25–49   | Suspicious         |
| 50–74   | Highly Suspicious  |
| 75–100  | Likely Copying     |

Scoring is **repetition-based** — a single sideways glance scores 0. The score rises only when the same behaviour repeats (3+ times in a 60-second window), reducing false positives in exam hall environments.

---

### Violation Types

| Type                  | Trigger                                      |
|-----------------------|----------------------------------------------|
| `no-face`             | Face not visible in camera                   |
| `multiple-faces`      | More than one face detected                  |
| `face-moved`          | Face position shifted significantly          |
| `looking-left`        | Head turned left (nose vs eye midpoint)      |
| `looking-right`       | Head turned right                            |
| `looking-down`        | Head tilted downward                         |
| `eyes-looking-left`   | Iris gaze shifted left                       |
| `eyes-looking-right`  | Iris gaze shifted right                      |
| `eyes-looking-up`     | Iris gaze shifted upward                     |
| `eyes-looking-down`   | Iris gaze shifted downward                   |
| `rapid-eye-movement`  | Fast iris movement between frames (saccade)  |
| `hand-detected`       | Hand appears in frame                        |
| `hand-movement`       | Hand moving actively                         |
| `noise-detected`      | Audio significantly above ambient level      |
| `tab-switch`          | Browser tab hidden / window minimised        |
| `copy-detected`       | Ctrl+C or right-click copy                   |
| `paste-detected`      | Ctrl+V paste                                 |
| `cut-detected`        | Ctrl+X cut                                   |

---

## Requirements

- Camera and microphone permission required
- Works in Chrome and Edge (WebAssembly + WebGL required)
- Must be served over **HTTPS** in production
- React 17 or higher

---

## Notes

- No video is stored — screenshots are JPEG thumbnails captured at violation time
- All detection runs locally in the browser via MediaPipe WebAssembly
- MediaPipe model files are loaded from jsDelivr CDN on first use

---

## License

MIT © Rahul Turaga