โ† Back to list

๐—›๐—ผ๐˜„ ๐—œ ๐—•๐˜‚๐—ถ๐—น๐˜ ๐—ฎ๐—ป ๐—”๐—œ-๐—ฃ๐—ผ๐˜„๐—ฒ๐—ฟ๐—ฒ๐—ฑ ๐—–๐—ฎ๐—บ๐—ฝ๐˜‚๐˜€ ๐— ๐—ฎ๐—ป๐—ฎ๐—ด๐—ฒ๐—บ๐—ฒ๐—ป๐˜ ๐—ฆ๐˜†๐˜€๐˜๐—ฒ๐—บโ€ฆ

A step-by-step breakdown of building TechPhantom AI Campus โ€” real-time facial attendance, full-stack architecture, and everything inโ€ฆ

Hussnain Bashir ยท 2026-06-06 09:05 ยท 5 claps ยท 8.9 min read
#ai #ai-projects #ai-project-ideas #hussnain-bashir
Open on Medium โ†—
Wiki topics: AI ยท AI ยท General ๐Ÿ›๏ธ ยท Architecture

๐—›๐—ผ๐˜„ ๐—œ ๐—•๐˜‚๐—ถ๐—น๐˜ ๐—ฎ๐—ป ๐—”๐—œ-๐—ฃ๐—ผ๐˜„๐—ฒ๐—ฟ๐—ฒ๐—ฑ ๐—–๐—ฎ๐—บ๐—ฝ๐˜‚๐˜€ ๐— ๐—ฎ๐—ป๐—ฎ๐—ด๐—ฒ๐—บ๐—ฒ๐—ป๐˜ ๐—ฆ๐˜†๐˜€๐˜๐—ฒ๐—บ ๐—ณ๐—ฟ๐—ผ๐—บ ๐—ฆ๐—ฐ๐—ฟ๐—ฎ๐˜๐—ฐ๐—ต

A step-by-step breakdown of building TechPhantom AI Campus โ€” real-time facial attendance, full-stack architecture, and everything in between.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

me

me

๐—ง๐—ต๐—ฒ ๐—ฃ๐—ฟ๐—ผ๐—ฏ๐—น๐—ฒ๐—บ ๐—ช๐—ผ๐—ฟ๐˜๐—ต ๐—ฆ๐—ผ๐—น๐˜ƒ๐—ถ๐—ป๐—ด

Every university Iโ€™ve seen handles attendance the same way: a teacher calls names, students raise hands, and someone scribbles on a sheet. In a class of 60 students, this wastes 5โ€“10 minutes every single session. Multiply that across hundreds of courses and an entire semester โ€” youโ€™re looking at thousands of hours lost, unreliable records, and rampant proxy attendance.

I decided to build something that eliminates this entirely. Not just a digital register โ€” a fully AI-powered system where a camera does the job automatically, with zero manual input from the teacher or the student.

This is the story of exactly how I built it.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—ฆ๐˜๐—ฒ๐—ฝ ๐Ÿญ โ€” ๐——๐—ฒ๐—ณ๐—ถ๐—ป๐—ฒ ๐˜๐—ต๐—ฒ ๐—ฆ๐˜†๐˜€๐˜๐—ฒ๐—บ ๐—•๐—ฒ๐—ณ๐—ผ๐—ฟ๐—ฒ ๐—ช๐—ฟ๐—ถ๐˜๐—ถ๐—ป๐—ด ๐—ฎ ๐—ฆ๐—ถ๐—ป๐—ด๐—น๐—ฒ ๐—Ÿ๐—ถ๐—ป๐—ฒ ๐—ผ๐—ณ ๐—–๐—ผ๐—ฑ๐—ฒ

The most common mistake developers make is jumping straight into code. I spent the first few days just thinking through what the system actually needed to do.

