Web Measurement Embedded App (WMEA) - v1.2.0
    Preparing search index...

    Integration Details

    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:

    1. 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.

    2. 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. Because the browser makes the choice, the built-in camera selector dropdown is hidden while this is set (a manual picker would have no effect). 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, the payload and metadata sent to DeepAffex Cloud are saved for debugging. When a measurement completes, a single ZIP file named <measurementId>.zip is downloaded, containing a payload-N.bin and metadata-N.bin for each chunk (N is the chunk index). A 30-second measurement emits 6 chunks (one every 5 seconds), so 12 files in total; only chunks produced during the measurement are included.
    • 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:

    1. Before initializing WMEA, present your own camera picker (e.g. a <select> populated from navigator.mediaDevices.enumerateDevices()). Camera permission must be granted first for device labels to be populated.
    2. Persist the user's chosen deviceId in localStorage (or another store of your choice).
    3. On subsequent visits, read the stored 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.

    The Profile passed to init() carries the user's demographic information. It has three forms:

    • Full profile (default): provide every field, as in the example above.
    • Partial profile: set partialProfile: true and provide only the fields you have. Only those fields are validated and sent with the measurement, and results are calculated using only those fields.
    • No profile: set bypassProfile: true to send no demographic information. WMEA emits an ERROR event with PROFILE_INFO_NOT_SET to let you know.
    // Partial profile: only height and weight are sent
    const profile: Profile = {
    heightCm: 180,
    weightKg: 75,
    partialProfile: true,
    bypassProfile: false,
    };
    Field Valid values
    age Whole number, 13 – 120 years
    heightCm 120 – 220 cm
    weightKg 30 – 300 kg
    sex SEX_ASSIGNED_MALE_AT_BIRTH, SEX_ASSIGNED_FEMALE_AT_BIRTH
    smoking SMOKER_TRUE, SMOKER_FALSE
    bloodPressureMedication BLOOD_PRESSURE_MEDICATION_TRUE, BLOOD_PRESSURE_MEDICATION_FALSE
    diabetes DIABETES_NONE, DIABETES_TYPE1, DIABETES_TYPE2

    When both heightCm and weightKg are provided, their BMI must be between 10 and 65. If any provided field is invalid, WMEA emits an ERROR event with INVALID_PROFILE and a message naming the field.

    Without partialProfile: true, every field is required, exactly as in earlier versions, so existing integrations don't need to change.

    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

    1. Mount event listeners BEFORE init;
    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;
    }
    };
    1. Call init with credentials

    Language selection follows this priority:

    1. Language specified in initialization object
    2. Browser's active language (if supported)
    3. English (default)

    For a list of available supported languages, please see SupportedLanguage

    For a complete list of available methods and properties, please see MeasurementEmbeddedApp