Skip to content

Temporal Fusion & Replay

Instantaneous neural network predictions are inherently noisy: a candidate blinking looks like closed eyes for 100 ms, a glance at the keyboard looks like gaze diversion for 300 ms, and a transient shadow can produce a momentary false-positive face.

The Temporal Fusion Engine converts noisy instantaneous Signals into stable, defensible Violation events.


How Temporal Fusion Works

Instead of triggering alarms on individual frames, the Rust engine uses state machines with:

  1. Hysteresis Bands: A violation requires a high confidence threshold to open (e.g. 0.75), but does not immediately clear until confidence falls below a lower threshold (e.g. 0.40).
  2. Hold Timers: A candidate looking away must sustain that behavior for a defined time window (e.g. 1500 ms) before a violation is declared.
  3. Score Accumulators: Multiple weak signals (e.g. borderline head pose combined with borderline gaze deviation) reinforce each other over time.

Deterministic Replay

One of the foundational design choices in Vigilo is that temporal fusion is a pure function of inputs and discrete time (t_ms).

Given an array of recorded Signals and a configuration, replay() produces a byte-identical sequence of Event objects every time:

ts
import { initVigilo, replay, type Signals, type Event } from 'vigilo-wasm';

await initVigilo();

// Load recorded signals from an exam session
const recordedSignals: Signals[] = await fetch('/api/sessions/123/signals.json')
  .then(r => r.json());

// Replay through the fusion engine with custom thresholds
const events: Event[] = replay(recordedSignals, {
  thresholds: {
    gaze_hold_ms: 2000, // Test stricter 2-second hold time
    head_turn_yaw_deg: 35.0,
  },
});

for (const event of events) {
  if (event.event === 'violation_started') {
    console.log(`Violation started at ${event.t_start_ms}ms: ${event.kind}`);
  }
}

Why Determinism Matters

  • Threshold Tuning: You can fine-tune proctoring sensitivities against a recorded benchmark corpus without re-running heavy neural inference on video files.
  • Auditability: If a student contests an exam flag, the session signals can be independently replayed and verified by academic integrity committees.

Standalone ProctorSession

If your application already receives signals from another source (e.g. a Web Worker, a custom WebSocket stream, or another vision model), use ProctorSession directly:

ts
import { initVigilo, ProctorSession, type Signals } from 'vigilo-wasm';

await initVigilo();

const session = new ProctorSession();

// Feed signals sequentially with monotonic t_ms
function onNewSignal(signals: Signals, t_ms: number) {
  const events = session.step(signals, t_ms);
  for (const event of events) {
    handleEvent(event);
  }
}

// At exam conclusion, close any lingering violations
function endExam(final_t_ms: number) {
  const closingEvents = session.finish(final_t_ms);
  console.log('Session ended with events:', closingEvents);
}

Released under the AGPL-3.0 License.