๐—ง๐—ต๐—ฟ๐—ฒ๐—ฒ ๐˜๐˜†๐—ฝ๐—ฒ๐˜€ ๐—ผ๐—ณ ๐˜‚๐˜€๐—ฒ๐—ฟ๐˜€: โ€ข ๐—”๐—ฑ๐—บ๐—ถ๐—ป โ€” manages students, courses, sessions, monitors attendance โ€ข ๐—ฆ๐˜๐˜‚๐—ฑ๐—ฒ๐—ป๐˜ โ€” views their own attendance, enrolls in courses, registers their face โ€ข ๐—ž๐—ถ๐—ผ๐˜€๐—ธ (๐—Ÿ๐—ถ๐˜ƒ๐—ฒ ๐—ฆ๐—ฐ๐—ฎ๐—ป๐—ป๐—ฒ๐—ฟ) โ€” a camera-based terminal that recognizes faces and marks attendance in real time

๐—–๐—ผ๐—ฟ๐—ฒ ๐—ณ๐—น๐—ผ๐˜„๐˜€ ๐—œ ๐—ต๐—ฎ๐—ฑ ๐˜๐—ผ ๐˜€๐˜‚๐—ฝ๐—ฝ๐—ผ๐—ฟ๐˜:

  1. Admin creates a course โ†’ enrolls students โ†’ starts an attendance session
  2. Student registers their face via webcam โ†’ system encodes and stores it
  3. Live camera feed streams to backend โ†’ backend matches faces โ†’ records attendance
  4. Both admin and student see live updates instantly

๐—ง๐—ต๐—ฒ ๐—ป๐—ผ๐—ป-๐—ป๐—ฒ๐—ด๐—ผ๐˜๐—ถ๐—ฎ๐—ฏ๐—น๐—ฒ๐˜€: โ€ข Real-time โ€” no polling, must use WebSockets โ€ข Privacy-safe โ€” store face encodings, never raw images โ€ข Role-based access โ€” admins cannot do student things and vice versa โ€ข Must work even if heavy AI libraries arenโ€™t installed (fallback mode)

Only after this clarity did I start setting up the project.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—ฆ๐˜๐—ฒ๐—ฝ ๐Ÿฎ โ€” ๐—ฃ๐—ฟ๐—ผ๐—ท๐—ฒ๐—ฐ๐˜ ๐—ฆ๐˜๐—ฟ๐˜‚๐—ฐ๐˜๐˜‚๐—ฟ๐—ฒ & ๐—˜๐—ป๐˜ƒ๐—ถ๐—ฟ๐—ผ๐—ป๐—บ๐—ฒ๐—ป๐˜ ๐—ฆ๐—ฒ๐˜๐˜‚๐—ฝ

I chose a clean separation: one ๐—ฏ๐—ฎ๐—ฐ๐—ธ๐—ฒ๐—ป๐—ฑ/ folder for Python/FastAPI, and one ๐—ณ๐—ฟ๐—ผ๐—ป๐˜๐—ฒ๐—ป๐—ฑ/ folder for React/Vite. Keeping them completely independent means either side can be swapped, redeployed, or scaled without touching the other.

The backend used a Python virtual environment to isolate dependencies โ€” ๐—™๐—ฎ๐˜€๐˜๐—”๐—ฃ๐—œ, ๐—ฆ๐—ค๐—Ÿ๐—”๐—น๐—ฐ๐—ต๐—ฒ๐—บ๐˜†, ๐—ฃ๐˜†๐—ฑ๐—ฎ๐—ป๐˜๐—ถ๐—ฐ, JWT libraries, ๐—ข๐—ฝ๐—ฒ๐—ป๐—–๐—ฉ, and face_recognition. The frontend used npm with ๐—ฅ๐—ฒ๐—ฎ๐—ฐ๐˜, ๐—”๐˜…๐—ถ๐—ผ๐˜€, Lucide Icons, ๐—ฅ๐—ฒ๐—ฐ๐—ต๐—ฎ๐—ฟ๐˜๐˜€, and react-webcam.

Having a clear folder structure from day one prevented the chaos of a disorganized codebase as the project grew.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—ฆ๐˜๐—ฒ๐—ฝ ๐Ÿฏ โ€” ๐——๐—ฒ๐˜€๐—ถ๐—ด๐—ป ๐˜๐—ต๐—ฒ ๐——๐—ฎ๐˜๐—ฎ๐—ฏ๐—ฎ๐˜€๐—ฒ ๐— ๐—ผ๐—ฑ๐—ฒ๐—น๐˜€

Before writing any API, I designed the full database schema. This is the foundation everything else sits on โ€” get it wrong and youโ€™ll be rewriting half the app later.

