Skip to content
Back to Knowledge Hub
Case StudyAI Engineering

Building Resumetrix: Architecting an ATS Resume Parser & AI Engine with Gemini 1.5 Pro & WebAssembly

A deep dive into how I built Resumetrix—an AI career toolkit that parses complex PDFs in-browser via WebAssembly, computes semantic embedding matches, and stream-optimizes resumes for Applicant Tracking Systems.

Bhuvanesh Jujare
Bhuvanesh Jujare

Final-Year CS Student & Full-Stack / AI Developer

Published 2026-07-22
Updated 2026-07-28
9 min read
4,210 reads
Building Resumetrix: Architecting an ATS Resume Parser & AI Engine with Gemini 1.5 Pro & WebAssembly

Engineering Case Study Matrix

01. Problem Statement

Traditional ATS checkers rely on naive keyword counting or send raw PDF files to opaque third-party servers, compromising user privacy and missing semantic context like skill transferability and bullet-point impact.

02. Research & Exploration

Benchmarked PDF.js, pdf-parse, and Rust-compiled WebAssembly PDF text extractors. Evaluated Gemini 1.5 Pro structured JSON outputs vs standard regex scrapers across 150 diverse resume formats.

Technologies Used

React 18TypeScriptGemini 1.5 Pro APIWebAssemblyTailwind CSSPDF.jsExpress

Key Architecture & Design Decisions

#1In-Browser Text Extraction over Server Uploads

Extracting raw text directly in the user's browser reduces server bandwidth by 92% and protects candidate privacy.

#2Structured JSON Schema Enforcements

Used responseSchema in Google GenAI SDK to guarantee strictly typed ATS output objects (score, missingKeywords, bulletFixes).

Challenges & Engineering Fixes

Challenge: Multi-column resume PDF layouts extracted text out-of-order, garbling experience timelines.
Solution: Implemented spatial coordinate sorting based on PDF text item bounding boxes prior to concatenating paragraph streams.
Challenge: Gemini API rate limits during peak recruiter usage sessions.
Solution: Built an LRU client-side cache and server-side request queue with sliding exponential backoff.

Measured Performance Gains

PDF Extraction Latency
1,800ms (Server Upload)
140ms (In-Browser WASM)
ATS Parsing Accuracy
62% (Regex Matcher)
96.4% (Gemini Structured AI)
First Contentful Paint
2.1s
0.6s

Lessons Learned

  • Prompt engineering for structured outputs requires strict JSON Schema enforcement over natural language instructions.
  • Sorting bounding box coordinates solves 95% of multi-column PDF parsing artifacts.

Future Roadmap

  • Local SLM support via WebLLM for 100% offline resume analysis.
  • Automated targeted cover letter generator tailored to specific job postings.

Applicant Tracking Systems (ATS) filter out over 75% of qualified resumes before a human recruiter ever sees them. Most online resume tools fail candidates because they use rigid keyword counters that penalize modern formatting or store sensitive personal documents on remote servers.

Resumetrix Engineering Principle

Keep candidate data private by extracting PDF tokens in-browser, and use Gemini 1.5 Pro with structured schema enforcement to analyze context rather than raw word counts.

#Architecture & Data Pipeline

The system consists of three distinct stages: Client-Side Token Extraction, Server-Side AI Analysis with Gemini 1.5 Pro, and Real-Time Interactive Scoring UI.

Resumetrix Processing Flow

1. Drag & Drop PDF
User uploads PDF in browser; binary array buffer is read locally.
2. WASM Coordinate Extraction
PDF text blocks are extracted and sorted by (X, Y) spatial bounds.
3. Secure API Proxy
Cleaned text payload sent to Express backend with Gemini proxy.
4. Structured JSON Output
Gemini returns typed scorecard with missing skills and rewritten bullet points.
5. Reactive Scorecard UI
Framer Motion animated metrics and instant inline editor.

#Solving Multi-Column PDF Parsing

Standard PDF text extraction streams text items in the order they were compiled into the binary stream, which often weaves left and right columns together. To solve this, we extract spatial metadata `[x, y, width, height]` for each text item and apply a column-clustering algorithm:

