Skip to content
Back to featured work
Real-time full-stack

Online Interview Assessment System

A browser-based technical-interview platform: live video, a collaborative code editor that runs 13 languages, chat, scheduling, and QR-based candidate join - all in one room.

Online Interview Assessment System screenshot
Role
Solo full-stack
Scope
Video + editor + scheduling
Real-time
WebRTC · WebSockets · REST
Languages
13 executable

TL;DR

A Google-Meet-style platform purpose-built for technical interviews: interviewer and candidate join a live video room, write and run code together in real time across 13 languages, chat, share screens, and keep a searchable record of every interview. The hard parts all live in the real-time layer: peer-to-peer video over WebRTC, character-by-character code sync over WebSockets, and bridging two auth systems (Firebase + JWT) into one trusted identity.

01

The problem

Built during the COVID-19 pandemic, when technical interviews moved fully remote overnight. Interviewers were stitching together a video call, a shared coding pad, and an email thread to assess a single candidate - constant context-switching, and nothing tied the who, when, code, and outcome of an interview together.

Remote technical interviews meant juggling three tools - a video call, a shared coding pad, and a calendar thread - with nothing tying the who, when, code, and outcome together. The goal: collapse all of it into one web app, no installs.

The result

Built solo, end to end - one room that replaces a video call, a shared coding pad, and a scheduling thread. Three real-time transports (WebRTC, WebSockets, REST) coordinated so media stays peer-to-peer and the server is a matchmaker, not a bottleneck.

02

Role & ownership

Sole developer - full-stack, design, and infrastructure. Owned the React SPA, the Express + Socket.IO server, the WebRTC mesh, the MongoDB schema, the Firebase-to-JWT auth bridge, and deployment.

03

System at a glance

Three independent transports run at once. REST (Express) handles stateful CRUD - auth, interview records, schedules. A Socket.IO server, attached to the same Node HTTP process, carries low-latency room state: presence, chat, live code sync, and WebRTC signaling. WebRTC then carries the actual audio/video peer-to-peer, never through the server. MongoDB Atlas is the source of truth; Firebase handles the Google sign-in experience; an external compiler API runs submitted code.

Client - React SPALanding · Dashboard · Scheduler · Interview Room
RESTAuth, interview records, schedules (JWT auth-token).
Express API
WebSocketPresence, chat, live code sync, WebRTC signaling.
Socket.IO server
WebRTCAudio/video media, peer-to-peer mesh - bypasses the server.
Other participants

Single Node HTTP server

Express (REST)/api/auth · /notes · /schedule
Socket.IO (WebSocket)rooms · presence · signaling
MongoDB Atlassource of truth (Mongoose)
FirebaseGoogle auth + Firestore
Compiler APIruns submitted code
Three transports run at once: REST for stateful CRUD, WebSocket for live room state and signaling, and peer-to-peer WebRTC for media that never touches the server.
04

The real-time engine

The interview room ties three real-time mechanisms together at once - peer-to-peer video, character-by-character code sync, and live code execution - each on the transport that suits it.

4.1

Video - WebRTC mesh

Direct peer-to-peer audio/video between everyone in the room, via simple-peer.

  • On join, the server hands you the list of existing peers; you open an initiator connection to each
  • SDP offers/answers and ICE candidates are exchanged over the socket relay
  • New joiners are added incrementally; leavers trigger peer teardown

Engineering highlights

  • Media never touches the server: it's a matchmaker for the handshake only, so it never becomes a bandwidth bottleneck.
  • Right-sized topology: a full mesh is ideal for the 2-4 people in an interview, with no media server to run.
4.2

Live code sync - Socket.IO

Both editors stay in lockstep as either side types.

  • The CodeMirror editor emits CODE_CHANGE on every keystroke
  • The server broadcasts it to the rest of the room, which applies it
  • A late joiner triggers SYNC_CODE and is replayed the current buffer