I needed 5 entities:

๐—จ๐˜€๐—ฒ๐—ฟ โ†’ Authentication for both admins and students ๐—ฆ๐˜๐˜‚๐—ฑ๐—ฒ๐—ป๐˜ โ†’ Extended profile linked to a User ๐—–๐—ผ๐˜‚๐—ฟ๐˜€๐—ฒ โ†’ A university course with a unique code ๐—”๐˜๐˜๐—ฒ๐—ป๐—ฑ๐—ฎ๐—ป๐—ฐ๐—ฒ๐—ฆ๐—ฒ๐˜€๐˜€๐—ถ๐—ผ๐—ป โ†’ A time-bounded window opened by an admin ๐—”๐˜๐˜๐—ฒ๐—ป๐—ฑ๐—ฎ๐—ป๐—ฐ๐—ฒ๐—ฅ๐—ฒ๐—ฐ๐—ผ๐—ฟ๐—ฑ โ†’ One attendance entry per student per session

The trickiest relationship was ๐—ฆ๐˜๐˜‚๐—ฑ๐—ฒ๐—ป๐˜ โ†” ๐—–๐—ผ๐˜‚๐—ฟ๐˜€๐—ฒ โ€” a student can be in many courses, and a course has many students. This is a classic many-to-many relationship, handled with a separate enrollment association table that also stores the date a student enrolled.

The most important design decision: the Student model stores the face encoding as raw binary โ€” a 128-float numpy array serialized to bytes. ๐—ก๐—ผ ๐—ฟ๐—ฎ๐˜„ ๐—ณ๐—ฎ๐—ฐ๐—ฒ ๐—ถ๐—บ๐—ฎ๐—ด๐—ฒ๐˜€ ๐—ฎ๐—ฟ๐—ฒ ๐—ฒ๐˜ƒ๐—ฒ๐—ฟ ๐˜€๐—ฎ๐˜ƒ๐—ฒ๐—ฑ ๐—ฎ๐—ป๐˜†๐˜„๐—ต๐—ฒ๐—ฟ๐—ฒ. This is a deliberate privacy-first choice.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—ฆ๐˜๐—ฒ๐—ฝ ๐Ÿฐ โ€” ๐—•๐˜‚๐—ถ๐—น๐—ฑ ๐˜๐—ต๐—ฒ ๐—”๐˜‚๐˜๐—ต๐—ฒ๐—ป๐˜๐—ถ๐—ฐ๐—ฎ๐˜๐—ถ๐—ผ๐—ป ๐—ฆ๐˜†๐˜€๐˜๐—ฒ๐—บ (๐—๐—ช๐—ง + ๐—ฅ๐—ผ๐—น๐—ฒ-๐—•๐—ฎ๐˜€๐—ฒ๐—ฑ ๐—”๐—ฐ๐—ฐ๐—ฒ๐˜€๐˜€)

Security is not optional. I built the auth layer before any feature routes โ€” because every route depends on knowing who is calling it and what role they have.

The system has two roles: ๐—ฎ๐—ฑ๐—บ๐—ถ๐—ป and ๐˜€๐˜๐˜‚๐—ฑ๐—ฒ๐—ป๐˜, stored directly on the User record.

๐—ฃ๐—ฎ๐˜€๐˜€๐˜„๐—ผ๐—ฟ๐—ฑ ๐—ต๐—ฎ๐˜€๐—ต๐—ถ๐—ป๐—ด uses ๐—•๐—ฐ๐—ฟ๐˜†๐—ฝ๐˜ via the passlib library. Passwords are never stored in plain text โ€” only the hash is saved to the database.

๐—๐—ช๐—ง ๐˜๐—ผ๐—ธ๐—ฒ๐—ป๐˜€ are issued on login. Each token carries the userโ€™s ID and role, and expires after 24 hours. Every subsequent API request must include this token in the Authorization header.

๐—ฅ๐—ผ๐˜‚๐˜๐—ฒ ๐—ฝ๐—ฟ๐—ผ๐˜๐—ฒ๐—ฐ๐˜๐—ถ๐—ผ๐—ป is handled by FastAPI dependency injection. I created two reusable guard functions โ€” one that enforces admin access, one that enforces student access. Any route just declares which guard it needs and FastAPI handles the rest automatically. This made it impossible to accidentally expose an admin endpoint to a regular student.

