Real-time selfie validation SDK with face detection, powered by MediaPipe. Detects faces, hands, and validates pose, lighting, and occlusions in real-time.
- ✅ Distance validation: TOO_CLOSE / TOO_FAR
- ✅ Centering: Face must be centered in oval guide
- ✅ Head pose: Detects tilted or turned head
- ✅ Illumination: Validates proper lighting
- ✅ Stability: Ensures user stays still before capture
- ✅ Multiple faces: Rejects when more than one face detected
- ✅ Hand near face detection: Prevents hand covering face (obstructions)
- ✅ 21 landmarks per hand: High precision tracking
- ✅ Real-time validation: Instant feedback
- 🌐 i18n: Portuguese (pt-BR), English (en), Spanish (es)
- 🎨 Visual feedback: Oval guide with color-coded status
- 🐛 Debug mode: Visualize landmarks and bounding boxes
- 📦 Multiple builds: ESM, CJS, UMD
- 🚀 GPU accelerated: Powered by MediaPipe with GPU support
For any web application (React, Angular, Vue, vanilla JS, Java backend with JS frontend, etc.) that wants to use the core validator:
npm install face-validator-sdkThe SDK declares
@mediapipe/tasks-vision(^0.10.15) as a regular dependency, so it is installed automatically when you installface-validator-sdk.
import { FaceValidator, ValidationStatus } from 'face-validator-sdk';
const validator = new FaceValidator({
container: '#selfieContainer',
ui: 'default',
locale: 'pt-BR',
debugMode: false,
mirror: true,
onStatusUpdate: (status, message) => {
console.log(status, message);
},
onCaptureSuccess: (blob) => {
const url = URL.createObjectURL(blob);
document.querySelector('img')!.src = url;
},
onError: (errorType, error) => {
console.error(errorType, error);
}
});
// The validator starts automatically.
// To stop and release resources: validator.destroy();| Status | Description |
|---|---|
INITIALIZING |
Loading MediaPipe models |
NO_FACE_DETECTED |
No face found in frame |
FACE_DETECTED |
Face detected, validating... |
TOO_CLOSE |
Face too close to camera |
TOO_FAR |
Face too far from camera |
OFF_CENTER |
Face not centered in oval |
FACE_OBSTRUCTED |
Hand covering face or low visibility |
HEAD_NOT_STRAIGHT |
Head tilted or turned |
MULTIPLE_FACES |
More than one face detected |
POOR_ILLUMINATION |
Insufficient lighting |
STAY_STILL |
Hold still for capture |
CAPTURING |
Taking photo... |
SUCCESS |
Capture successful! |
ERROR |
An error occurred |
interface FaceValidatorOptions {
// UI structure
container?: HTMLElement | string; // Element or selector to auto-render video/canvas and status
ui?: 'default' | 'none'; // Default: 'default'
autoStart?: boolean; // Default: true
mirror?: boolean; // Default: true
// Camera
videoElement?: HTMLVideoElement; // Use if you want to control the video manually
overlayCanvasElement?: HTMLCanvasElement | null;
videoConstraints?: MediaTrackConstraints;
videoWidth?: number;
videoHeight?: number;
// Idioma e debug
locale?: 'pt-BR' | 'en' | 'es';
debugMode?: boolean;
customMessages?: Partial<Record<ValidationStatus, string>>;
// Callbacks
onStatusUpdate?: (status: ValidationStatus, message: string) => void;
onCaptureSuccess?: (imageBlob: Blob) => void;
onError?: (errorType: ValidationStatus, error: Error) => void;
// Thresholds de validacao
minDetectionConfidence?: number;
minIlluminationThreshold?: number;
minFaceSizeFactor?: number;
maxFaceSizeFactor?: number;
stabilizationTimeThreshold?: number;
stabilityMovementThreshold?: number;
minFaceVisibilityScore?: number;
maxHeadTiltDegrees?: number;
maxHandFaceDistance?: number;
// Advanced
modelPath?: string; // Path to MediaPipe WASM (auto-detected via CDN)
}import { ReactSelfieCapture } from 'face-validator-sdk';
export function SelfieModal() {
const handleCapture = (imageBase64: string | null) => {
if (!imageBase64) return;
// Send the selfie to your API
};
return (
<ReactSelfieCapture
locale="pt-BR"
onCapture={handleCapture}
onDismiss={() => console.log('Modal closed')}
debugMode={false}
labels={{
previewQuestion: 'Check your selfie before saving',
savePhoto: 'Confirm selfie'
}}
/>
);
}locale: defines the language for messages and labels displayed.onCapture: main callback; receives the base64 image ornullif the user cancels.onDismiss: used to close the modal/dialog when the user clicks cancel.debugMode: enables landmark visualization for debugging.labels: overrides default texts (e.g., preview title and button label).
import { AfterViewInit, Component, OnDestroy } from '@angular/core';
import { FaceValidator, ValidationStatus } from 'face-validator-sdk';
@Component({
selector: 'app-selfie-dialog',
template: '<div id="selfieContainer"></div>'
})
export class SelfieDialogComponent implements AfterViewInit, OnDestroy {
private validator: FaceValidator | null = null;
ngAfterViewInit(): void {
this.validator = new FaceValidator({
container: '#selfieContainer',
ui: 'none',
locale: 'pt-BR',
debugMode: false,
mirror: true,
onStatusUpdate: (status: ValidationStatus, message: string) => {
console.log(status, message);
},
onCaptureSuccess: (blob: Blob) => {
console.log('Selfie capturada', blob);
},
onError: (errorType: ValidationStatus, error: Error) => {
console.error(errorType, error);
}
});
}
ngOnDestroy(): void {
this.validator?.destroy();
}
}container: target element where the SDK automatically renders video and canvas.ui: usenoneto render your own UI (status, buttons, etc.).locale: defines the language for SDK messages.debugMode: displays landmarks for debugging during development.mirror: mirrors the camera (selfie default).onStatusUpdate: receives the status and message for you to update the UI.onCaptureSuccess: receives theBlobof the captured selfie for upload/preview.onError: captures camera or model loading errors.
If you are building a React application and want a ready‑to‑use selfie capture UI, the SDK exposes an optional React component that encapsulates:
- Camera access (
getUserMedia) - Validation loop (
FaceValidator) - Overlay drawing (oval + feedback)
- Preview step (photo + buttons)
- i18n for pt-BR, en, es
Your React app should already have react and react-dom installed. Then:
npm install face-validator-sdk
@mediapipe/tasks-visionis installed automatically as a dependency of the SDK.
reactandreact-domare declared as peerDependencies – they are not bundled inside the package.
Non‑React applications should use the coreFaceValidatorAPI shown above instead ofReactSelfieCapture.
import { ReactSelfieCapture } from 'face-validator-sdk';
function SelfieExample() {
const handleCapture = (imageBase64: string | null) => {
if (imageBase64) {
// send to API, store, etc.
}
};
return (
<ReactSelfieCapture
locale={navigator.language} // 'pt-BR' | 'en' | 'es' (auto-normalized)
onCapture={handleCapture}
onDismiss={() => console.log('Modal closed')}
/>
);
}type SupportedLocale = 'pt-BR' | 'en' | 'es';
type SelfieCaptureStyles = {
container?: React.CSSProperties;
media?: React.CSSProperties;
messageBanner?: React.CSSProperties;
primaryButton?: React.CSSProperties;
secondaryButton?: React.CSSProperties;
};
type SelfieCaptureUILabelOverrides = Partial<{
previewQuestion: string;
savePhoto: string;
tryAgain: string;
cancel: string;
}>;
interface ReactSelfieCaptureProps {
onCapture: (image: string | null) => void; // base64 data URL or null on cancel
onDismiss?: () => void;
// Behaviour
locale?: SupportedLocale | string; // Default: 'pt-BR' (auto-normalized)
videoWidth?: number; // Default: 512
videoHeight?: number; // Default: 384
debugMode?: boolean; // Default: false
modelPath?: string; // Optional MediaPipe WASM path; if omitted, uses internal CDN default
// Visual customization (inline styles)
styles?: SelfieCaptureStyles;
// Optional UI labels override (per-locale defaults exist)
labels?: SelfieCaptureUILabelOverrides;
}By default, the component renders UI labels in Portuguese (pt-BR), English (en) or Spanish (es):
- Preview question (“O que você achou?” / “What do you think?” / “¿Qué te pareció?”)
- Buttons (“Salvar foto”, “Tentar novamente”, “Cancelar”, etc.)
You can override any of these without having to set up an external i18n layer:
<ReactSelfieCapture
locale="pt-BR"
onCapture={handleCapture}
labels={{
previewQuestion: 'Confira sua selfie antes de salvar',
savePhoto: 'Confirmar selfie',
}}
/>The component ships with a sensible default layout, but you can tweak it via the styles prop:
<ReactSelfieCapture
onCapture={handleCapture}
styles={{
container: { borderRadius: 24 },
media: { borderRadius: 16 },
messageBanner: { backgroundColor: '#f0f9ff', color: '#0369a1' },
primaryButton: { backgroundColor: '#2563eb', borderColor: '#2563eb' },
secondaryButton: { borderRadius: 9999 },
}}
/>;Tip: you can wrap
ReactSelfieCapturein your own modal/dialog and passonDismissto close it from the Cancel button.
The SDK uses two MediaPipe models running in parallel:
- FaceLandmarker: 478 facial landmarks + face detection
- HandLandmarker: 21 hand landmarks per hand
Migrated from face-api.js (discontinued 2021) to MediaPipe (Google):
| Feature | face-api.js | MediaPipe |
|---|---|---|
| Landmarks | 68 points | 478 points |
| Hand detection | ❌ None | ✅ 21 pts/hand |
| Maintenance | ❌ Discontinued | ✅ Active (Google) |
| Performance | CPU only | ✅ GPU accelerated |
| Accuracy | ~60-70% | ✅ ~90-95% |
| Model size | ~8MB | ~15MB |
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'feat: add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
MIT License - see LICENSE file for details.
- MediaPipe by Google
- face-api.js (original inspiration)
- 🐛 Report Bug
- 💡 Request Feature
- 📧 Contact: GitHub Profile