Building a Real-Time Facial Recognition Attendance System: The Hard Parts
Building a facial recognition demo is straightforward — there are excellent open-source models, plenty of tutorials, and a webcam is all you need to get something that looks impressive in a five-minute walkthrough. Building one that works reliably across office entrances and warehouse floors, with varying lighting, masks, glasses, and motion, tracked across 10,000-plus employees every single working day, is a completely different engineering problem. The gap between "works in a demo" and "works in production" is where almost all of the real effort in this project actually went.
The Stack
The system runs on a Node.js backend, with OpenCV and TensorFlow handling detection and recognition, a React frontend for admin and kiosk interfaces, and Firebase for real-time synchronization of check-in events across entrances.
Hard Part 1: Anti-Spoofing
The first version of the system could be fooled by holding a photo of an employee's face up to the camera on a phone screen. This isn't a hypothetical attack — it's the first thing anyone testing a facial recognition system tries, deliberately or accidentally, and it needs to fail loudly before the system goes anywhere near production.
We addressed this with two complementary checks:
- Liveness detection — the system requires a brief natural motion cue (a slight head turn, a blink) that a static photo can't reproduce convincingly, rather than trusting a single still frame.
- GPS-based location verification — a check-in is only accepted from a device whose location corresponds to an actual registered office or warehouse entrance, which rules out an entire class of remote-spoofing attempts.
False-positive spoofing attempts dropped sharply once both checks were in place together — neither one alone was sufficient, but the combination closed most of the practical attack surface without adding noticeable friction to a genuine check-in.
Hard Part 2: Low-Light Accuracy
Office and warehouse entrances have wildly inconsistent lighting — a warehouse loading bay at 6 a.m. looks nothing like a well-lit office lobby at noon. The original recognition pipeline, trained mostly on well-lit reference photos, had a false-reject rate at these entrances high enough that employees started manually overriding the system constantly just to get through the door on time — which defeats the entire point of an automated attendance system.
We addressed this in two ways. First, we retrained the recognition model on a dataset specifically augmented with low-light and partial-occlusion samples, including employees wearing masks — not just clean, well-lit reference images. Second, we added an active lighting-check step: when ambient light at an entrance falls below a threshold, the system prompts a second capture attempt with adjusted exposure settings, rather than silently failing the first attempt and leaving the employee standing at the door wondering what went wrong.
A system that fails silently trains its users to distrust it. Prompting for a clear second attempt, instead of a bare rejection, was a small UX change that had an outsized effect on how much employees actually relied on the system versus routing around it.
Hard Part 3: GDPR-Compliant Biometric Storage
Facial recognition data is special category data under GDPR — it sits in the same sensitive tier as health data or religious belief, and mishandling it carries real regulatory and reputational risk, independent of how good the recognition accuracy is.
The key architectural decision here was simple to state and important to actually enforce: we do not store raw face images anywhere in the system. Instead, we store only the face embedding vector — a numerical representation used for matching — encrypted at rest. The original captured image is discarded immediately after the embedding is extracted, and never touches persistent storage.
This has a very practical benefit beyond the compliance argument itself: a deletion request under GDPR's right to erasure is handled by deleting the embedding record. There's no original image sitting in some backup or object store that a deletion process might miss.
// Simplified capture pipeline
async function processCheckIn(frameBuffer) {
const embedding = await extractFaceEmbedding(frameBuffer);
// frameBuffer is discarded here — never written to disk or storage
const match = await matchEmbedding(embedding, employeeEmbeddings);
await recordAttendance(match.employeeId, { timestamp: Date.now() });
return match;
}
Data Classification Summary
| Data | Stored? | Retention |
|---|---|---|
| Raw captured image | No — discarded after processing | N/A |
| Face embedding vector | Yes, encrypted at rest | Duration of employment |
| Attendance timestamp | Yes | Per statutory record-keeping requirement |
Hard Part 4: Real-Time Sync at Scale
With thousands of employees checking in within the same tight morning window — everyone arriving between 8:45 and 9:00, effectively — the real-time database was getting hammered with a burst of near-simultaneous writes. Firebase handled the raw throughput reasonably well, but rapid retries from a flaky connection at an entrance kiosk could produce duplicate check-in records for the same person within seconds of each other.
We fixed this by batching attendance writes into short windows rather than writing on every single event immediately, and wrapping the actual write in a transaction keyed on employee ID and a coarse time bucket, so a rapid retry is recognized as a duplicate and discarded rather than recorded as a second check-in.
Best Practices
- Never store raw biometric images if an embedding is sufficient for your matching use case — it removes an entire category of compliance and breach risk.
- Design for the actual deployment environment's lighting and occlusion conditions, not just a well-lit reference dataset.
- Combine multiple weak signals (liveness, location) rather than relying on any single anti-spoofing check.
- Make failure visible and actionable to the end user — a silent rejection erodes trust in the system faster than an occasional false reject with clear guidance.
Common Mistakes
- Training and validating recognition models only on clean, well-lit reference images, then being surprised by real-world false-reject rates.
- Storing raw face images "just in case," which multiplies your compliance exposure for no real product benefit.
- Treating anti-spoofing as a single check rather than a layered defense — a single signal is almost always beatable.
- Writing attendance events without deduplication logic, then discovering duplicate check-ins under real network conditions at scale.
Security Considerations
Beyond the GDPR storage design, the embedding database itself is a high-value target — compromising it doesn't directly reveal a face image, but it could still enable an impersonation attack against the matching system. Embeddings are encrypted at rest, access is scoped and audited, and the matching service itself runs with no direct external network exposure.
FAQ
Q: Can you reconstruct someone's face from a stored embedding?
Not directly with the embedding models we use — they're designed for comparison, not reconstruction — but we still treat embeddings as sensitive data and encrypt them at rest, since the regulatory classification for biometric data doesn't hinge on reversibility alone.
Q: How do you handle an employee who changes their appearance significantly (new glasses, beard, etc.)?
The matching threshold has tolerance built in for gradual change, and there's a manual re-enrollment flow for cases where the change is significant enough to affect match confidence consistently.
Rolling This Out Across Multiple Site Types
Offices and warehouses aren't just different in lighting — they're different in almost every dimension that matters for a biometric attendance system. Office entrances tend to have a single, well-defined doorway and a predictable flow of people arriving within a narrow morning window. Warehouse floors often have multiple entry points, higher ambient noise and dust that can affect camera hardware over time, and shift patterns that spread arrivals across a wider window but with higher instantaneous concentration at shift-change moments.
We initially deployed a single configuration — camera settings, lighting thresholds, retry behavior — across both site types, on the assumption that the underlying recognition model was the variable that mattered and everything else could stay uniform. That assumption didn't hold up. Warehouse entrances needed a more aggressive dust-tolerant camera housing and a higher default lighting-check threshold, given the more variable ambient light near loading bays. We ended up building a per-site-type configuration profile rather than a single global one, which added operational complexity but meaningfully reduced false rejects at warehouse sites specifically.
Handling Enrollment for a Large, Distributed Workforce
Enrolling 10,000-plus employees into a biometric system isn't a one-time bulk import — new employees join continuously, and the enrollment process itself needed to be fast enough not to become a bottleneck for onboarding. We built a self-service enrollment kiosk flow that captures several reference angles under guided lighting conditions, rather than relying on a single HR-submitted photo, since photos submitted for other purposes (ID badges, HR records) were frequently taken under lighting conditions that didn't match the deployed entrances at all, and using them directly as the enrollment reference measurably increased false rejects for new employees in their first weeks.
A Practical Enrollment Checklist
1. Capture 3-5 reference angles under guided, on-site lighting
(not a submitted ID photo).
2. Run a liveness check during enrollment itself, so a spoofed
enrollment can't be used to bootstrap a fraudulent identity.
3. Store only the resulting embeddings; discard captured frames
immediately after extraction.
4. Flag low-confidence embeddings for manual re-enrollment rather
than accepting a borderline capture.
Conclusion
None of the four hard parts here were about getting a recognition model to run — that part is largely solved territory. They were about making the system trustworthy under real lighting, real attack attempts, real regulatory scrutiny, and real concurrent load. Recognition accuracy reached 99.9% in target deployment conditions, mask detection and anti-spoofing shipped without any hardware upgrade, and the system now runs reliably across 10,000-plus tracked employees — which is the actual bar for "production," not the demo.