The system also auto-creates a default admin account on first startup โ€” no manual database seeding required.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—ฆ๐˜๐—ฒ๐—ฝ ๐Ÿฑ โ€” ๐—•๐˜‚๐—ถ๐—น๐—ฑ ๐˜๐—ต๐—ฒ ๐—™๐—ฎ๐—ฐ๐—ฒ ๐—˜๐—ป๐—ฐ๐—ผ๐—ฑ๐—ถ๐—ป๐—ด ๐—ฃ๐—ถ๐—ฝ๐—ฒ๐—น๐—ถ๐—ป๐—ฒ

This was the most technically complex piece. The goal: take an image, detect the face, and produce a 128-number vector โ€” called an ๐—ฒ๐—ป๐—ฐ๐—ผ๐—ฑ๐—ถ๐—ป๐—ด โ€” that uniquely represents that personโ€™s face.

๐—ฃ๐—ฟ๐—ถ๐—บ๐—ฎ๐—ฟ๐˜† ๐—บ๐—ฒ๐˜๐—ต๐—ผ๐—ฑ โ€” ๐—ฑ๐—น๐—ถ๐—ฏ ๐˜ƒ๐—ถ๐—ฎ ๐—ณ๐—ฎ๐—ฐ๐—ฒ_๐—ฟ๐—ฒ๐—ฐ๐—ผ๐—ด๐—ป๐—ถ๐˜๐—ถ๐—ผ๐—ป: The face_recognition library (built on dlib) detects face locations in an image and computes a 128-dimension floating-point vector for each face found. This encoding is the mathematical โ€œfingerprintโ€ of a face. Two photos of the same person produce encodings with a very small Euclidean distance. Two different people produce a large distance. A threshold of 0.5 is used โ€” faces within that distance are considered a match.

๐—ง๐—ต๐—ฒ ๐—ฐ๐—ต๐—ฎ๐—น๐—น๐—ฒ๐—ป๐—ด๐—ฒ: dlib requires C++ build tools to compile from source. Many environments donโ€™t have them. Rather than making the whole system fail, I built a graceful fallback.

๐—™๐—ฎ๐—น๐—น๐—ฏ๐—ฎ๐—ฐ๐—ธ ๐—บ๐—ฒ๐˜๐—ต๐—ผ๐—ฑ โ€” ๐—ข๐—ฝ๐—ฒ๐—ป๐—–๐—ฉ ๐—›๐—ฎ๐—ฎ๐—ฟ ๐—–๐—ฎ๐˜€๐—ฐ๐—ฎ๐—ฑ๐—ฒ + ๐—ฐ๐—ผ๐—น๐—ผ๐—ฟ ๐—ต๐—ถ๐˜€๐˜๐—ผ๐—ด๐—ฟ๐—ฎ๐—บ ๐—ฒ๐—ป๐—ฐ๐—ผ๐—ฑ๐—ถ๐—ป๐—ด: If face_recognition isnโ€™t available, OpenCVโ€™s Haar Cascade classifier detects the face region. That region is resized to a fixed size and analyzed channel-by-channel to build a 128-bin color histogram โ€” a different kind of numerical fingerprint. Itโ€™s less accurate than dlib embeddings but keeps the system functional without any C++ dependencies.

Both methods produce a 128-dimension array, so storage, comparison, and matching work identically regardless of which method is used. The system detects which method is available at startup and uses it consistently throughout.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—ฆ๐˜๐—ฒ๐—ฝ ๐Ÿฒ โ€” ๐—œ๐—ป๐˜๐—ฒ๐—ด๐—ฟ๐—ฎ๐˜๐—ฒ ๐—ฌ๐—ข๐—Ÿ๐—ข๐˜ƒ๐Ÿด ๐—ณ๐—ผ๐—ฟ ๐—ฃ๐—ฒ๐—ฟ๐˜€๐—ผ๐—ป ๐——๐—ฒ๐˜๐—ฒ๐—ฐ๐˜๐—ถ๐—ผ๐—ป

