The problem nobody talks about in face verification
Most face-verification flows check one thing: is there a face in the frame that matches the enrolled user? They don't seriously check is this a live human being, right now, in front of the camera?
That gap is exactly what attackers exploit. Hold up an iPhone Live Photo (it has subtle motion baked in), print a 3D paper frame, or replay a screen recording on a 4K monitor — and a lot of "liveness" SDKs wave you through.
We ran into this building attendance and identity-verification features for field workforce apps, and ended up writing our own on-device liveness + matching engine for Flutter. Here's the technical breakdown of how it works, and what we learned building something that has to run fully offline, in under 300ms, on a mid-range Android phone.
Why offline-first changes the architecture
Cloud-based liveness APIs are the default because they're easy to build. Upload a frame, get a score back. But that approach has three real costs:
Latency — a round trip adds 500ms–2s depending on network, which kills conversion on check-in flows
Privacy — you're shipping someone's face to a server, which is a GDPR/compliance headache the moment you touch EU or health-adjacent users
Availability — field workers in low-connectivity zones (warehouses, rural sites, basements) can't verify at all
So we built the entire pipeline to run on-device:
Frame Capture → Edge AI Inference (TFLite) → Biometric Vector Validation
Frame Capture — the SDK opens the front camera and streams raw frames locally inside the widget. No frame ever leaves the device.
Edge AI Inference — lightweight, encrypted TFLite models detect the face, analyze passive 3D depth landmarks, and score spoof features (texture, reflection, depth consistency) — all in the same pass.
Vector Validation — the face is converted into a 512-dimension embedding and matched 1:1 or 1:N against locally stored template hashes. Raw images are never persisted; only the encrypted vector hash is kept.
What "passive" liveness actually means
Active liveness ("blink now," "turn your head") is annoying and still spoofable with enough effort. Passive liveness analyzes a single camera session for signals a photo or screen literally cannot reproduce:
Depth micro-structure — real skin has depth variance a flat image or screen doesn't
Reflection physics — screens and glossy photo paper reflect light differently than skin
Texture entropy — printed/replayed faces have compression and moiré artifacts at the pixel level
We map roughly 50,000 depth points onto the detected face per frame to make this robust against extreme angles, low light, and glasses — without asking the user to do anything.
Integrating it in Flutter
dart
import 'package:binimise_biometric/binimise_biometric.dart';
final result = await BinimiseBiometric.runLivenessCheck();
if (result.isLive) {
final match = await BinimiseBiometric.matchFace(
templateId: enrolledUserId,
);
print('Match confidence: ${match.confidenceScore}');
} else {
print('Spoof attempt blocked: ${result.spoofType}');
}
Both liveness and face classification happen in the same camera session — no secondary prompt, no "now scan again" screen. That single decision made the biggest difference to our drop-off rate in testing.
Specs, for the skeptics
Platform Min version Architectures
Android API 24+ (7.0) arm64-v8a, armeabi-v7a, x86_64
iOS 14.0+ Physical (arm64), Simulator (arm64, x86_64)
Liveness check: < 300ms
1:1 / 1:N face match: < 1ms (after enrollment)
Zero network calls at runtime
No raw image persistence — GDPR-friendly by design
What it stops (and what it doesn't try to)
It's built specifically against the attacks that beat naive systems: iPhone Live Photos, printed 3D frames, silicone masks, and screen replays (including 4K/8K playback). It also blocks rooted/compromised devices from completing verification at all. It is not trying to be a full KYC/document-verification suite — it's a focused liveness + matching layer you drop into an existing auth or attendance flow.
Try it
The SDK is free to try, no credit card:
GitHub: https://github.com/binimiselabs/binimise_biometric
Demo app: https://play.google.com/store/apps/details?id=com.liveness.liveness_app
If you're building anything with attendance, KYC, or repeat identity checks in Flutter, I'd genuinely like feedback on where this breaks — spoof detection is an arms race and outside testing is how you find the gaps.
Top comments (0)