Selfie Module

Selfie Module Guide

This guide is specific to Web SDK 2.0. If you are still using 1.x, you can find documentation here. Contact your Incode Representative for upgrade information and check if you are a candidate for this upgrade.

The Selfie module captures a user's face with ML-powered liveness detection to prevent spoofing.

Follows the camera-capture pattern. See that page for the shared manager lifecycle, capture sub-states, and skeleton; the rest of this page covers Selfie-specific config, detection statuses, and methods.

Tag

<incode-selfie> is a standard Web Component. Importing the UI subpath registers the custom element; importing the CSS applies the module's styles.

import '@incodetech/web/selfie';
import '@incodetech/web/selfie/styles.css';

Properties

Set these as JavaScript properties on the element (not as HTML attributes):

Property Type Required Description
config SelfieConfig Configuration options (validation flags, modes)
onFinish () => void Called when capture completes successfully
onError (error: string) => void Called when an error occurs

WASM Requirements

The Selfie module uses WebAssembly for face detection and liveness analysis. Pre-warm WASM during setup() so models are ready before the user reaches the camera step:

await setup({
  apiURL: 'https://demo-api.incodesmile.com',
  token: 'your-session-token',
  wasm: { pipelines: ['selfie'] },
});

See WASM Configuration for self-hosted paths and the lower-level warmupWasm() API.

Usage

Vanilla HTML / TypeScript

<incode-selfie></incode-selfie>

<script type="module">
  import { setup } from '@incodetech/core';
  import '@incodetech/web/selfie';
  import '@incodetech/web/selfie/styles.css';

await setup({
    apiURL: 'https://demo-api.incodesmile.com',
    token: 'your-session-token',
    wasm: { pipelines: ['selfie'] },
  });

const selfie = document.querySelector('incode-selfie');
  selfie.onFinish = () => console.log('Selfie captured!');
  selfie.onError = (err) => console.error('Selfie error:', err);
</script>

React

import { useEffect, useRef } from 'react';
import { setup } from '@incodetech/core';
import type { SelfieConfig } from '@incodetech/core/selfie';
import '@incodetech/web/selfie';
import '@incodetech/web/selfie/styles.css';

type SelfieElement = HTMLElement & {
  config?: SelfieConfig;
  onFinish: () => void;
  onError: (error: string) => void;
};

await setup({
  apiURL: 'https://demo-api.incodesmile.com',
  token: 'your-session-token',
  wasm: { pipelines: ['selfie'] },
});

export function SelfieCapture() {
  const ref = useRef<SelfieElement>(null);

useEffect(() => {
    const el = ref.current;
    if (!el) return;
    el.onFinish = () => console.log('Selfie captured!');
    el.onError = (err) => console.error('Selfie error:', err);
  }, []);

return <incode-selfie ref={ref} />;
}

Headless Mode

For complete UI control, use the createSelfieManager from @incodetech/core/selfie.

Quick Start

import { setup } from '@incodetech/core';
import { createSelfieManager } from '@incodetech/core/selfie';
import { warmupWasm } from '@incodetech/core/wasm';

await setup({
  apiURL: 'https://demo-api.incodesmile.com',
  token: 'your-session-token',
});

await warmupWasm({
  wasmPath: '/wasm/webLib.wasm',
  glueCodePath: '/wasm/webLib.js',
  modelsBasePath: '/wasm/models',
  pipelines: ['selfie'],
});

const manager = createSelfieManager({
  config: {
    showTutorial: true,
    autoCaptureTimeout: 10,
    validateLenses: true,
    validateFaceMask: true,
  },
});

manager.subscribe((state) => {
  console.log('Status:', state.status);

if (state.status === 'capture') {
    console.log('Detection:', state.detectionStatus);
    console.log('Stream ready:', !!state.stream);
  }

if (state.status === 'finished') {
    console.log('Selfie captured!', state.processResponse);
    manager.stop();
  }
});

