Skip to main content

On-device liveness detection

The widget runs Google ML Kit face detection on every preview frame to drive on-device challenge gating ("blink now", "turn left") and to capture a hero frame only when the user is aligned with the oval. The server remains the source of truth for the verdict; on-device ML is a UX layer.

What runs on-device

  • ML Kit face detection (FastMode, classification enabled for eye / smile probabilities)
  • Per-challenge detectors: blink (eye-open probability), head turn (head Euler angle), smile (smile probability)
  • Alignment check against the on-screen oval before challenges start

What runs on the server

  • The authoritative anti-spoof and deepfake checks against the captured frame(s)
  • All scoring — is_live, score, threshold come from /v1/liveness/check, never the device

If on-device gating accepts a challenge but the server rejects the frame as a spoof, the server wins. result.isLive reflects the server's decision.

Why no enable / disable toggle

The v1 enableOnDeviceLiveness flag is gone as of v2. On-device ML Kit is always on — it's tightly coupled to the widget's challenge UX (without it, "blink now" prompts can't progress automatically). Customization moved into LivenessConfig: adjust allowedChallenges and challengeCount to control which detectors run.

Integrators that want a server-only flow with no on-device gating should build their own capture UI and call client.livenessCheckImage(File) directly. The widget's value is the on-device coupling — there's no point owning the widget if you're going to bypass that layer.

Footprint

The ML Kit dependency adds ~10 MB to the APK regardless of which detectors are in allowedChallenges. It's a runtime gate, not a compile-time one.