npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-face-match

v0.7.2

Published

Expo module for on-device face matching across a device photo library using FaceNet embeddings and ObjectBox vector storage.

Downloads

145

Readme

react-native-face-match

On-device face similarity search for React Native. Give it a reference photo and it scans the device gallery to find matching faces — fully offline, no cloud API needed.

Built as an Expo module using FaceNet 512 (TensorFlow Lite) for face embeddings and ObjectBox as the on-device vector store.


How it works

  1. Extracts a 512-dimensional face embedding from your reference image using FaceNet.
  2. Scans the device photo library (up to 3,000 photos), detects faces, and stores their embeddings in ObjectBox.
  3. Finds photos whose embeddings are within a cosine-distance threshold of the reference.
  4. Emits real-time progress events so you can show a live progress bar.

Results from previous runs are cached — subsequent searches only process new photos.


Installation

npm install react-native-face-match
# or
yarn add react-native-face-match

iOS

Run pod install after adding the package:

cd ios && pod install

Add the following keys to your Info.plist:

<key>NSPhotoLibraryUsageDescription</key>
<string>We need access to your photo library to find matching faces.</string>

The FaceNet model (facenet_512.tflite, ~23 MB) is bundled inside the iOS pod — no download step needed on iOS.

Android

Add the following permissions to your AndroidManifest.xml:

<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- For Android 12 and below -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.INTERNET" />

Unlike iOS, the Android model is not bundled — you must host facenet_512.tflite yourself and call downloadModel() before the first use.


Quick start

import FaceMatch from 'react-native-face-match';

// Android only: download the model once before first use
if (Platform.OS === 'android') {
  await FaceMatch.downloadModel('https://your-cdn.example.com/facenet_512.tflite', false);
}

// Check if the reference image contains a face
const hasFace = await FaceMatch.isFaceAvailable(referenceImageUri);

// Subscribe to live progress
const sub = FaceMatch.addListener('FaceComparisonProgress', (data) => {
  console.log(`${data.progressPercent}% — ${data.totalProcessed}/${data.totalImages}`);
  console.log('Current matches:', data.data?.results);
});

// Run the gallery search
const result = await FaceMatch.getPhotos(referenceImageUri);
console.log('Matches:', result.data);

sub.remove();

API

downloadModel(modelUrl, forceDownload) — Android only

Downloads the FaceNet TFLite model from your CDN. Must be called before getPhotos() or isFaceAvailable() on Android.

FaceMatch.downloadModel(modelUrl: string, forceDownload: boolean): Promise<{
  success: boolean;
  modelPath: string;
  fileSize: number;
  exists: boolean;
}>

| Parameter | Type | Description | |-----------|------|-------------| | modelUrl | string | HTTPS URL to your hosted facenet_512.tflite | | forceDownload | boolean | Re-download even if the model already exists locally |


isFaceAvailable(imageUri)

Returns true if a face is detected in the given image. Use this to validate the reference photo before starting a search.

FaceMatch.isFaceAvailable(imageUri: string): Promise<boolean>

Accepts file://, ph:// (iOS Photo Library), or base64 data URIs.


getPhotos(referenceImageUri)

Scans the device photo library and returns photos that match the reference face. This is the main method.

FaceMatch.getPhotos(referenceImageUri: string): Promise<{
  data: PhotoMatch[];
  time: string;
  totalImagesProcessed: string;
  timeTakenToScanGalleryInSec: string;
  timeTakenToGetDataFromDbInSec: string;
  failedProcessed: number;
  finalProcessedImages: number;
  finalAvailableMemoryMB: string;
  finalMemoryUsage: string;
}>

PhotoMatch object:

| Field | Type | Description | |-------|------|-------------| | matchedImagePath | string | URI of the matched gallery photo | | score | string | Similarity score (lower = more similar; threshold is 0.35) | | photoTimestamp | number | Unix timestamp (ms) of when the photo was taken |

Requires photo library permission. Automatically skips screenshots, WhatsApp media, and cache files.


compareSinglePhoto(imageUri, referenceImageUri)

Compares two specific images and returns a similarity score. Useful for one-off comparisons without scanning the gallery.

FaceMatch.compareSinglePhoto(imageUri: string, referenceImageUri: string): Promise<{
  score: string;
  photoTimestamp: string;
}>

addListener('FaceComparisonProgress', callback)

Subscribe to real-time progress updates during getPhotos(). Events fire approximately every 2% of processing progress.

const subscription = FaceMatch.addListener('FaceComparisonProgress', (data) => {
  // data.progressPercent    — 0–100
  // data.totalImages        — total gallery images to scan
  // data.totalProcessed     — images processed so far
  // data.successfulProcessed
  // data.failedProcessed    — images with no face or load error
  // data.remainingImages
  // data.data?.results      — current top matches (updated in real time)
});

// Always remove the listener when done
subscription.remove();

expirePhotoPickerCache()

Clears the list of already-processed images. The next getPhotos() call will re-process the entire gallery.

FaceMatch.expirePhotoPickerCache(): void

clearObjectBoxDatabase()

Deletes all stored face embeddings from ObjectBox. Combined with expirePhotoPickerCache(), this gives you a full reset.

FaceMatch.clearObjectBoxDatabase(): void

Events

| Event name | Payload | |------------|---------| | FaceComparisonProgress | { progressPercent, totalImages, totalProcessed, successfulProcessed, failedProcessed, remainingImages, data: { results: PhotoMatch[] } } |


Accepted image formats

| Format | iOS | Android | |--------|-----|---------| | file:// path | ✅ | ✅ | | ph:// Photo Library URI | ✅ | ❌ | | Base64 data URI (data:image/...) | ✅ | ✅ | | JPEG / HEIC / PNG | ✅ | ✅ |


Performance notes

  • Caching: embeddings computed in previous runs are stored in ObjectBox. Repeat searches only process new photos — no redundant work.
  • Concurrency: uses a producer-consumer pipeline with up to 4 producers and 8 consumers running in parallel (scaled to available CPU cores).
  • Gallery limit: scans up to 3,000 of the most recent photos.
  • Model size: the FaceNet 512 model is ~23 MB. On iOS it is bundled with the pod. On Android it is downloaded on first use.
  • Similarity threshold: the default match threshold is a cosine distance of 0.35. Scores below this threshold are considered a match.

Example

A full working example is in example/App.tsx. It demonstrates:

  • Downloading the model (Android)
  • Picking a reference photo and validating it has a face
  • Running a gallery search with a live progress bar
  • Comparing a single photo
  • Clearing the cache

To run the example, install expo-image-picker alongside this package:

npx expo install expo-image-picker

Platform requirements

| | Minimum | |-|---------| | iOS | 15.1 | | Android | API 21 (Android 5.0) | | Expo SDK | 49+ |


License

MIT