@meeeetup/camera-core
v0.6.0
Published
Platform-agnostic core for the meeeetup-cam face-capture SDK
Downloads
635
Readme
@meeeetup/camera-core
Platform-agnostic face-capture pipeline for the Meeeetup face-ID service: face tracking, pose-quality scoring, best-frame selection and a bounded send buffer. No DOM, no native modules, no network access, zero runtime dependencies.
Most integrators want a platform package instead:
- browsers →
@meeeetup/camera-web - React Native →
@meeeetup/camera-react-native
Use this package directly only when you supply your own detector and frame source — for example a server-side or worker pipeline. ESM-only.
Install
npm i @meeeetup/camera-coreHow it works
You feed it detections plus a frame, it hands back the best image per person:
Detection[] → NMS → track matching → frontalness scoring
→ 3-gate commit → SendBuffer → onBatchCapture(SelectedFace[])A face is committed once it has been confirmed for at least 3 frames and 300 ms
with a frontalness score above minFrontalness (default 50), and the same face
is re-sent at most once per cooldownMs (default 10 s). Set captureWindowMs
to swap that settle gate for a bounded per-track best-shot window instead.
Usage
import { FaceCaptureSession, type Detection, type FrameBuffer } from "@meeeetup/camera-core";
const session = new FaceCaptureSession({
sessionType: "passive",
onBatchCapture: async (faces) => {
await upload(faces); // your transport — the SDK never calls the network
},
});
// Per frame, from your detector:
session.processDetections(detections, frameBuffer);
session.tick(); // per animation frame: ages tracks, draws nothing
await session.flushBatch(); // optional manual flush, e.g. on page hide
session.dispose();Two contracts you must satisfy:
interface Detection {
boundingBox: { originX: number; originY: number; width: number; height: number }; // 0–1
keypoints: Array<{ x: number; y: number }>; // [0] rightEye [1] leftEye [2] nose
score: number; // detector confidence 0–1
}
interface FrameBuffer {
readonly width: number;
readonly height: number;
toJpegBase64(quality?: number): string;
cropFaceJpeg(cx: number, cy: number, bw: number, bh: number, quality?: number): string | null;
}Keypoint slot [2] is the nose. Pass noseLandmark: "noseBase" when your
detector reports the nose base rather than the tip (ML Kit does) so frontalness
scores stay comparable across detectors.
Session options
| Option | Default | Meaning |
|---|---|---|
| sessionType | required | "passive" (batched) or "interactive" (single shot) |
| onBatchCapture | — | Output sink. Required in passive mode; without it every face is discarded |
| batchIntervalMs | 10000 | How often a passive session flushes |
| cooldownMs | 10000 | Minimum ms between sends for the same face |
| minDetectionScore | 0.5 | Detector confidence floor, applied before any pose analysis |
| minFaceHeight | 0 (off) | Minimum face-box height as a fraction of frame height (0–1), applied with minDetectionScore |
| minFrontalness | 50 | Pose score a frame must reach to count as a confirmed frame, and to be selectable inside a capture window |
| captureWindowMs | 0 (off) | Per-track best-shot window in ms. 0 keeps the settle-based commit gate |
| maxBufferedFaces | 200 | Retained while offline; oldest dropped beyond this |
| maxFacesPerRequest | 50 | Larger backlogs split across batches |
| noseLandmark | "noseTip" | Which landmark occupies keypoint [2] |
Callbacks: onSelect, onTrackRemoved, onBatchSent, onBatchError.
With captureWindowMs set, each track opens its own window once it is
confirmed and has 3 frames at or above minFrontalness. Every better frame is
then published to onSelect straight away with final: false, and at expiry
the held frame is published once more with final: true. A closed window
re-arms after cooldownMs in passive mode, or on session.rearm() in
interactive mode.
Throw from onBatchCapture to report a failed send. A thrown error with no
status, or a status below 400 or 500 and above, is requeued; a 4xx drops that
batch, because resending an unchanged rejected payload livelocks.
Licence
Proprietary — see LICENSE. Use requires a current agreement with MeeeetUp1120.
