Optional camera evidence
Add coarse gaze summaries to browser observation with the optional first-party camera controller. Processing runs locally after the user enables the camera and completes calibration. Ordinary observation remains the default integration path.
Camera access is off by default and starts through an explicit user gesture. Offer a clear enable control, explain the purpose before permission is requested, and keep a visible Stop camera control. Keep ordinary observation available when a person declines camera access or measurements are unavailable. Choosing the non-camera path carries no camera-related penalty.
Availability and prerequisites
This guide applies to integrations with an approved compatible camera asset set. Before starting, confirm that set with founders@graypass.org, then host its worker, models, WASM runtime, and notices as described below. The public browser core download supplies ordinary observation and challenges; camera assets are obtained separately. Use the browser SDK observation flow as the base integration, then add this controller after installing the complete asset set.
Start from a user gesture
import { GrayPass } from '@graypass/browser';
const tracker = GrayPass.createEyeTracker({
assetBase: '/gaze/',
video: cameraPreview,
onUpdate: (state) => renderCameraState(state),
});
const observer = await GrayPass.observe({
token: clientToken,
subject: subjectId,
purpose: 'observe',
endpoint: 'https://app.graypass.org',
eyeTracking: tracker,
});
enableCameraButton.addEventListener('click', async () => {
await tracker.start();
const result = await tracker.calibrate({
onTarget: (target) => showCalibrationTarget(target),
});
if (result.status !== 'ready') showCalibrationRetry();
});
stopCameraButton.addEventListener('click', () => tracker.stop());
This is an illustrative TypeScript excerpt. The UI functions and DOM elements belong to your application, and the token and subject come from its authenticated backend. Handle start and calibration exceptions, hide the target when calibration ends, and preserve a usable non-camera path. Camera access requires HTTPS or a browser-supported localhost secure context.
Calibration and lifecycle
Render targets using normalized viewport coordinates supplied by the tracker. Calibration fits estimates on one target set and checks them on separate targets. A completed calibration call can return needs_calibration; inspect result.status, local quality information, and guidance before displaying a ready state.
Calibration quality describes screen-estimation quality for this visit, rather than identity confidence. Viewport changes, sustained head-position changes, calibration age, or a restart can require fresh calibration. Hiding the page stops the camera; returning requires explicit enablement again.
Use a tracker during enrollment if the evaluation requires a compatible enrolled camera reference. Only one observer should consume a tracker at a time. A shared tracker has a lifecycle separate from observer.stop(). On pause, exit, subject deletion, or application teardown, call tracker.stop() or tracker.dispose() as appropriate and end the observer.
tracker.stop();
await observer.stop();
Host the complete asset directory
For an integration with the required compatible asset set, host its complete dist/gaze/ directory. Copy the entire directory, including worker, models, WASM files, licenses, and notices, to your application's /gaze/ route. Preserve its structure. Serve JavaScript with its normal JavaScript MIME type and WASM as application/wasm.
The camera directory contains the client-side processing runtime, including SIMD and non-SIMD variants selected by the worker. Keep the supplied licenses and notices with these files. Host this directory when your integration offers camera evidence.
assetBase must use the page's own origin. It is independent of the observer's API endpoint. A page at https://merchant.example must load its camera assets from that origin even when evidence is sent to https://app.graypass.org. Cross-origin asset directories are rejected.
The page's Content Security Policy must allow first-party scripts, worker-src 'self', and API connections to the configured GrayPass endpoint. Its Permissions Policy must permit camera=(self); microphone access is unnecessary. The worker response needs its own WASM-capable policy:
Content-Security-Policy: default-src 'none';
script-src 'self' 'wasm-unsafe-eval'; connect-src 'self';
worker-src 'none'; object-src 'none';
frame-ancestors 'none'; base-uri 'none';
Send the policy as one HTTP header value; the line breaks above are for readability. Keep the HTML page's script policy restricted to its required first-party scripts. Configure headers on the server serving the worker, not on the remote API host.
What leaves the device
Raw video frames, eye crops, face geometry, individual gaze paths, and calibration diagnostics remain local in this SDK flow. The observer can submit coarse dwell, transition, dispersion, and region summaries with sampling and calibration quality. The observer submits usable measurements according to collection and calibration quality. The summaries remain subject-linked derived data and require appropriate handling.
Review Data flow, Browser SDK, and Troubleshooting before offering camera collection to users.