Face recognition alone has a problem: someone could hold up a printed photo and trick the system. ๐—ฌ๐—ข๐—Ÿ๐—ข๐˜ƒ๐Ÿด adds a real-world validation layer โ€” it confirms that an actual person is physically present in the camera frame before any face recognition is attempted.

YOLOv8 (the nano variant, optimized for speed) runs on every incoming camera frame and looks specifically for the โ€œpersonโ€ class. If no person is detected, face recognition is skipped entirely โ€” saving compute and preventing the most obvious spoofing attack.

Like face_recognition, YOLOv8 is wrapped in a conditional import so the system starts cleanly and continues in degraded mode if the model isnโ€™t available.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—ฆ๐˜๐—ฒ๐—ฝ ๐Ÿณ โ€” ๐—•๐˜‚๐—ถ๐—น๐—ฑ ๐˜๐—ต๐—ฒ ๐—ฅ๐—ฒ๐—ฎ๐—น-๐—ง๐—ถ๐—บ๐—ฒ ๐—ช๐—ฒ๐—ฏ๐—ฆ๐—ผ๐—ฐ๐—ธ๐—ฒ๐˜ ๐—ž๐—ถ๐—ผ๐˜€๐—ธ

This was the most exciting engineering challenge. The live attendance kiosk needs to receive a continuous stream of camera frames from the browser, run the full AI pipeline on each one, and send the result back instantly โ€” all without the overhead of repeated HTTP requests.

๐—ช๐—ฒ๐—ฏ๐—ฆ๐—ผ๐—ฐ๐—ธ๐—ฒ๐˜๐˜€ are the only sensible solution. They keep a persistent, two-way connection open between the browser and the server.

Hereโ€™s the flow that happens dozens of times per second:

  1. Browser captures a webcam frame using react-webcam
  2. Frame is encoded as a base64 JPEG string and sent over the WebSocket
  3. Backend decodes the image and runs ๐—ฌ๐—ข๐—Ÿ๐—ข๐˜ƒ๐Ÿด person detection
  4. If a person is confirmed, runs face encoding on the frame
  5. Loads all enrolled studentsโ€™ stored encodings from the database
  6. Compares using Euclidean distance to find the closest match
  7. If a match is found within the tolerance threshold, records attendance and sends back the studentโ€™s name, confidence score, and face bounding box coordinates
  8. Browser draws the bounding box overlay on a canvas and displays the name

The frontend throttles frame sending to approximately 10 frames per second to avoid overwhelming the backend while still feeling real-time to the user.

๐— ๐˜‚๐—น๐˜๐—ถ-๐—ฐ๐—ผ๐˜‚๐—ฟ๐˜€๐—ฒ ๐—ฐ๐—ผ๐—ป๐—ณ๐—น๐—ถ๐—ฐ๐˜ ๐—ฟ๐—ฒ๐˜€๐—ผ๐—น๐˜‚๐˜๐—ถ๐—ผ๐—ป was also added: if a student is enrolled in two courses that both have active sessions at the same time, the system handles the selection intelligently rather than recording double attendance.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—ฆ๐˜๐—ฒ๐—ฝ ๐Ÿด โ€” ๐—•๐˜‚๐—ถ๐—น๐—ฑ ๐˜๐—ต๐—ฒ ๐—™๐—ฎ๐˜€๐˜๐—”๐—ฃ๐—œ ๐—ฅ๐—˜๐—ฆ๐—ง ๐—˜๐—ป๐—ฑ๐—ฝ๐—ผ๐—ถ๐—ป๐˜๐˜€

With the AI pipeline working, I built all the standard management endpoints that power the dashboards.

๐—˜๐—ป๐—ฑ๐—ฝ๐—ผ๐—ถ๐—ป๐˜ ๐—ด๐—ฟ๐—ผ๐˜‚๐—ฝ๐˜€: โ€ข ๐—”๐˜‚๐˜๐—ต ๐—ฟ๐—ผ๐˜‚๐˜๐—ฒ๐˜€ โ€” register, login, get current user, update profile, upload/delete profile picture โ€ข ๐—”๐—ฑ๐—บ๐—ถ๐—ป ๐—ฟ๐—ผ๐˜‚๐˜๐—ฒ๐˜€ โ€” full CRUD for students, courses, and attendance sessions; attendance reports with filtering by course or session โ€ข ๐—ฆ๐˜๐˜‚๐—ฑ๐—ฒ๐—ป๐˜ ๐—ฟ๐—ผ๐˜‚๐˜๐—ฒ๐˜€ โ€” view own profile, enroll/unenroll from courses, view attendance history, register own face via webcam