Engineering highlights

  • Echo-loop guard: programmatic setValue is ignored, so an applied remote change doesn't re-broadcast and ping-pong forever.
  • No blank-editor race: replaying the buffer to late joiners means everyone converges on the same code regardless of join order.
4.3

Multi-language execution

Run the candidate's code in 13 languages, in front of both people.

  • C, C++, Java, Python, C#, Go, JS, Node, PHP, Perl, TypeScript, Kotlin, Ruby - each with a starter stub
  • Run ships code + stdin to an external compiler API
  • Output / error / status is synced back through the same socket channel

Engineering highlights

  • No untrusted code on my server: execution is offloaded, so there's no sandbox to maintain - at the cost of a third-party dependency.
  • Shared result: interviewer and candidate see the same run output at the same moment.

Live sync

CodeMirrorKeystroke in editor
emit CODE_CHANGE
Peers apply via setValueprogrammatic setValue is ignored, so it never re-emits

Run code

13 languagesClick Run
External compiler API
Result synced to bothinterviewer + candidate see the same output
Edits broadcast as keystroke diffs (with an echo-loop guard); Run is offloaded to a compiler API and the result is synced to both sides at once.
05

Under the hood

Full-stack, built solo: a React SPA, an Express + Socket.IO server sharing one process, and a MongoDB document model with per-record ownership.

Frontend

React 18 · Tailwind + MUI · Framer Motion · CodeMirror · simple-peer

  • Actions.js holds the socket event names both client and server import - one source of truth for the protocol.
  • Auth lives in React context; WebRTC peers + the socket sit in refs so they survive re-renders.
  • Tailwind for bespoke layout, MUI/Syncfusion for the heavy widgets (grids, scheduler).

Backend

Node · Express · Socket.IO · JWT · bcrypt · Nodemailer · Passport

  • One HTTP server hosts both REST and WebSocket - a single deploy target serving two protocols.
  • In-memory maps track socket to username and room membership for presence.
  • fetchuser middleware verifies the JWT and scopes every record to its owner.

Database

MongoDB Atlas · Mongoose · 5 collections

  • Soft-delete: records flip isDeleted into a recoverable bin before any hard delete.
  • db.js retries the connection up to 3x so a transient Atlas hiccup at boot won’t kill the server.
  • Queries always include authorId, so users can’t fetch each other’s records by ID.

REST API surface

  • /api/authregister · login · google · password reset · account deletion · schedule-interview
  • /api/notesCRUD interview records · status filter · soft-delete bin · case-insensitive search
  • /api/schedulecreate · list · delete planned interview slots
06

The auth bridge

Two auth worlds had to be reconciled. Firebase gives a great login experience (one-click Google) but its identity is only trusted on the client; the Express API has to trust requests on its own terms. The bridge keeps both - Firebase owns the login UX, a backend JWT is the actual key the server trusts.

FirebaseGoogle Sign-In
POST /api/auth/google
Find or create MongoDB user, mint JWTnew users get an auto-generated username, no password
JWT sent on every protected callserver verifies it and scopes records to the owner
Firebase owns the login experience; the backend JWT is the actual key the server trusts. MongoDB - not Firestore - is the source of truth.

There's also a full classic path for non-Google users, with production-grade lifecycle features rarely seen in a student project:

  • Email/password with bcrypt (salt rounds = 10) and express-validator input rules, issuing the same JWT.
  • Forgot/reset password via an emailed, single-use UUID token with same-day expiry (Nodemailer).
  • Account deletion as a two-step, email-confirmed flow that cascades and removes the user's interview records.
07

