Headless Mode
Headless mode lets you use the SDK’s processing capabilities without rendering any default UI. Build your own interface or run sessions in the background.
When to Use Headless Mode
Section titled “When to Use Headless Mode”Headless mode is the right choice when:
- Custom UI — You want full control over the scanning interface to match your application’s design system. See the Custom UI guide for a complete walkthrough.
- Programmatic sessions — You need to trigger scans from application logic without user interaction (e.g., automated health checks in a kiosk or telehealth flow).
- Existing UI integration — You already have a camera feed and UI components, and you want to add vital signs measurement without the SDK’s default overlay.
- Server-side orchestration — Your backend controls when scans happen, and the frontend just needs to capture and process frames.
Trade-offs
Section titled “Trade-offs”| Standard Mode | Headless Mode | |
|---|---|---|
| UI rendering | SDK handles it | You handle it |
| Camera access | SDK manages automatically | SDK manages, or you provide a stream |
| Progress feedback | Built-in overlay | You implement via callbacks/events |
| Quality warnings | Built-in prompts | You implement via onQualityWarning |
| Time to integrate | Faster | More work, more control |
Configuration
Section titled “Configuration”To use headless mode, omit the container option when calling measureVitals(). The SDK instance itself is initialized the same way:
import { CircadifySDK } from '@circadify/sdk';
const sdk = new CircadifySDK({ apiKey: 'ck_test_your_key_here', onProgress: (progress) => { // Handle progress in your own UI console.log(`${progress.phase}: ${progress.percent}%`); }, onQualityWarning: (warning) => { // Display quality feedback in your own UI console.warn(warning.message); },});All standard configuration options still apply in headless mode. The only difference is that measureVitals() is called without a container:
// Headless: no containerconst result = await sdk.measureVitals({ demographics: { age: 35, sex: 'M' },});
// Standard: with containerconst result = await sdk.measureVitals({ container: document.getElementById('scan-container'),});Session Lifecycle
Section titled “Session Lifecycle”A headless session follows the same lifecycle as a standard session. The difference is that your code is responsible for reflecting each phase in the UI.
1. Initialize
Section titled “1. Initialize”Create the SDK instance with your callbacks:
const sdk = new CircadifySDK({ apiKey: 'ck_test_your_key_here', onProgress: (progress) => updateProgressBar(progress.percent), onQualityWarning: (warning) => showWarning(warning.message),});2. Start the scan
Section titled “2. Start the scan”Call measureVitals() without a container. The SDK requests camera access (if not already granted), begins frame capture, and starts processing:
try { const result = await sdk.measureVitals({ demographics: { age: 35, sex: 'M' }, }); displayResults(result);} catch (error) { handleError(error);}3. Monitor progress
Section titled “3. Monitor progress”The onProgress callback fires throughout the scan with phase and percent information. Typical phases include calibration (face detection and light metering) and measurement (vital signs extraction).
4. Handle completion or failure
Section titled “4. Handle completion or failure”measureVitals() resolves with a VitalSignsResult on success, or throws a CircadifyError on failure. Check the confidence score to determine result reliability.
5. Clean up
Section titled “5. Clean up”Call sdk.destroy() when you are done to release all resources:
sdk.destroy();Using events instead of callbacks
Section titled “Using events instead of callbacks”You can also use the event API to monitor session state:
sdk.on('session:progress', (event) => { updateProgressBar(event.progress);});
sdk.on('session:complete', (event) => { displayResults(event.result);});
sdk.on('session:error', (event) => { handleError(event.error);});Cancellation
Section titled “Cancellation”Pass an AbortController signal to allow the user (or your application logic) to cancel a scan:
const controller = new AbortController();
// Cancel after 30 secondssetTimeout(() => controller.abort(), 30000);
try { const result = await sdk.measureVitals({ signal: controller.signal, });} catch (error) { if (error.code === 'CANCELLED') { console.log('Scan was cancelled.'); }}Camera Management
Section titled “Camera Management”By default, the SDK requests camera access via getUserMedia when measureVitals() is called and releases it when the scan completes or destroy() is called.
Using the SDK’s default camera handling
Section titled “Using the SDK’s default camera handling”In most cases, you do not need to manage the camera yourself. The SDK will:
- Request front-facing camera access (
facingMode: 'user') - Select an appropriate resolution for processing
- Release the camera stream when the session ends
Providing your own MediaStream
Section titled “Providing your own MediaStream”If you already have a camera stream (for example, because your app displays a preview before the scan), you can provide it to avoid a second permission prompt:
// Acquire your own camera streamconst stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'user', width: { ideal: 640 }, height: { ideal: 480 }, },});
// Display in your custom video elementconst video = document.getElementById('my-video');video.srcObject = stream;
// Pass the stream to the SDK -- it will use your stream instead of requesting its ownconst result = await sdk.measureVitals({ mediaStream: stream,});Camera constraints
Section titled “Camera constraints”For best results, ensure your stream meets these minimum requirements:
- Resolution: 640x480 or higher
- Frame rate: 15 fps or higher (30 fps recommended)
- Facing mode: Front-facing camera (
user) for face-based vital signs
Lower resolutions or frame rates may reduce measurement accuracy and confidence scores.
Next Steps
Section titled “Next Steps”- Custom UI — Build a custom visual interface on top of headless mode
- Error Codes — Handle errors in headless mode