FastAPIโ€™s Pydantic integration made validation clean โ€” each request body has a strict schema, and invalid requests are automatically rejected with clear error messages before they ever reach the database.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—ฆ๐˜๐—ฒ๐—ฝ ๐Ÿต โ€” ๐—•๐˜‚๐—ถ๐—น๐—ฑ ๐˜๐—ต๐—ฒ ๐—ฅ๐—ฒ๐—ฎ๐—ฐ๐˜ ๐—™๐—ฟ๐—ผ๐—ป๐˜๐—ฒ๐—ป๐—ฑ

The frontend is organized around three main views and a shared authentication context.

๐—”๐˜‚๐˜๐—ต๐—ฒ๐—ป๐˜๐—ถ๐—ฐ๐—ฎ๐˜๐—ถ๐—ผ๐—ป is managed via React Context โ€” the logged-in userโ€™s data and JWT token are stored in context and localStorage, available to any component without prop drilling.

๐—”๐—ฃ๐—œ ๐—ฐ๐—ฎ๐—น๐—น๐˜€ all go through a centralized Axios instance that automatically attaches the JWT token to every request โ€” no component ever manually handles authorization headers.

๐—”๐—ฑ๐—บ๐—ถ๐—ป ๐——๐—ฎ๐˜€๐—ต๐—ฏ๐—ผ๐—ฎ๐—ฟ๐—ฑ โ€” the largest component, organized into tabs: โ€ข Overview with live statistics โ€” total students, courses, active sessions, face enrollment rate โ€ข Student management โ€” add, edit, delete, course assignments, face registration via webcam โ€ข Course management โ€” create, update, delete โ€ข Session management โ€” start and stop attendance sessions per course โ€ข Attendance reports with course and session filtering

๐—ฆ๐˜๐˜‚๐—ฑ๐—ฒ๐—ป๐˜ ๐——๐—ฎ๐˜€๐—ต๐—ฏ๐—ผ๐—ฎ๐—ฟ๐—ฑ โ€” the student-facing view: โ€ข Personal attendance rate per course shown as bar charts via Recharts โ€ข List of enrolled courses with per-course percentages โ€ข Available courses for self-enrollment โ€ข Webcam-based face registration โ€” student takes a photo that gets sent to the backend for encoding

๐—Ÿ๐—ถ๐˜ƒ๐—ฒ ๐—ž๐—ถ๐—ผ๐˜€๐—ธ โ€” connects to the WebSocket endpoint, streams webcam frames, receives recognition results, and draws bounding box overlays on a canvas. Shows โ€œRecognized โœ“โ€ with the student name, or โ€œUnknown Faceโ€ for unregistered individuals.

๐——๐—ฒ๐˜€๐—ถ๐—ด๐—ป ๐˜€๐˜†๐˜€๐˜๐—ฒ๐—บ โ€” a full glassmorphism CSS system with a dark theme, gradient accents, smooth hover transitions, and consistent spacing โ€” all in vanilla CSS, no Tailwind dependency.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—ฆ๐˜๐—ฒ๐—ฝ ๐Ÿญ๐Ÿฌ โ€” ๐——๐—ฒ๐—ฝ๐—น๐—ผ๐˜†๐—บ๐—ฒ๐—ป๐˜ ๐—”๐—ฟ๐—ฐ๐—ต๐—ถ๐˜๐—ฒ๐—ฐ๐˜๐˜‚๐—ฟ๐—ฒ

Local development runs two processes in parallel โ€” the backend on port 8000 with hot reload, and the Vite dev server on port 5173 with HMR.

Production deployment uses a proper layered stack:

