About the Project — JobJam

Tagline: Connect. Practice. Grow — Career Prep, Humanized.

Inspiration

Most career-prep tools lean heavily on AI simulations and miss the human connection that makes practice feel real. I built JobJam to pair people with similar goals for anonymous, real-time video practice, so they can improve interviews, public speaking, and networking without needing existing connections or social capital.

What I Learned

  • Integrating real-time video in iOS with Agora and a secure token server.
  • Using semantic embeddings (Hugging Face intfloat/e5-large-v2) for meaning-based matching.
  • Architecting anonymous, privacy-first flows with Firebase Auth and Firestore.
  • Designing a responsive matchmaking pipeline with vector search and thresholding.

How I Built It

Stack

  • Frontend: SwiftUI (iOS)
  • Auth & DB: Firebase Authentication (Anonymous), Firestore
  • AI Matching: Hugging Face Inference API — intfloat/e5-large-v2
  • Video: Agora SDK (token server on Render)
  • Assistant: Gemini (JobBot) for in-call prompts and tips

Workflow

  1. Anonymous sign-in (Firebase Auth).
  2. User enters a short job description (d).
  3. The app requests an embedding (\mathbf{v}_d \in \mathbb{R}^n) from the model and stores it in Firestore along with the user’s queue state.
  4. The matchmaker computes similarity against queued users and selects the best candidate above a threshold (\theta).
  5. On a match, the client requests an Agora token from the token server and joins the video channel.
  6. During the call, users can mute, toggle video, leave, and ask JobBot for prompts—without leaving the session.

The Matching Math

To compare two users’ descriptions, JobJam uses cosine similarity:

[ \operatorname{sim}(\mathbf{v}_i,\mathbf{v}_j) = \frac{\mathbf{v}_i \cdot \mathbf{v}_j}{|\mathbf{v}_i|\;|\mathbf{v}_j|} ]

A match occurs when: [ \operatorname{sim}(\mathbf{v}_i,\mathbf{v}_j) \ge \theta, ] where (\theta) is tuned empirically to balance match quality and wait time.

Selection rule (argmax with constraint): [ j^* = \arg\max_{j \in \mathcal{Q}} \operatorname{sim}(\mathbf{v}i,\mathbf{v}_j) \quad \text{s.t.} \quad \operatorname{sim}(\mathbf{v}_i,\mathbf{v}{j^*}) \ge \theta, ] with (\mathcal{Q}) the set of queued users.

Notes on efficiency: For small queues, a linear scan (O(|\mathcal{Q}|)) is sufficient. As traffic grows, approximate nearest-neighbor (ANN) indexing (e.g., HNSW) can reduce latency while preserving match quality.

Challenges I Faced

  • Anonymous-first design: Preserving privacy while still enabling meaningful pairing.
  • Low-latency matching: Keeping the end-to-end path (embed → compare → connect) snappy on mobile networks.
  • Concurrency & state: Coordinating queue updates, race conditions, and call setup without mismatches or “double-pairs.”
  • Video + AI coexistence: Ensuring JobBot never blocks the UI or degrades call quality.

Impact & What’s Next

  • Impact: Faster, more human practice loops—users can jump in, match, and practice within seconds.
  • Next: Add local audio recording + transcription + speaking feedback (e.g., fluency, filler words) as a post-call report; personalize JobBot prompts; and introduce scalable vector search for larger queues.

Note: Assembly/transcription features are planned but not yet implemented in this version.

Built With

Share this project:

Updates