manager.load();

State Machine Flow

load

nextStep

granted

upload done

success

idle

tutorial

permissions

capture

processing

finished

States Reference

Status Description Key Properties
idle Initial state, waiting for load()
loading Checking permissions (when no tutorial)
tutorial Showing tutorial. Entered when showTutorial is true orageAssurance is true ageAssurance (boolean; mirrors config.ageAssurance)
permissions Camera permission handling permissionStatus
capture Camera active, detecting face stream, captureStatus, detectionStatus, attemptsRemaining
processing Server-side processing of the captured selfie
finished Capture complete processResponse?
closed User closed the flow
error Fatal error occurred error

Capture State Properties

Property Type Description
stream CameraStream Camera stream for <video> element
captureStatus string Current status of the capture
detectionStatus DetectionStatus Face detection feedback
attemptsRemaining number Remaining capture attempts
uploadError string? Error message if upload failed
assistedOnboarding boolean Whether assisted onboarding mode is active
debugFrame ImageData? Latest processed frame (for debugging)

Detection Status Values

Status User Instruction
idle "Preparing camera..."
detecting "Detecting face..."
noFace "Position your face in the frame"
tooManyFaces "Only one face should be visible"
tooClose "Move back"
tooFar "Move closer"
blur "Hold still, image is blurry"
dark "Improve lighting conditions"
faceAngle "Face your camera directly"
headWear "Remove head coverings"
lenses "Remove glasses or lenses"
eyesClosed "Open your eyes"
faceMask "Remove face mask"
centerFace "Center your face"
getReady "Get ready..."
getReadyFinished "Hold still..."
capturing "Capturing photo..."
manualCapture "Tap to capture"
offline "No network connection"

API Methods

Method Description When to Use
load() Starts the selfie flow Always call first
nextStep() Advances from tutorial to permissions When tutorial
requestPermission() Requests camera access When permissions.idle or permissions.learnMore
capture() Manual capture trigger When detectionStatus === 'manualCapture'
close() Close the flow Anytime
stop() Cleanup resources When unmounting
getState() Returns current state Anytime
subscribe(callback) Subscribe to state changes Returns unsubscribe function

Capture-only flow

createSelfieCaptureOnlyManager exposes the same state machine and API surface as createSelfieManager, but bypasses Incode's /omni/add/face upload. Instead of submitting the captured frame and waiting on server-side processing, the manager invokes a customer-supplied onCapture(response) callback with the raw face image.

The config is SelfieConfig plus a required onCapture callback — enforced at compile time:

import { setup } from '@incodetech/core';
import { initializeSession } from '@incodetech/core/session';
import { createSelfieCaptureOnlyManager, type SelfieCaptureOnlyConfig, type FaceCaptureOnlyResponse } from '@incodetech/core/selfie';

const config: SelfieCaptureOnlyConfig = {
  showTutorial: true,
  showPreview: false,
  captureAttempts: 3,
  validateLenses: true,
  onCapture: async (response: FaceCaptureOnlyResponse) => {
    const { image } = response;
    await uploadToMyBackend(image.blob);
  },
};

const manager = createSelfieCaptureOnlyManager({ config });
manager.load();

Configuration Options

SelfieConfig is FlowModuleConfig['SELFIE'] & BaseFaceCaptureConfig. Here are some options you can configure:

Option Type Required Description
showTutorial boolean Show tutorial before capture
showPreview boolean Show preview after capture
enableFaceRecording boolean Enable video recording of the capture
autoCaptureTimeout number Seconds before auto-capture triggers
captureAttempts number Maximum capture attempts
validateLenses boolean Reject captures with glasses/lenses
validateFaceMask boolean Reject captures with face mask
validateBrightness boolean Reject captures with poor lighting
deepsightLiveness `'SINGLE_FRAME' 'MULTIMODAL' 'VIDEOLIVENESS'`

Troubleshooting

Face Not Detected

Camera Issues