Circadify

Events

Handle SDK lifecycle events and per-frame state via callbacks.

The SDK is headless — all UI state flows through callbacks set on the constructor. This page covers the lifecycle and per-frame events; see Configuration for the full callback reference.

Callback API

Register callbacks when creating the SDK instance. They remain active for all measureVitals() calls on that instance.

import { CircadifySDK } from '@circadify/web-sdk';
 
const sdk = new CircadifySDK({
  apiKey: 'ck_live_your_key_here',
  onProgress:        (e)  => console.log(`${e.phase}: ${e.percent}%`),
  onQualityState:    (q)  => updateQualityMeters(q),
  onQualityWarning:  (w)  => showToast(w.message),
  onLandmarks:       (lm) => renderFaceMesh(lm),
  onCameraReady:     ({ stream }) => mirrorStream(stream),
});
typescript

To stop receiving events, call sdk.destroy(). There is no per-callback unsubscribe mechanism — callbacks are tied to the SDK instance lifecycle.

Progress Events

The onProgress callback fires as the measurement moves through each phase. Use it to update progress bars, status text, or step indicators.

interface ProgressEvent {
  phase: 'initializing' | 'readiness' | 'capturing' | 'uploading' | 'processing';
  percent: number; // 0–100
  elapsed: number;
  remaining?: number;
  captureProgress?: number; // 0–1, only while phase === 'capturing' — see below
}
typescript

captureProgress is the normalized frame-buffer fill ratio (0–1) and is only defined while phase === 'capturing'. Prefer it for capture-phase progress UI: percent is mapped across all phases and may not increase monotonically inside the capture phase across SDK versions.

PhaseDescriptionTypical Duration
initializingPreparing SDK runtime and session stateVaries
readinessWaiting for face detection and quality checks to passUser-dependent
capturingCapturing the measurementScan-duration dependent
uploadingUploading the measurement payloadNetwork dependent
processingWaiting for cloud processingNetwork and service dependent

Quality State Events

The onQualityState callback fires every frame during readiness and capture with the full quality picture. Use it to drive live quality meters or pills.

Frames where no face is detected are skipped — the callback does not fire on them, so meters driven by this event freeze at their last values rather than degrade when the user leaves the frame. Watch onLandmarks (which fires with empty landmarks) to detect face loss.

interface QualityState {
  lighting: { brightness: number; stability: number; isOk: boolean; /* … */ };
  motion:   { motionMagnitude: number; isStill: boolean };
  pose:     { yaw: number; pitch: number; isFacingForward: boolean };
  isReady:  boolean;
  messages: string[];
}
typescript
const sdk = new CircadifySDK({
  apiKey: 'ck_live_your_key_here',
  onQualityState: (q) => {
    setLightingPill(q.lighting.isOk ? 'good' : 'warn');
    setMotionPill(q.motion.isStill ? 'good' : 'warn');
    setPosePill(q.pose.isFacingForward ? 'good' : 'warn');
    setReady(q.isReady);
  },
});
typescript

Quality Warning Events

The onQualityWarning callback fires on transient quality drops. Use it for short-lived toasts; use onQualityState for sustained UI state.

interface QualityWarning {
  type: 'lighting' | 'motion' | 'face_position' | 'occlusion';
  severity: 'low' | 'medium' | 'high';
  message: string;
}
typescript
Warning TypeTriggerExample Message
lightingScene is too dark, too bright, or has flickering light"Too dark - increase lighting"
motionExcessive head or body movement detected"Please hold still"
face_positionHead is turned or tilted away from the camera"Turn head left"
occlusionDeclared in the type union but never emitted by 4.x

The example messages above are the actual 4.x strings, but treat message as non-normative UI copy — branch on type, never on message text.

Severity Levels

SeverityMeaning
lowDeclared in the type union but never emitted by 4.x — warnings always carry medium or high
mediumNoticeable impact on accuracy; user should correct
highMeasurement quality is severely compromised; prompt the user immediately

Landmark Events

The onLandmarks callback emits normalized face landmarks during measurement. Use it to render a custom face overlay:

const sdk = new CircadifySDK({
  apiKey: 'ck_live_your_key_here',
  onLandmarks: (landmarks) => {
    canvasRenderer.drawOverlay(landmarks); // 2D canvas; or use CircadifyScanView for a ready-made glow overlay
  },
});
typescript
Need a face / heat-glow overlay?

Prefer the bundled CircadifyScanView from the Web SDK's React bindings over building one from this callback. For a custom overlay, use a 2D <canvas> in immediate mode (Custom UI → Face overlay) and clear it on empty-landmark frames. Don't hand-roll a three.js / @react-three/fiber overlay; if you must, see Advanced: a 3D (r3f) overlay. Landmarks are normalized [0,1] and not mirrored.

Error Handling During Measurement

Errors during measureVitals() are thrown as CircadifyError exceptions, not delivered via callbacks. Wrap your measurement call in a try/catch block to handle failures.

import { CircadifySDK, CircadifyError, CircadifyErrorCode } from '@circadify/web-sdk';
 
try {
  const result = await sdk.measureVitals({ videoElement: myVideoEl });
} catch (error) {
  if (error instanceof CircadifyError) {
    if (error.isRetryable) {
      showRetryPrompt(error.message);
    } else {
      showError(error.message);
    }
  }
}
typescript

See Error Codes for the full list of error codes and recovery strategies.

Next Steps