๐—š๐˜‚๐—ป๐—ถ๐—ฐ๐—ผ๐—ฟ๐—ป + ๐—จ๐˜ƒ๐—ถ๐—ฐ๐—ผ๐—ฟ๐—ป ๐˜„๐—ผ๐—ฟ๐—ธ๐—ฒ๐—ฟ๐˜€ โ†’ Multi-process ASGI serving ๐—ก๐—ด๐—ถ๐—ป๐˜… โ†’ SSL termination, WebSocket proxy, static file serving ๐——๐—ผ๐—ฐ๐—ธ๐—ฒ๐—ฟ โ†’ Reproducible Python + AI environment ๐—ฉ๐—ฒ๐—ฟ๐—ฐ๐—ฒ๐—น โ†’ CDN-backed React frontend deployment ๐—ฃ๐—ผ๐˜€๐˜๐—ด๐—ฟ๐—ฒ๐—ฆ๐—ค๐—Ÿ (prod) / ๐—ฆ๐—ค๐—Ÿ๐—ถ๐˜๐—ฒ (dev) โ†’ Persistent data storage

The most critical Nginx detail: ๐—ช๐—ฒ๐—ฏ๐—ฆ๐—ผ๐—ฐ๐—ธ๐—ฒ๐˜ connections require special proxy headers โ€” Upgrade and Connection. Without them, the live kiosk silently fails behind Nginx with no obvious error.

๐——๐—ผ๐—ฐ๐—ธ๐—ฒ๐—ฟ is especially important because dlib and YOLOv8 have complex native dependencies โ€” C++ libraries and model weight files. Containerizing the backend means the exact same environment runs in development, staging, and production, eliminating environment-specific bugs entirely.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—–๐—ต๐—ฎ๐—น๐—น๐—ฒ๐—ป๐—ด๐—ฒ๐˜€ ๐—œ ๐—›๐—ถ๐˜ ๐—ฎ๐—ป๐—ฑ ๐—›๐—ผ๐˜„ ๐—œ ๐—ฆ๐—ผ๐—น๐˜ƒ๐—ฒ๐—ฑ ๐—ง๐—ต๐—ฒ๐—บ

โŒ ๐—ฑ๐—น๐—ถ๐—ฏ ๐˜„๐—ผ๐—ปโ€™๐˜ ๐—ถ๐—ป๐˜€๐˜๐—ฎ๐—น๐—น ๐˜„๐—ถ๐˜๐—ต๐—ผ๐˜‚๐˜ ๐—–++ ๐—ฏ๐˜‚๐—ถ๐—น๐—ฑ ๐˜๐—ผ๐—ผ๐—น๐˜€ โ†’ Built the OpenCV histogram fallback that produces a compatible 128-dimension encoding. System gracefully degrades without breaking.

โŒ ๐—ช๐—ฒ๐—ฏ๐—ฆ๐—ผ๐—ฐ๐—ธ๐—ฒ๐˜ ๐—ณ๐—ฟ๐—ฎ๐—บ๐—ฒ๐˜€ ๐—ฎ๐—ฟ๐—ฟ๐—ถ๐˜ƒ๐—ถ๐—ป๐—ด ๐—ณ๐—ฎ๐˜€๐˜๐—ฒ๐—ฟ ๐˜๐—ต๐—ฎ๐—ป ๐—ฝ๐—ฟ๐—ผ๐—ฐ๐—ฒ๐˜€๐˜€๐—ถ๐—ป๐—ด ๐˜€๐—ฝ๐—ฒ๐—ฒ๐—ฑ โ†’ Added frame throttling on the frontend โ€” send a frame every 100ms, not on every render cycle. The backend processes one frame per connection at a time.

โŒ ๐— ๐˜‚๐—น๐˜๐—ถ๐—ฝ๐—น๐—ฒ ๐—ฎ๐—ฐ๐˜๐—ถ๐˜ƒ๐—ฒ ๐˜€๐—ฒ๐˜€๐˜€๐—ถ๐—ผ๐—ป๐˜€ ๐—ฐ๐—ฎ๐˜‚๐˜€๐—ถ๐—ป๐—ด ๐—ฐ๐—ผ๐—ป๐—ณ๐˜‚๐˜€๐—ถ๐—ผ๐—ป โ†’ Added multi-course conflict detection. If a student is in two courses with active sessions simultaneously, the system handles the selection intelligently.

