RenderCheck
Assert the text users actually see, not just what is in the DOM. RenderCheck takes a screenshot in your
Playwright or Cypress test, reads it with OCR on your machine, and fails the test when the rendered text is
wrong, too faint to read, or missing. No API key, no upload, no network calls.
Expected to find text "[Total: $14.00]" but OCR read "[Total: €14.00]" (Confidence: 96%)
Diff: Total: [$]14.00 → Total: [€]14.00
npm install --save-dev @rendercheck/sdk
import { test } from '@playwright/test';
import { expect } from '@rendercheck/sdk/playwright';
test('checkout shows the right total', async ({ page }) => {
await page.goto('/checkout');
await expect(page).toShowText('Total: $14.00');
});
This repository also contains the free browser tool at getrendercheck.com, which extracts text from images
entirely in your browser (Tesseract.js, WebAssembly). The rest of this README describes that tool.
Free image-to-text tool
Free, private OCR that runs entirely in your browser: zero API costs, and your images never leave your device.

How it is built: see docs/ARCHITECTURE.md for the system, browser and SDK diagrams and a table mapping each privacy claim to the code and test that back it.
✨ Features
- 🚀 Fast - Runs on your device, so there is no upload wait
- 🔒 Private - Images are read in your browser and are not uploaded
- 🤖 Local OCR - Tesseract.js (WebAssembly) runs in your browser, self-hosted, with no model download
- 📊 Honest confidence - Low-confidence results are returned and flagged, not silently replaced
- 🎨 Image Enhancement - Preprocessing steps before recognition
- 💰 Completely Free - No API costs, no hidden fees
- 🌓 Dark/Light Mode - Beautiful UI with theme toggle
- 📱 Mobile Friendly - Responsive design for all devices
- ✨ V3 Premium UI - Aurora gradients, glass-morphism, micro-interactions
- ⌨️ Keyboard Shortcuts - Quick actions with keyboard (C/D/H/T/Escape)
- ♿ Accessible - Keyboard navigation; serious and critical axe-core checks run in CI
- 🎯 SEO Optimized - Intent-specific pages with FAQ rich snippets
🎨 UI Variants
V3 Premium UI (Recommended)
Enable with VITE_UX_V2=1 environment variable for:
- Aurora Background - Animated gradient blobs with grain texture
- Glass Morphism - Modern glassmorphic cards with backdrop blur
- Staged Progress - 3-step progress indicator (Upload → OCR → Render)
- Confetti Animation - Celebratory micro-interaction on copy
- History Drawer - Slide-in drawer with last 20 results
- Keyboard Shortcuts:
C - Copy result
D - Download result
H - View history
T - Toggle theme
Escape - Close drawer/clear result
- Core Web Vitals - LCP < 1.8s, INP < 200ms, CLS < 0.1
- 5 Intent Pages - SEO-optimized routes with FAQ sections
Classic UI (Fallback)
Original simple UI without premium features.
🔍 SEO Defaults
All canonical and OG/Twitter image URLs are computed as absolute using VITE_SITE_URL (see .env.example).
- Default OG image:
/og/default.png (1200×630, logo/high contrast, ≤200KB recommended)
- Set per-route OG via
ogImage prop in SEO helpers/components.
- Canonical links are always absolute.
How to set VITE_SITE_URL:
- Locally: add to
.env.local (e.g. VITE_SITE_URL=http://localhost:5173)
- Vercel/Production: set in dashboard or
vercel.json env block.
Validation:
After deploy, validate with Twitter Card Validator and Facebook Sharing Debugger.
- Per-word confidence scores
- Low-confidence results are flagged for review
- Accuracy depends on the image. There is no general accuracy figure;
packages/rendercheck/benchmarks is a small self-made suite that shows the class of bug and lists the cases where it is wrong
🧩 Spec-Driven Development (SDD)
This repo uses Spec-Driven Development for reliability and accessibility. All features, APIs, schemas, and UX contracts are defined in /specs and enforced in CI.
How to work with specs
- Product features:
/specs/product/*.feature (BDD scenarios)
- API contracts:
/specs/api/openapi.yml (OpenAPI)
- Schemas:
/specs/schemas/*.json (JSON Schema)
- UX tokens/contracts:
/specs/ux/tokens.json, /specs/ux/components/*.md
- AI goldens:
/specs/ai/evals/goldens/*.json
How AI agents should use specs
- Always read relevant specs before proposing changes
- Update tests and contracts if specs change
- Use BDD runner and schema guard helpers for validation
PR Checklist
CI Gates
npm run spec:lint: Spectral lint for OpenAPI
npm run spec:schemas: Validate JSON schemas and tokens
npm run spec:test: Playwright BDD tests
npm run spec:diff: OAS diff for breaking changes
Prerequisites
- Node.js 20+
- npm 10+ or yarn 4+
Quick Start (Development)
# Clone and setup
git clone https://github.com/ParthibanRajasekaran/text-from-image.git
cd text-from-image
npm install
# Start dev server
npm run dev
# Visit http://localhost:5173
# Run tests
npm run test
# Build for production
npm run build
🚀 Deployment
Deploy to Vercel (Production)
Deploy your EXACT local code to production with zero drift:
# First time setup
npm run vercel:link # Link to your Vercel project
npm run vercel:pull:prod # Pull production environment variables
# Deploy to production
npm run deploy:prod
After deployment, verify the footer shows the correct commit SHA matching your local git commit.
GitHub Actions (Automated Deploys)
This project uses controlled deployments to avoid accidental deploys:
Preview Deployments (Pull Requests):
- Only triggers when PR has
deploy:preview label
- Deploys to preview URL, posted as PR comment
- Cancelled automatically when new commits pushed
Production Deployments (Main Branch):
- Automatically deploys on push to
main branch
- Skip with
[skip deploy] in commit message
- Only deploys if app files changed (not docs-only)
Manual Deployment:
- GitHub UI: Actions → Deploy to Vercel → Run workflow
Required GitHub Secrets
Add these in Settings → Secrets and variables → Actions:
| Secret | Where to get it |
|---|
VERCEL_TOKEN | https://vercel.com/account/tokens |
VERCEL_ORG_ID | Run vercel link, check .vercel/project.json |
VERCEL_PROJECT_ID | Run vercel link, check .vercel/project.json |
Environment Variables (Vercel)
Set these in Vercel Dashboard → Project → Settings → Environment Variables:
# Feature Flags (Production environment)
VITE_UX_V2=true # Enable enhanced UI
VITE_ANALYTICS_ENABLED=true # Enable Web Vitals tracking
VITE_OCR_MIN_CONFIDENCE=60 # Tesseract confidence threshold
📖 Full deployment guide: See docs/DEPLOYMENT.md for:
- Zero-drift deployment strategy
- Bundle size monitoring
- CI/CD setup
- Troubleshooting common issues
🏗️ Architecture
Application Structure
pages/ # SEO-optimized route pages (lazy-loaded)
├── ImageToText.tsx
├── ExtractTextFromImage.tsx
└── ...
components/ # Reusable React components
├── v3/ # Premium UI (Aurora, Glass, History)
└── ...
hooks/ # Custom React hooks (useShortcuts, useLocalHistory, etc.)
lib/ # Business logic
├── analytics.ts # Web Vitals tracking
└── config/ # Feature flags & env guards
services/ # OCR engine (Tesseract.js, in the browser)
src/test/ # Test suite (vitest): routes, SEO, a11y
Request Flow: Image Upload → OCR → Result
User Upload
↓
File Validation (type, size)
↓
Image Preprocessing (optional)
↓
Tesseract OCR (in the browser, 2-5s)
├─ Confidence ≥ 60%? → Return ✓
└─ Below 60% → Return, flagged low confidence ✓
↓
Result Display + Copy/Download
Tech Stack
| Layer | Technology |
|---|
| UI | React 19.2 + TypeScript 5.8 |
| Build | Vite 6.2 + Rollup (code splitting) |
| OCR | Tesseract.js 5.1 (WebAssembly, self-hosted) |
| Styling | CSS + Tailwind + Custom properties |
| State | React Hooks (useReducer, Context) |
| Testing | Vitest + Testing Library |
| Deployment | Vercel (automated via GitHub Actions) |
Feature Flags & Configuration
See .env.example for all available flags:
| Flag | Purpose | Default |
|---|
VITE_UX_V2 | Enable premium UI (Aurora/Glass) | false |
VITE_SITE_URL | Canonical domain for SEO | computed |
VITE_OCR_MIN_CONFIDENCE | Tesseract confidence threshold | 60 |
See also: Env Guards for runtime validation.
🛠️ How It Works
1. Image Upload
- Supports PNG, JPG, WEBP
- Max file size: 20MB
- Drag & drop or click to browse
2. Processing
- Step 1: Validate file (type, size, browser compatibility)
- Step 2: Apply image preprocessing (optional, auto-enabled)
- Step 3: Run Tesseract OCR in the browser
- Step 4: If confidence < 60%, show the result flagged as low confidence
3. Results
- Extracted text with confidence score
- Copy to clipboard
- Download as .txt or .doc
- Shows which method was used
📊 Performance
| Aspect | Status |
|---|
| Speed | Depends on your device and image size; the OCR runs in your browser |
| Accuracy | Not benchmarked yet. Clear, high-resolution printed English text reads best |
| Cost | Free |
| Privacy | Images are read in the browser and are not uploaded (see /security) |
| Offline | Works after the OCR model has downloaded once |
Accuracy figures will be published here only with the benchmark script and data that produce them.
🔒 Privacy
- ✅ 100% client-side processing - No server uploads
- ✅ No data collection - Your images never leave your device
- ✅ No API keys required - No external dependencies
- ✅ No tracking - Privacy-first design
- ✅ Nothing to collect - Images are processed in your browser; see the privacy policy
🚀 Deployment
How We Deploy
This project uses GitHub Actions for automated CI/CD with label-gated preview deploys to avoid waste:
Feature Branch → PR → [add deploy:preview label] → Preview Deploy ✓
↓
Main Branch → Push → Auto-Production Deploy ✓
Key principles:
- ✅ All code changes go through tests first
- ✅ Production deploys only from
main branch
- ✅ Preview deploys opt-in (require
deploy:preview label)
- ✅ Skip deploys with
[skip deploy] in commit message
Deployment workflow: .github/workflows/deploy.yml
Environment config: docs/DEPLOYMENT.md (setup, troubleshooting, env vars)
Local Deployment (Testing)
# Setup (one-time)
npm install -g vercel
vercel link --yes
# Deploy preview locally
vercel
# Deploy production locally
vercel --prod --confirm
📦 Bundle Size & Code Splitting
Optimizations
This project uses aggressive code splitting to minimize initial load time:
- ✅ Dynamic Imports - Heavy libraries loaded only when needed
- ✅ Vendor Chunking - React, Tesseract, motion in separate chunks
- ✅ Lazy Loading - OCR engines loaded on-demand, not upfront
- ✅ Tree Shaking - Unused code automatically removed
Bundle Breakdown
| Chunk | Size (gzipped) | When Loaded |
|---|
index.js (main) | ~145 KB | Initial page load |
react-vendor.js | ~140 KB | Initial page load |
tesseract.js | ~23 KB | When user starts OCR |
jspdf.js | ~40 KB | When user exports to PDF |
First-load JS: ~285 KB (gzipped) ✅
Total app (all features): ~547 KB (gzipped)
Analyze Bundle
# Generate interactive bundle visualization
npm run analyze
# Opens stats.html with treemap of all chunks
The analyzer shows:
- Which dependencies contribute most to bundle size
- How code splitting distributes code across chunks
- Gzipped and Brotli compressed sizes
- Duplicate dependencies (if any)
How Code Splitting Works
1. Tesseract.js (23 KB)
// ❌ Before: Loaded upfront
import Tesseract from 'tesseract.js';
// ✅ After: Loaded when user starts OCR
const { recognize } = await import('tesseract.js');
2. jsPDF (40 KB)
// ❌ Before: CDN script tag in index.html
<script src="https://cdn.../jspdf.umd.min.js"></script>
// ✅ After: Loaded only when user exports PDF
const { default: jsPDF } = await import('jspdf');
Performance Impact
| Metric | Before | After | Improvement |
|---|
| First load JS | ~850 KB | ~285 KB | 67% reduction |
| Time to Interactive | ~5.2s | ~1.8s | 3.4s faster |
| Lighthouse Score | 72 | 94 | +22 points |
| Build warnings | "Some chunks > 500 KB" | ✅ None | Fixed |
Best Practices
✅ DO:
- Keep initial bundle under 300 KB (gzipped)
- Use dynamic imports for heavy libraries
- Analyze bundle regularly with
npm run analyze
- Lazy-load features users might not use
❌ DON'T:
- Import entire libraries when you only need a few functions
- Load OCR engines upfront before user uploads image
- Bundle large assets (WASM, models) - serve from CDN or
/public
Related Files
vite.config.ts - Rollup manual chunks configuration
services/*Service.ts - Dynamic imports for OCR engines
utils/fileUtils.ts - Dynamic import for jsPDF
package.json - Bundle analyzer script
🔒 No ads, no third-party scripts
The site ships no advertising and loads nothing from a third party at runtime. This is deliberate:
- The Content-Security-Policy in
vercel.json is script-src 'self' 'wasm-unsafe-eval' and connect-src 'self', so the OCR worker and WebAssembly run from the site's own origin and cannot be interfered with.
- RenderCheck's pitch is that nothing leaves the browser. An ad network would fill the network panel with requests that make that claim hard to believe.
e2e/no-third-party.spec.ts fails if any route requests another origin or if the policy allows an ad host.
SEO:
- All pages have unique meta tags and canonical URLs.
- Guides and policy pages are crawlable and linked from Home/footer.
- robots.txt and sitemap.xml are present.
Performance:
- No OCR/UX regressions; light/dark mode consistent.
🤝 Contributing
See CONTRIBUTING.md for:
- Code standards & TypeScript conventions
- Accessibility (a11y) requirements
- Testing & how to run the test suite
- Commit message conventions
- PR process & deprecation policy
Quick checklist before submitting PR:
npm run lint # Fix linting issues
npm run test # Run full test suite
npm run build # Verify production build
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
Third-Party Licenses
- Tesseract.js - Apache 2.0
- React - MIT
- Vite - MIT
All dependencies are compatible with commercial use.
🙏 Acknowledgments
📮 Contact
🌟 Show Your Support
If you find this project useful, please give it a ⭐️ on GitHub!
Made with ❤️ by Parthiban Rajasekaran