# FaceX: a full face pipeline in WebAssembly, with no server in the loop

> FaceX bundles detection, landmarks, a 576-point 3D mesh, recognition and passive anti-spoof into one C codebase that runs in the browser or natively on CPU. The pitch is zero cloud and zero Python; the trade-off is that you carry the weights and pick your own threshold.

**facex-engine/facex** — Full face stack that runs entirely in the browser. Detection, 576-point 3D mesh, recognition, anti-spoof, smile — all WebAssembly, zero server. Apache 2.0.

- Repository: https://github.com/facex-engine/facex
- Website: https://facex-engine.github.io/facex/demo/
- Stars: 342 · Forks: 52
- Language: C
- License: Apache-2.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/facex-engine-facex

## The problem FaceX targets: face matching without a round trip to someone else's datacenter

Most face-matching products are APIs. You send a face to AWS Rekognition, Azure Face, Google Vision or FaceTec, you get a score back, and you pay per call. The README frames the cost directly: for a 100K-user app doing one match per user per day, it lists AWS Rekognition CompareFaces at $1.00 per 1,000 matches and roughly $3,000 per month, Azure at $3,000 to $4,500, Google Vision at $4,500. FaceX is the opposite arrangement. It is a C library, compiled to WebAssembly for the browser and to native code for x86-64, that performs detection, alignment, embedding and anti-spoof on the device. The README states the weights stay in the user's browser and the latency is 20 to 30 ms.

The audience is narrower than the marketing implies. FaceX suits teams building KYC selfie-to-ID comparison, offline face login, access control on edge hardware, exam proctoring, or in-store kiosks where a $30 SoC is the target. It does not suit teams that want a managed service with a support contract, or teams without anyone who can compile C and mount a weights directory.

## How the pipeline is assembled: detector, landmarks, mesh, embedding, anti-spoof

The stack is five models, and the README is explicit that four of them were trained in-house. The detector is a YuNet-style FCOS network trained on WIDER FACE at 401 KB. Landmarks are 98 points trained on WFLW at 1.1 MB. The dense mesh is 576 points distilled from MediaPipe at 5.6 MB. Recognition ships in four sizes from 0.8 MB to 8.4 MB, built on MobileFaceNet plus ArcFace trained on MS1M, with LFW accuracy from 95.62% to 99.07% reported as a 10-fold mean. Anti-spoof is the exception: two MiniFASNet models of 1.7 MB each, credited to MinivisionAI Silent-Face and licensed Apache 2.0.

The native path exposes this as a small C API. You initialise from a weights file, hand it a 112x112 aligned face, and get a 512-float embedding back:

```c
#include "facex.h"
FaceX* fx = facex_init("facex_xs.bin", NULL);
float emb[512];
facex_embed(fx, face_112x112, emb);
float sim = facex_similarity(emb_a, emb_b);   // >0.3 = same person
```

The comparison threshold of 0.3 is stated in the README comment, not derived from a published ROC curve. That is the single number you will spend the most time re-tuning, because it depends on your camera, your lighting and your tolerance for false accepts.

Weights ship as AES-256-GCM ciphertext and are decrypted in the browser through WebCrypto. The README is unusually honest about what that buys: it says this is not DRM and a determined attacker can dump the decrypted bytes from the WASM heap. The stated benefits are friction against casual scraping, per-customer key revocation for SaaS deployments, and an audit trail at the key-issuing endpoint.

## Building and running FaceX locally

The repository ships a Makefile with separate targets for the static library, a CLI, an example program, an encryption tool and a golden test. The default target builds all of the library and CLI artefacts. The Makefile probes the compiler for AVX-512 support and adds -mavx512f, -mavx512vnni and -mprefer-vector-width=512 when it finds it, otherwise it falls back to the AVX2 and FMA flags in the base CFLAGS.

```bash
make
make example && ./facex-example
```

The golden test is the quickest way to confirm your build matches the reference weights, and it takes the fp32 weight file as an argument:

```bash
make test
./golden-test data/edgeface_xs_fp32.bin
```

For the browser path, the README gives a two-command recipe that serves the wasm directory over plain HTTP:

```bash
git clone https://github.com/facex-engine/facex
cd facex/wasm && python -m http.server 8000
# open http://127.0.0.1:8000/demo_mesh.html
```

There is also a Dockerfile that builds a REST API rather than a demo. It compiles a static facex-server with gcc 13 and -march=x86-64-v3, then copies it into an Alpine 3.19 image running gunicorn on two workers. The exposed port is 8080 and the endpoints are POST /detect, POST /embed, POST /compare and GET /health. The Dockerfile comments state that weights must be mounted or baked in, which means the image alone is not a working service:

```bash
docker build -t facex .
docker run -p 8080:8080 facex
```

Running that container without a weights mount will start gunicorn but the face endpoints have nothing to initialise from. The README does not document rollback of a weights update, so plan your own versioning for the mounted directory.

## Where FaceX is the wrong tool

The anti-spoof models are the weakest link in the ownership story. Detection, landmarks, mesh and recognition are all trained by the project, but the two MiniFASNet models come from MinivisionAI Silent-Face. That is a third-party dependency inside a project whose main selling point is that everything is yours. If you need to retrain liveness for a specific attack class, such as a printed mask under infrared, the README does not describe how.

