This page explains how the Web Measurement Embedded App (WMEA) fits into your application. It primarily focuses on technical integration, but also covers user-facing messages and supported languages.
The WMEA is integrated directly by initializing the MeasurementEmbeddedApp class. Fetch your credentials first, then call init method which accepts an object of type MeasurementEmbeddedAppOptions.
import MeasurementEmbeddedApp, {
faceAttributeValue,
type Profile,
type MeasurementEmbeddedAppOptions,
} from '@nuralogix.ai/web-measurement-embedded-app';
const { SEX_ASSIGNED_MALE_AT_BIRTH, SMOKER_FALSE, BLOOD_PRESSURE_MEDICATION_FALSE, DIABETES_NONE } =
faceAttributeValue;
const measurementApp = new MeasurementEmbeddedApp();
// Retrieve credentials before init
// Let's assume fetchStudyId and fetchToken functions
// call your server endpoints (e.g., /api/studyId, /api/token)
const studyIdResponse = await fetchStudyId(); // { status: '200', studyId }
const tokenResponse = await fetchToken(); // { status: '200', token, refreshToken }
if (studyIdResponse.status === '200' && tokenResponse.status === '200') {
const container: HTMLDivElement = document.createElement('div');
// Style the container before passing it to MeasurementEmbeddedApp
Object.assign(container.style, {
position: 'fixed',
top: '60px',
left: '0',
width: '100vw',
height: 'calc(100vh - 60px)',
});
const profile: Profile = {
age: 40,
heightCm: 180,
weightKg: 60,
sex: SEX_ASSIGNED_MALE_AT_BIRTH,
smoking: SMOKER_FALSE,
bloodPressureMedication: BLOOD_PRESSURE_MEDICATION_FALSE,
diabetes: DIABETES_NONE,
bypassProfile: false,
};
const options: MeasurementEmbeddedAppOptions = {
container,
appPath: './measurement-app',
settings: {
token: tokenResponse.token,
refreshToken: tokenResponse.refreshToken,
studyId: studyIdResponse.studyId,
},
profile,
// language: 'en', // [optional] See list of supported languages
// apiUrl: 'api.na-east.deepaffex.ai', // [optional] for region specific data processing
// config, // [optional]
loadError: function (error) {
console.error('Load error', error);
},
};
}
Notes:
Profile values must use the exact faceAttributeValue enum values as shown in the example above. If you are collecting demographic info from a form we recommend setting up your form to use these values so you don't have to worry about conversions.
Default Config used if the optional config object is not passed:
const defaultConfig: Config = {
checkConstraints: true,
cameraFacingMode: undefined,
cameraAutoStart: false,
measurementAutoStart: false,
cancelWhenLowSNR: true,
debugMode: false,
downloadPayloads: false,
defaultCameraId: '',
};
Config options:
checkConstraints (default: true) – enables constraint validation pre-measurement.cameraFacingMode (default: undefined) – use 'user' for front camera or 'environment' for back camera. When set, it takes precedence over defaultCameraId — the camera is selected by facing direction and the browser chooses the device. Leave it unset to select by defaultCameraId (or the browser default).cameraAutoStart (default: false) – automatically starts the camera once permission is granted.measurementAutoStart (default: false) – automatically starts the measurement once constraints are satisfied.cancelWhenLowSNR (default: true) – cancels the measurement if signal-to-noise ratio falls below threshold.debugMode (default: false) – enables verbose logging to the console for debugging purposes.downloadPayloads (default: false) – when enabled, saves payload and metadata binary files for each chunk sent to DeepAffex Cloud so you can send them to NuraLogix for debugging. A 30-second measurement emits 6 chunks (every 5 seconds), generating 2 files per chunk. Ensure your browser allows multiple file downloads and does not block popups.defaultCameraId (default: '') – preferred camera deviceId to use on startup. Used only when cameraFacingMode is not set (a set cameraFacingMode takes precedence). When the camera selector dropdown is shown (desktop), this becomes the default selection. When the dropdown is hidden (mobile, or cameraAutoStart: true), this camera is opened directly. If the supplied ID does not match any connected device, the browser's default camera is used. See Persisting a camera selection below for usage.cameraFacingMode takes precedence over defaultCameraId. A defaultCameraId only counts when it is valid — it matches a currently connected camera; an absent, empty, or disconnected ID is treated as unset.
cameraFacingMode |
defaultCameraId valid? |
Camera opened |
|---|---|---|
| set | yes | selected by facing mode — the browser picks the device; defaultCameraId is ignored |
| set | no | selected by facing mode |
| unset | yes | the defaultCameraId device (exact match) |
| unset | no | the browser default camera (first available) |
Browsers don't always expose enough metadata for WMEA to pick the "right" camera on its own — for example, iPads expose both a standard and an ultra-wide front camera as facingMode: 'user', and there's no standard way to tell them apart. defaultCameraId lets your application drive that choice instead.
A typical pattern:
<select> populated from navigator.mediaDevices.enumerateDevices()). Camera permission must be granted first for device labels to be populated.deviceId in localStorage (or another store of your choice).deviceId and pass it to WMEA as config.defaultCameraId when calling init().const savedCameraId = localStorage.getItem('preferredCameraId') ?? '';
await measurementApp.init({
// …other options
config: {
cameraAutoStart: true,
defaultCameraId: savedCameraId,
},
});
Note that deviceId values are stable per-(origin, browser profile) but change when the user clears site data. If a stored ID is stale (not connected), WMEA opens the browser's default camera — so leave cameraFacingMode unset when relying on defaultCameraId, since a set cameraFacingMode would take precedence over it.
MeasurementEmbeddedApp.Results | Results are delivered via on.results
event handler. A common pattern is to save them to your app state, then
navigate to the results page in your application:
measurementApp.on.results = (results) => {
// 1) Persist results in your app state
store.measurement.setResults(results);
// 2) Navigate to the results page in your SPA
router.push('/results');
};
Note: Results are available immediately upon measurement completion through the event handler. If legal agreement allows, detailed results can be retrieved later using DeepAffex API endpoints.
Errors are delivered via on.error event handler:
import { ErrorCodes } from '@nuralogix.ai/web-measurement-embedded-app';
measurementApp.on.error = (error) => {
switch (error.code) {
case ErrorCodes.PROFILE_INFO_NOT_SET:
console.log('Profile information is not set', error);
break;
// Add switch statements for the rest of available ErrorCodes
default:
console.log('An unknown error occurred', error);
}
};
For a list of available error codes please see ErrorCodes
Events are delivered via on.event event handler:
import { appEvents } from '@nuralogix.ai/web-measurement-embedded-app';
measurementApp.on.event = (appEvent) => {
switch (appEvent) {
case appEvents.APP_LOADED:
console.log('Application loaded');
break;
// Add switch statements for the rest of available app events
default:
console.log('An unknown app event occurred', appEvent);
break;
}
};
For a list of available error codes please see appEvents
measurementApp.on.event = (appEvent) => {
switch (appEvent) {
case appEvents.MEASUREMENT_PREPARED:
console.log('Credentials are valid!');
break;
case appEvents.ASSETS_DOWNLOADED:
console.log('Assets downloaded, ready to measure!');
break;
}
};
measurementApp.on.error = (error) => {
switch (error.code) {
case ErrorCodes.MEASUREMENT_PREPARE_FAILED:
console.error('Credentials are invalid or missing');
console.error('Check your token, refreshToken, or studyId');
break;
}
};
Language selection follows this priority:
For a list of available supported languages, please see SupportedLanguage
For a complete list of available methods and properties, please see MeasurementEmbeddedApp