Vanilla JavaScript
The Circadify SDK works without any framework. The SDK is headless: you render the <video> element and any overlays, the SDK drives the camera, extraction, and upload, and emits per-frame events for you to consume.
Install the SDK
Section titled “Install the SDK”@circadify/web-sdk is a private package — complete the .npmrc + PAT setup first, then install:
npm install @circadify/web-sdkThe SDK ships as ESM with a CommonJS fallback. Use a bundler (Vite, webpack, esbuild, Parcel) to pull it into your page:
import { CircadifySDK } from '@circadify/web-sdk';
const sdk = new CircadifySDK({ apiKey: 'ck_live_your_key_here',});Basic Setup
Section titled “Basic Setup”Render a <video> element. Pass it as videoElement to measureVitals(). The SDK binds the camera stream to that element and processes frames out of it.
<!DOCTYPE html><html><body> <video id="preview" autoplay playsinline muted style="width: 480px"></video> <button id="start-btn">Start Scan</button> <pre id="results"></pre>
<script type="module"> import { CircadifySDK } from '@circadify/web-sdk';
const videoEl = document.getElementById('preview');
const sdk = new CircadifySDK({ apiKey: 'ck_live_your_key_here', onProgress: (e) => console.log(`${e.phase}: ${e.percent}%`), onQualityWarning: (w) => console.warn('Quality issue:', w), });
document.getElementById('start-btn').addEventListener('click', async () => { try { const result = await sdk.measureVitals({ videoElement: videoEl });
document.getElementById('results').textContent = JSON.stringify(result, null, 2); } catch (error) { console.error('Scan failed:', error.message); } }); </script></body></html>The measureVitals() call handles the full lifecycle: camera access, face detection, measurement, and result delivery. It returns a VitalSignsResult with heartRate, respiratoryRate, hrv, spo2, systolicBp, diastolicBp, confidence, sessionId, and timestamp.
Lifecycle Callbacks
Section titled “Lifecycle Callbacks”The SDK reports session progress and per-frame state through callbacks set at construction time. There is no .on() event-emitter API — subscribe by configuring callbacks on the CircadifySDK constructor:
const sdk = new CircadifySDK({ apiKey: 'ck_live_your_key_here',
// Phase + percent throughout the scan onProgress: (event) => { const bar = document.getElementById('progress'); bar.style.width = `${event.percent}%`; bar.textContent = `${event.phase} (${Math.round(event.percent)}%)`; },
// Full per-frame quality state (lighting, motion, pose, readiness) onQualityState: (q) => { setLightingPill(q.lighting.isOk); setMotionPill(q.motion.isStill); setPosePill(q.pose.isFacingForward); },
// Transient quality warnings — good for toast notifications onQualityWarning: (warning) => { showToast(warning.message); },
// 468-point face mesh (MediaPipe). Render your own overlay if you want one. onLandmarks: (landmarks) => { canvasRenderer.drawMesh(landmarks); },
// Fires once after the camera is acquired (only when no videoElement is passed) onCameraReady: ({ stream, video }) => { document.getElementById('start-btn').disabled = false; },});Successful results and errors are surfaced through measureVitals()’s returned promise — there is no separate completion event:
try { const result = await sdk.measureVitals({ videoElement: videoEl }); document.getElementById('result').textContent = `Heart Rate: ${result.heartRate} BPM`;} catch (error) { document.getElementById('error').textContent = error.message; document.getElementById('error').hidden = false;}See SDK Configuration for the full callback list.
Cancelling a Scan
Section titled “Cancelling a Scan”Use an AbortController to let the user cancel a scan mid-session:
const controller = new AbortController();
document.getElementById('cancel-btn').addEventListener('click', () => { controller.abort();});
try { const result = await sdk.measureVitals({ videoElement: videoEl, signal: controller.signal, });} catch (error) { if (error.code === 'CANCELLED') { console.log('Scan cancelled by user.'); }}Cleanup
Section titled “Cleanup”Call sdk.destroy() when you are done with the SDK to release all resources. This stops active camera streams and unloads WASM modules.
// When navigating away or tearing downsdk.destroy();For pages that load and unload dynamically (SPA routing), tie cleanup to your router’s lifecycle:
window.addEventListener('beforeunload', () => { sdk.destroy();});Next Steps
Section titled “Next Steps”- Custom UI — Build a fully-custom scan UI from the SDK callbacks
- SDK Configuration — All available options
- Troubleshooting — Common issues and fixes