The encrypted weights also cut both ways. If your deployment requires reproducible builds with pinned, auditable artefacts, shipping AES-256-GCM ciphertext means the bytes you distribute are not the bytes the model runs. The README acknowledges the decryption happens in the browser and that the plaintext is recoverable from the WASM heap. Per-customer key revocation is a real operational feature, but it also means a key-management system you now have to run.

Platform coverage is uneven. The README marks browser, Linux, macOS and Windows x86-64 as shipping. Apple Silicon, ARM Linux and Android are described as being in PR #3, and the NXP i.MX NPU, ESP32-P4 and bare-metal MCU targets are drafts or in progress. If your product is an iOS app or an Android build, the README does not claim those targets are shipping today.

Finally, the accuracy claim needs context. LFW at 99.07% is a 10-fold mean on a benchmark that is widely regarded as saturated. It says nothing about your surveillance footage, your low-light selfies, or your demographic distribution.

## FaceX against InsightFace and the cloud APIs

The obvious open-source comparison is InsightFace, which the README names in its replacement table alongside dlib and FaceNet. The difference is packaging and deployment target rather than model quality. InsightFace is a Python-first stack built on ONNX Runtime and PyTorch, which means a Python runtime, a model zoo download, and a server process. FaceX is C99 with hand-written SIMD kernels and its own inference engine, nn2, which the README claims runs YOLOv8 and MiniFASNet at 1.5 to 2x ONNX Runtime speed on the same hardware. That claim is the project's own, not an independent measurement, and it is the number to verify yourself before you build a capacity plan around it.

Against the cloud APIs the difference is architectural, not numerical. AWS Rekognition, Azure Face, Google Vision and FaceTec ZoOm all receive the face image. FaceX does not, which is what makes the GDPR framing in the README plausible: there is no processor, because there is no transmission. The counter-argument is that you become responsible for the model, the thresholds, the liveness calibration and the key management that a vendor would otherwise own. For a small team, that is a real cost even at $0 per match.

If your constraint is a web app that must not send biometric data anywhere, FaceX is close to the only shape of solution in this list. If your constraint is maximum accuracy with minimum engineering, the cloud APIs remain the easier choice.

## Licence, maintenance and the cost of staying current

FaceX is Apache 2.0, and the repository carries a LICENSE file at the top level. The bundled MiniFASNet anti-spoof models are also Apache 2.0 per the README table, which matters because it means the whole shipped set is under one permissive licence with no GPL component. The README makes a point of the absence of FFmpeg in the wider stack specifically to avoid GPL contamination and codec CVE surface. That is a statement about the sibling projects, not about FaceX itself, but it reflects the same design intent. This is not legal advice; if you redistribute the encrypted weights you should read the licence and the MiniFASNet attribution yourself.

The last push to the default branch was on 2026-05-14, and the most recent release, facex-nano-1.0, was tagged the same day. The initial v1.0.0 release was on 2026-04-26. The repository is not archived. The upgrade surface is small in one sense, because there are no runtime dependencies beyond libm and pthread on the native side, and onnxruntime-node plus sharp in the Node package manifest. It is larger in another: the weights are versioned artefacts, and the golden test exists precisely so you can detect when a new weight file changes the embeddings. Run make test against each new weights file before you swap it into a deployment.

## Conclusion

Adopt FaceX if you need face matching that never leaves the device and you are willing to own the weights, the thresholds and the calibration. Do not adopt it if you need vendor-managed accuracy guarantees, a hosted liveness attestation, or a documented rollback path, because the README does not describe one. Before committing, verify on your own hardware what the 99.07% LFW figure means for your camera and lighting, and check that facex_similarity returns the separation you need at the 0.3 cutoff the README uses.

## FAQ

### What is FaceX?

FaceX is a face pipeline written in C and compiled to WebAssembly, covering detection, 98-point landmarks, a 576-point 3D mesh, recognition and passive anti-spoof. It runs in the browser or natively on CPU with no server component, and it is licensed Apache 2.0.

### Does FaceX need a GPU or a Python environment?

No. The README describes the stack as zero-dependency C99 with hand-written SIMD kernels, and the native build links only against libm and pthread. The browser path runs through onnxruntime-web.

### What similarity threshold does FaceX use to decide two faces match?

The README's C example uses a comment stating that a facex_similarity value above 0.3 means the same person. That number is a starting point, not a calibrated operating point, so you should re-tune it for your camera and lighting.

### Are the FaceX model weights encrypted?

Yes. The README states the roughly 17 MB of WASM weights ship as AES-256-GCM ciphertext and are decrypted in the browser via WebCrypto. It also states plainly that this is not DRM and the decrypted bytes can be dumped from the WASM heap.

## Sources

- [facex-engine/facex on GitHub](https://github.com/facex-engine/facex)
- [License: Apache-2.0](https://github.com/facex-engine/facex/blob/main/LICENSE)
- [Project website](https://facex-engine.github.io/facex/demo/)
- [README](https://github.com/facex-engine/facex/blob/main/README.md)
- [Releases](https://github.com/facex-engine/facex/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/facex-engine-facex