src/lib/pdfParser.ts
typescript
1400 font-semibold">export 400 font-semibold">interface TextItemWithBounds {
2 300 font-medium">str: 300 font-medium">string;
3 x: 300 font-medium">number;
4 y: 300 font-medium">number;
5 width: 300 font-medium">number;
6 height: 300 font-medium">number;
7}
8
9400 font-semibold">export 400 font-semibold">function 300 font-medium">sortPdfPageItems(items: TextItemWithBounds[]): 300 font-medium">string {
10 400 font-semibold">const TOLERANCE_Y = 4; // 4px margin 400 font-semibold">for horizontal line alignment
11
12 400 font-semibold">const sorted = [...items].300 font-medium">sort((a, b) => {
13 400 font-semibold">const yDiff = Math.300 font-medium">abs(a.y - b.y);
14 400 font-semibold">if (yDiff < TOLERANCE_Y) {
15 400 font-semibold">return a.x - b.x; // Same line, sort left to right
16 }
17 400 font-semibold">return b.y - a.y; // Higher Y coordinate comes first on page
18 });
19
20 400 font-semibold">return sorted.300 font-medium">map((item) => item.300 font-medium">str).300 font-medium">join(400 font-semibold">class="text-emerald-300">" ");
21}

#Enforcing Structured AI JSON Outputs

To ensure our React frontend receives deterministic types, we pass a `responseSchema` definition to Google GenAI SDK. This eliminates unparseable Markdown blocks or missing fields:

server/geminiParser.ts
typescript
1400 font-semibold">import { GoogleGenAI, Type } 400 font-semibold">from 400 font-semibold">class="text-emerald-300">"@google/genai";
2
3400 font-semibold">const ai = 400 font-semibold">new 300 font-medium">GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
4
5400 font-semibold">export 400 font-semibold">async 400 font-semibold">function 300 font-medium">analyzeResume(resumeText: 300 font-medium">string, jobDescription: 300 font-medium">string) {
6 400 font-semibold">const response = 400 font-semibold">await ai.models.300 font-medium">generateContent({
7 model: 400 font-semibold">class="text-emerald-300">"gemini-1.5-pro",
8 contents: [
9 {
10 role: 400 font-semibold">class="text-emerald-300">"user",
11 parts: [
12 { text: 400 font-semibold">class="text-emerald-300">`Job Description: ${jobDescription}` },
13 { text: 400 font-semibold">class="text-emerald-300">`Candidate Resume: ${resumeText}` },
14 ],
15 },
16 ],
17 config: {
18 responseMimeType: 400 font-semibold">class="text-emerald-300">"application/json",
19 responseSchema: {
20 400 font-semibold">type: Type.OBJECT,
21 properties: {
22 atsScore: { 400 font-semibold">type: Type.NUMBER, description: 400 font-semibold">class="text-emerald-300">"Overall ATS compatibility percentage 0-100" },
23 missingSkills: { 400 font-semibold">type: Type.ARRAY, items: { 400 font-semibold">type: Type.STRING } },
24 strengths: { 400 font-semibold">type: Type.ARRAY, items: { 400 font-semibold">type: Type.STRING } },
25 bulletImprovements: {
26 400 font-semibold">type: Type.ARRAY,
27 items: {
28 400 font-semibold">type: Type.OBJECT,
29 properties: {
30 original: { 400 font-semibold">type: Type.STRING },
31 improved: { 400 font-semibold">type: Type.STRING },
32 impactReason: { 400 font-semibold">type: Type.STRING },
33 },
34 required: [400 font-semibold">class="text-emerald-300">"original", 400 font-semibold">class="text-emerald-300">"improved", 400 font-semibold">class="text-emerald-300">"impactReason"],
35 },
36 },
37 },
38 required: [400 font-semibold">class="text-emerald-300">"atsScore", 400 font-semibold">class="text-emerald-300">"missingSkills", 400 font-semibold">class="text-emerald-300">"strengths", 400 font-semibold">class="text-emerald-300">"bulletImprovements"],
39 },
40 },
41 });
42
43 400 font-semibold">return JSON.300 font-medium">parse(response.text!);
44}

Pro-Tip: Temperature Tuning for Structured Data

Setting `temperature: 0.2` when requesting structured schemas significantly decreases hallucinatory keys while maintaining creative bullet point rewrites.

#Performance Benchmarks

PhaseLegacy Regex MethodResumetrix PipelineSpeedup / Delta
PDF Parsing1,800 ms (Server Upload)140 ms (Client WASM)12.8x Faster
Format Accuracy62% (Misses columns)98.5% (Bounding Box Sort)+36.5% Precision
API Reliability84% (Raw text JSON fail)99.9% (Schema Enforced)Zero Type Errors
Client Memory42 MB8 MB80% Less RAM

#Key Takeaways

1. Combine in-browser processing with serverless GenAI for privacy and ultra-fast FCP. 2. Always enforce strict JSON schemas on LLM outputs for production web applications. 3. Spatial bounding box sorting is indispensable for document processing pipelines.

FINAL CHAPTER
LET'S BUILD THE FUTURE.

Always Learning. Always Building.

Available for Software Engineering • AI Engineering • Full Stack Development
GitHub
LinkedIn
Resume
Email
zsh - status.sh
BHUVANESH

Final-year Computer Science student building web applications, exploring applied AI, and turning concepts into real-world projects with clean code and care.

India

QUICK LINKS

Designed & Engineered by Bhuvanesh Jujare

Crafted with curiosity, consistency, and clean engineering.

© 2026 Bhuvanesh Jujare