Technical challenges & decisions

  • 1

    Bridged Firebase auth to a backend JWT

    Firebase gave one-click Google sign-in but its identity is only trusted client-side. On login the client posts to /api/auth/google; the server upserts a MongoDB user and mints its own JWT as the real trust anchor. Decouples the backend from Firebase - the tradeoff is /google trusts the posted email (prod should verify the Firebase ID token).

  • 2

    WebRTC mesh over an SFU

    Each participant opens a direct peer connection to every other; Socket.IO relays only the SDP/ICE handshake. Media never touches the server. Perfect for 2-4 person interviews; a mesh would need an SFU to scale past that.

  • 3

    Keystroke-diff code sync with a late-joiner replay

    The editor broadcasts CODE_CHANGE over Socket.IO, guarding against echo loops by ignoring programmatic setValue. A late joiner fires SYNC_CODE and is replayed the current buffer so it never starts blank. Lightweight - not conflict-free (no CRDT/OT yet).

  • 4

    Offloaded code execution to an external compiler API

    Running untrusted code across 13 languages on my own server is a sandbox I did not want to maintain. The run request ships code + stdin to a compiler API and syncs the result back through the same socket, so both sides see the output simultaneously.

  • 5

    A single shared event-name contract (Actions.js)

    Socket event names (JOIN, CODE_CHANGE, SYNC_CODE, DISCONNECTED) live in one module imported by both client and server. Renaming an event can no longer silently desync the two halves of the realtime protocol.

  • 6

    Three transports in one Node process

    Express (REST) and Socket.IO (WebSocket) share a single HTTP server, while WebRTC media stays peer-to-peer. One deploy target, two protocols served, and the server never relays video - so it is a matchmaker, not a bottleneck.

  • 7

    Soft-delete by default

    Interview records flip an isDeleted flag into a recoverable bin rather than being destroyed; a separate action hard-deletes. Undo-friendly UX at the cost of a little query complexity.

  • 8

    Resilient DB connect + per-record ownership

    db.js retries the Mongo connection up to 3 times so a transient Atlas hiccup at boot does not kill the server. Every list/search query is scoped to authorId === req.user.id, so a user physically cannot read another user’s records by ID manipulation.

  • 9

    Production-grade account lifecycle

    Password reset and account deletion run on emailed, single-use UUID tokens with expiry (Nodemailer); deletion is a two-step, email-confirmed flow that cascades and removes the user’s interview records. Rare in a student project, and the right default.

08

What I'd do differently

This is an older solo codebase that works end to end. Being able to critique my own system is part of the story - here is the path from “student project that works” to a production service.

  • Secrets management: move all config to environment variables / a secrets manager and rotate anything that ever lived in the repo - the first thing I'd fix.
  • Verify Firebase ID tokens server-side with the Admin SDK instead of trusting the posted email on /api/auth/google.
  • JWT expiry + refresh, and move tokens out of sessionStorage toward httpOnly cookies.
  • Swap the mesh for an SFU (mediasoup / LiveKit) the moment group interviews need more than ~4 people.
  • Conflict-free code sync with a CRDT (Yjs) or OT so simultaneous typing can't clobber.
  • Centralize config (hardcoded localhost URLs become env-driven) and add the test suite it currently lacks.
  • TypeScript end to end - the realtime protocol is exactly the kind of contract that benefits from static types.
09

What this demonstrates

Real-time systems thinking

Coordinating WebRTC, WebSockets, and REST without them stepping on each other.

Full-stack ownership

From a marketing landing page to JWT middleware to schema design - built solo.

Integration over reinvention

Knowing when to lean on Firebase, a compiler API, or an SFU instead of building everything.

Product instinct

Soft-delete bins, QR join, scheduling, email flows - features that come from thinking about the user.

Engineering maturity

Shipping something that works and articulating exactly how I'd harden it for production.

10

Tech stack & tools

  • React
  • JavaScript
  • Tailwind CSS
  • Material UI
  • Framer Motion
  • WebRTC
  • Socket.io
  • CodeMirror
  • Node.js
  • Express
  • MongoDB
  • Mongoose
  • Firebase
  • JWT
  • Passport

Have a product that needs building?

I help founders ship full-stack products - from the first architecture decision to production. Currently available for freelance work.