Building FaceGuard: Offline Face Recognition in React Native
- React Native
- TensorFlow Lite
- On-device ML
- TypeScript
Most face-recognition tutorials send your camera frames to a cloud API. I wanted the opposite: everything on the device, nothing leaves the phone. That constraint is what makes FaceGuard interesting.
The constraints
- Offline-first. No network calls for inference.
- Cross-platform. One React Native codebase, Android and iOS.
- Anti-spoofing. A photo of a face shouldn't pass.
On-device inference with MobileFaceNet
I used a quantized MobileFaceNet model running through TensorFlow Lite. The model turns a face crop into a 128-dimensional embedding; recognition is just a nearest-neighbour search over stored embeddings.
const embedding = await runTflite(faceCrop); // Float32Array(128)
const match = gallery
.map((g) => ({ id: g.id, dist: cosineDistance(g.vec, embedding) }))
.sort((a, b) => a.dist - b.dist)[0];
if (match.dist < THRESHOLD) authenticate(match.id);
The whole thing runs in tens of milliseconds on a mid-range phone.
Liveness: making spoofing hard
Embeddings alone can't tell a live face from a printed photo. So FaceGuard adds challenge-response liveness: the app asks you to blink or smile, and verifies the action happened over a sequence of frames before accepting.
The stack
- Expo SDK + React Native
- TFLite for inference
- SQLite for the local embedding store
- Zustand for state, Reanimated for the camera UI
- TypeScript throughout
What I learned
On-device ML is a different discipline from cloud ML. You're constantly trading model size vs accuracy vs latency, and you can't hide a slow model behind a loading spinner — the user is staring at their own face. Quantization and a tight pre-processing pipeline mattered more than the model architecture itself.