โŒ ๐—™๐—ฎ๐—ฐ๐—ฒ ๐—ฒ๐—ป๐—ฐ๐—ผ๐—ฑ๐—ถ๐—ป๐—ด๐˜€ ๐—ป๐—ผ๐˜ ๐—ฝ๐—ผ๐—ฟ๐˜๐—ฎ๐—ฏ๐—น๐—ฒ ๐—ฏ๐—ฒ๐˜๐˜„๐—ฒ๐—ฒ๐—ป ๐—ฑ๐—น๐—ถ๐—ฏ ๐—ฎ๐—ป๐—ฑ ๐—ข๐—ฝ๐—ฒ๐—ป๐—–๐—ฉ โ†’ The encoding method is detected at runtime and used consistently. Mixing methods on a single deployment is avoided by design.

โŒ ๐—๐—ช๐—ง ๐˜€๐˜‚๐—ฏ ๐—ณ๐—ถ๐—ฒ๐—น๐—ฑ ๐—บ๐˜‚๐˜€๐˜ ๐—ฏ๐—ฒ ๐—ฎ ๐˜€๐˜๐—ฟ๐—ถ๐—ป๐—ด ๐—ฝ๐—ฒ๐—ฟ ๐˜€๐—ฝ๐—ฒ๐—ฐ๐—ถ๐—ณ๐—ถ๐—ฐ๐—ฎ๐˜๐—ถ๐—ผ๐—ป โ†’ User ID is cast to a string when creating the token and back to an integer when verifying. A subtle bug that caused 401 errors until tracked down.

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—ช๐—ต๐—ฎ๐˜ ๐—œโ€™๐—ฑ ๐——๐—ผ ๐——๐—ถ๐—ณ๐—ณ๐—ฒ๐—ฟ๐—ฒ๐—ป๐˜๐—น๐˜†

  1. ๐—ฆ๐—ฒ๐—ฝ๐—ฎ๐—ฟ๐—ฎ๐˜๐—ฒ ๐˜๐—ต๐—ฒ ๐—”๐—œ ๐˜„๐—ผ๐—ฟ๐—ธ๐—ฒ๐—ฟ โ€” run face recognition in a dedicated async process to avoid blocking the event loop under heavy load
  2. ๐—”๐—ฑ๐—ฑ ๐—ฅ๐—ฒ๐—ฑ๐—ถ๐˜€ โ€” for session state caching instead of hitting the database on every WebSocket frame
  3. ๐—”๐—ฑ๐—ฑ ๐—น๐—ถ๐˜ƒ๐—ฒ๐—ป๐—ฒ๐˜€๐˜€ ๐—ฑ๐—ฒ๐˜๐—ฒ๐—ฐ๐˜๐—ถ๐—ผ๐—ป โ€” blink detection or head movement to prevent photo spoofing more robustly
  4. ๐— ๐˜‚๐—น๐˜๐—ถ-๐—ฐ๐—ฎ๐—บ๐—ฒ๐—ฟ๐—ฎ ๐˜€๐˜‚๐—ฝ๐—ฝ๐—ผ๐—ฟ๐˜ โ€” one WebSocket connection per camera, all feeding into a shared session

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

๐—–๐—น๐—ผ๐˜€๐—ถ๐—ป๐—ด ๐—ง๐—ต๐—ผ๐˜‚๐—ด๐—ต๐˜๐˜€

This project touched every layer of modern software: database design, secure authentication, computer vision, real-time communication, UI development, and production deployment. Each layer had its own challenges, and solving them end-to-end gave me a much deeper appreciation for how complex production systems actually work.

If youโ€™re a developer looking to go beyond CRUD apps, Iโ€™d strongly recommend building something that involves real-time data and AI โ€” it forces you to think about performance, reliability, and user experience in ways that simple request/response systems never do.

Feel free to reach out if you have questions about any specific part of the architecture!

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

Python #FastAPI #ReactJS #ComputerVision #YOLOv8 #FaceRecognition #WebSocket #FullStack #EdTech #SoftwareEngineering


๋ฉ”ํƒ€๋ฐ์ดํ„ฐ
post_id
cfb94b5b1ad7
slug
-cfb94b5b1ad7
url
https://medium.com/@hussnain-bashir/-cfb94b5b1ad7
canonical_url
https://medium.com/@hussnain-bashir/-cfb94b5b1ad7
author_url
https://medium.com/@hussnain-bashir
status
ok
fetched_at
2026-06-26 03:39:16