DEV Community

Cover image for đź“·Building a Face Recognition Attendance System with Next.js, TypeScript, face-api.js, and Supabase
Ferry Ananda Febian
Ferry Ananda Febian

Posted on

đź“·Building a Face Recognition Attendance System with Next.js, TypeScript, face-api.js, and Supabase

Hi, Devs! đź‘‹

In this article, I want to share my experience building a modern attendance system that leverages face recognition technology on the web. This project was developed using Next.js (a React framework), TypeScript, face-api.js for face detection, and Supabase for backend and data storage.

I designed this system to be easily integrated into various digital attendance needs whether for offices, schools, or communities with a modern user experience and seamless verification process.

🔍 Background

Manual attendance especially with signatures or conventional fingerprint scanners can be slow and even lead to proxy attendance issues. I saw an opportunity for face recognition to solve these problems, providing a faster, more accurate, and contactless attendance experience.

⚙️ Tech Stack & Workflow

This project is powered by several key technologies:

  • Next.js: For building fast, scalable, SEO-friendly web apps.
  • TypeScript: For structured development and fewer bugs.
  • face-api.js: A lightweight JavaScript library for in-browser face detection and recognition.
  • Supabase: Backend as a Service supporting auth, database, and storage no need to build your own backend from scratch.

How It Works (Overview):

  1. User face registration
    Users simply face their device camera. The system captures their face and stores the face descriptor in Supabase.

  2. Attendance with face recognition
    For check-in, users only need to open the attendance page. The system automatically detects the face, matches it against saved data, and logs attendance if there’s a match.

  3. Data storage & querying
    Attendance records and user info are stored neatly in Supabase (PostgreSQL + Storage), making it easy to access for both internal dashboards and further analytics.

✨ Key Features

  • Live Face Recognition: Check-in with your face, no need for manual logins.
  • Real-time Feedback: The system instantly notifies whether the face is recognized.
  • Lightweight Dashboard: Admins can view attendance history and user data through a simple web dashboard.
  • Data Security: Raw face photos are never stored only encrypted face descriptors.
  • Cross-Device: Works from desktop or mobile browsers.

🚀 Why face-api.js & Supabase?

  • face-api.js
    This library is battle-tested, delivers solid performance for attendance use cases, and runs entirely client-side (in the browser), so there’s no need for a dedicated inference server and user privacy is enhanced.

  • Supabase
    Offers a complete stack for authentication, data, and storage, all ready-to-use. Integration is smooth, documentation is developer-friendly, and features like row-level security keep user data private.

🛠️ Quick Glimpse at the Implementation

The app is modular by design. Here’s an overview of the main architecture and flow:

  1. Frontend:
    Built with Next.js and face-api.js, users can scan their face directly in the browser.

  2. Auth & Data:
    User authentication and attendance data management are handled via Supabase Auth and tables.

  3. Face Recognition Integration:
    face-api.js performs live face detection and compares the captured descriptor with those in Supabase. If there’s a match, attendance is automatically recorded.

  4. Dashboard:
    Admins can view and download attendance records or manage users through a simple UI.

đź’ˇ Challenges & Solutions

  • Face Detection Accuracy:
    The main challenge is optimizing lighting and face positioning for accurate detection. The app provides real-time guidance and tips during the scan.

  • Data Privacy:
    Only face descriptors are stored (never raw photos), ensuring stronger privacy for users.

  • Cross-Platform Compatibility:
    In-browser face recognition can be tricky across devices. By using face-api.js, I was able to optimize performance and provide fallback options for unsupported devices.

🎯 Who Is This Project For?

  • Developers interested in face recognition integration in modern web apps.
  • Startups/Companies needing fast, efficient, contactless attendance solutions.
  • Communities/Organizations wanting a plug-and-play attendance system just a browser and webcam needed.

🧑‍💻 Example Code Snippets

1. Initializing face-api.js in a Next.js Component

import { useEffect } from 'react';
import * as faceapi from 'face-api.js';

export default function FaceScanner() {
  useEffect(() => {
    const loadModels = async () => {
      await faceapi.nets.tinyFaceDetector.loadFromUri('/models');
      await faceapi.nets.faceRecognitionNet.loadFromUri('/models');
      await faceapi.nets.faceLandmark68Net.loadFromUri('/models');
    };
    loadModels();
  }, []);

  return (
    <div>
      {/* ... Camera & UI elements here */}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

2. Capturing and Generating a Face Descriptor

async function handleCapture() {
  const video = document.getElementById('videoElement') as HTMLVideoElement;
  const detection = await faceapi
    .detectSingleFace(video, new faceapi.TinyFaceDetectorOptions())
    .withFaceLandmarks()
    .withFaceDescriptor();

  if (detection) {
    // The descriptor is saved to Supabase
    const { descriptor } = detection;
    await saveFaceDescriptor(descriptor);
  } else {
    alert('Face not detected, please try again.');
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Saving a Face Descriptor to Supabase

import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_KEY!
);

async function saveFaceDescriptor(descriptor: Float32Array) {
  const { data, error } = await supabase
    .from('users')
    .update({ face_descriptor: Array.from(descriptor) })
    .eq('id', userId);

  if (error) throw error;
  return data;
}
Enter fullscreen mode Exit fullscreen mode

4. Attendance Check: Face Matching Logic

function isMatch(inputDescriptor: Float32Array, storedDescriptor: number[]): boolean {
  // face-api.js has a helper for euclideanDistance
  const distance = faceapi.euclideanDistance(inputDescriptor, new Float32Array(storedDescriptor));
  // Threshold can be adjusted (e.g., 0.5)
  return distance < 0.5;
}
Enter fullscreen mode Exit fullscreen mode

5. Querying Attendance Data from Supabase

async function fetchAttendanceHistory(userId: string) {
  const { data, error } = await supabase
    .from('attendance')
    .select('*')
    .eq('user_id', userId)
    .order('created_at', { ascending: false });

  if (error) throw error;
  return data;
}
Enter fullscreen mode Exit fullscreen mode

đź”— Demo & Preview Video

Want to see it in action?
Check out my short preview video showcasing the app’s features and workflow:

Demo apps
Short Preview Video Showcasing

Preview Reports
Preview Reports

Thanks for reading!
If you have questions or feedback, feel free to drop a comment below.
Happy coding & stay creative! 🚀

Note:
Full source code, installation guide, and sample configs are available upon request via DM or in the comments.

Top comments (2)

Collapse
 
ferryops profile image
Ferry Ananda Febian

reserved()

Collapse
 
kishore_b9fa9b6057779d0db profile image
Kishore G

link