OCR-powered spell checker for burned-in text in videos and images, with a server-side transcript-aware proofing pipeline and BYOK OpenRouter integration.
Features · Architecture · Getting Started · Deploy

localStorage.ProofFrame is a two-process app:
| Process | What it does | Where it can run |
|---|---|---|
| Web frontend (Vite/React) | Landing/pricing pages, scan UI, and the entire image scan pipeline (OCR + spell-check) client-side | Any static host (Vercel, etc.) |
| Analysis API (Express) | The video proofing pipeline: native ffmpeg frame/audio extraction, server-side OCR, transcript consensus, alignment, and the AI judge | A long-running Node host with ffmpeg/ffprobe installed and writable disk |
The frontend talks to the API at /api/*. In development the Vite dev server proxies /api to the backend. In a deployed split setup, set VITE_PROOFFRAME_API_BASE so the static frontend points at the hosted API.
BYOK: there is no server-stored AI key. Each user pastes their own OpenRouter key into the scan settings (stored in localStorage); it is forwarded to the backend only for the duration of that scan and is never persisted server-side.
Video jobs and their artifacts (frames, crops, audio, result.json) are written under .proofframe/jobs/<scanId>/ (gitignored).
ffmpeg and ffprobe on PATH (required for video scanning; image scanning works without them)
brew install ffmpeggit clone https://github.com/markksantos/proofframe.git
cd proofframe
npm install
# Optional: copy and tweak env config
cp .env.example .env
# Start both the API and the web app together
npm run dev
/api/health)Open the app, go to Scan, and drop an image or video. For transcript-aware video proofing, paste an OpenRouter key in the settings card (optional).
| Script | Description |
|---|---|
npm run dev | Run API + web concurrently (development) |
npm run dev:web | Web only (Vite) |
npm run dev:api | API only (tsx watch) |
npm run start:api | API only, production (tsx, no watch) |
npm run build | Type-check + production web build to dist/ |
npm run typecheck | Type-check without emitting |
npm run lint | ESLint |
npm test | Vitest |
ProofFrame deploys as two pieces:
vercel.json sets the SPA rewrite and the COOP/COEP headers that Tesseract.js/WASM need. Build command npm run build, output dist/. Set VITE_PROOFFRAME_API_BASE to your API origin at build time so video scans reach the backend.ffmpeg and keep a process alive with writable disk). Run npm run start:api. Lock down CORS with PROOFFRAME_CORS_ORIGIN=<your frontend origin>.The API cannot run on Vercel/Netlify serverless: it needs native ffmpeg, persistent disk for job artifacts, and long-lived polling jobs.
If you only need image proofing, the static frontend alone is fully functional — video scanning simply reports the API as unavailable.
See .env.example. Summary:
| Variable | Process | Purpose |
|---|---|---|
PROOFFRAME_API_PORT | API | API port (default 8787) |
PROOFFRAME_CORS_ORIGIN | API | Comma-separated allowed origins (production) |
PROOFFRAME_JUDGE_MODEL | API | Override the AI judge model |
PROOFFRAME_STT_MODELS | API | Override the transcription models |
VITE_PROOFFRAME_API_BASE | Web (build-time) | API origin for deployed split mode |
| Category | Technology |
|---|---|
| Frontend | React 19, TypeScript 5.9, Vite 7 |
| Styling | Tailwind CSS 4 |
| Routing | React Router DOM 7 |
| Animations | Framer Motion 12 |
| OCR | Tesseract.js 7 (browser + server) |
| Backend | Express 5, Node.js, native ffmpeg/ffprobe |
| Spell Check | nspell 2 (en_US Hunspell dictionary) |
| AI (BYOK) | OpenRouter (audio transcription + evidence judge) |
| jsPDF 4 | |
| Icons | Lucide React |
| Tests | Vitest |
Images (browser): upload → Tesseract OCR → nspell spell-check → overlay errors → optional PDF export.
Videos (server): upload → native ffmpeg extracts audio + timestamped frames → (BYOK) consensus transcription across OpenRouter models → server OCR per frame → group visible text into time-ranged segments → spell-check + align against transcript (mismatch / missing-caption / timing / unreadable) → optional AI evidence judge → results with frame thumbnails, crops, and time ranges.
MIT License © 2025 Mark Santos
11 commits
TypeScript
98.2%
OCR-powered spell checker for burned-in text in videos and images, with a server-side transcript-aware proofing pipeline and BYOK OpenRouter integration.
Features · Architecture · Getting Started · Deploy

localStorage.ProofFrame is a two-process app:
| Process | What it does | Where it can run |
|---|---|---|
| Web frontend (Vite/React) | Landing/pricing pages, scan UI, and the entire image scan pipeline (OCR + spell-check) client-side | Any static host (Vercel, etc.) |
| Analysis API (Express) | The video proofing pipeline: native ffmpeg frame/audio extraction, server-side OCR, transcript consensus, alignment, and the AI judge | A long-running Node host with ffmpeg/ffprobe installed and writable disk |
The frontend talks to the API at /api/*. In development the Vite dev server proxies /api to the backend. In a deployed split setup, set VITE_PROOFFRAME_API_BASE so the static frontend points at the hosted API.
BYOK: there is no server-stored AI key. Each user pastes their own OpenRouter key into the scan settings (stored in localStorage); it is forwarded to the backend only for the duration of that scan and is never persisted server-side.
Video jobs and their artifacts (frames, crops, audio, result.json) are written under .proofframe/jobs/<scanId>/ (gitignored).
ffmpeg and ffprobe on PATH (required for video scanning; image scanning works without them)
brew install ffmpeggit clone https://github.com/markksantos/proofframe.git
cd proofframe
npm install
# Optional: copy and tweak env config
cp .env.example .env
# Start both the API and the web app together
npm run dev
/api/health)Open the app, go to Scan, and drop an image or video. For transcript-aware video proofing, paste an OpenRouter key in the settings card (optional).
| Script | Description |
|---|---|
npm run dev | Run API + web concurrently (development) |
npm run dev:web | Web only (Vite) |
npm run dev:api | API only (tsx watch) |
npm run start:api | API only, production (tsx, no watch) |
npm run build | Type-check + production web build to dist/ |
npm run typecheck | Type-check without emitting |
npm run lint | ESLint |
npm test | Vitest |
ProofFrame deploys as two pieces:
vercel.json sets the SPA rewrite and the COOP/COEP headers that Tesseract.js/WASM need. Build command npm run build, output dist/. Set VITE_PROOFFRAME_API_BASE to your API origin at build time so video scans reach the backend.ffmpeg and keep a process alive with writable disk). Run npm run start:api. Lock down CORS with PROOFFRAME_CORS_ORIGIN=<your frontend origin>.The API cannot run on Vercel/Netlify serverless: it needs native ffmpeg, persistent disk for job artifacts, and long-lived polling jobs.
If you only need image proofing, the static frontend alone is fully functional — video scanning simply reports the API as unavailable.
See .env.example. Summary:
| Variable | Process | Purpose |
|---|---|---|
PROOFFRAME_API_PORT | API | API port (default 8787) |
PROOFFRAME_CORS_ORIGIN | API | Comma-separated allowed origins (production) |
PROOFFRAME_JUDGE_MODEL | API | Override the AI judge model |
PROOFFRAME_STT_MODELS | API | Override the transcription models |
VITE_PROOFFRAME_API_BASE | Web (build-time) | API origin for deployed split mode |
| Category | Technology |
|---|---|
| Frontend | React 19, TypeScript 5.9, Vite 7 |
| Styling | Tailwind CSS 4 |
| Routing | React Router DOM 7 |
| Animations | Framer Motion 12 |
| OCR | Tesseract.js 7 (browser + server) |
| Backend | Express 5, Node.js, native ffmpeg/ffprobe |
| Spell Check | nspell 2 (en_US Hunspell dictionary) |
| AI (BYOK) | OpenRouter (audio transcription + evidence judge) |
| jsPDF 4 | |
| Icons | Lucide React |
| Tests | Vitest |
Images (browser): upload → Tesseract OCR → nspell spell-check → overlay errors → optional PDF export.
Videos (server): upload → native ffmpeg extracts audio + timestamped frames → (BYOK) consensus transcription across OpenRouter models → server OCR per frame → group visible text into time-ranged segments → spell-check + align against transcript (mismatch / missing-caption / timing / unreadable) → optional AI evidence judge → results with frame thumbnails, crops, and time ranges.
MIT License © 2025 Mark Santos
11 commits
TypeScript
98.2%