Check from a screenshot that text is really visible to users. Local, offline OCR.
Runs on your machine from its package; the steps are below.
At most: only reads or looks up information.
Odel charges nothing per call. The publisher may charge for its own service.
Runs locally over stdio
This server isn't hosted — your MCP client launches it from a package registry. Use one of the commands below, or drop the config into your client (e.g. Claude Desktop).
Run
npx -y @rendercheck/sdk
MCP client config
{
"mcpServers": {
"rendercheck": {
"command": "npx",
"args": [
"-y",
"@rendercheck/sdk"
]
}
}
}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');
});
packages/rendercheckThis 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, 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.
Enable with VITE_UX_V2=1 environment variable for:
C - Copy resultD - Download resultH - View historyT - Toggle themeEscape - Close drawer/clear resultOriginal simple UI without premium features.
All canonical and OG/Twitter image URLs are computed as absolute using VITE_SITE_URL (see .env.example).
/og/default.png (1200×630, logo/high contrast, ≤200KB recommended)ogImage prop in SEO helpers/components.How to set VITE_SITE_URL:
.env.local (e.g. VITE_SITE_URL=http://localhost:5173)vercel.json env block.Validation: After deploy, validate with Twitter Card Validator and Facebook Sharing Debugger.
packages/rendercheck/benchmarks is a small self-made suite that shows the class of bug and lists the cases where it is wrongThis repo uses Spec-Driven Development for reliability and accessibility. All features, APIs, schemas, and UX contracts are defined in /specs and enforced in CI.
/specs/product/*.feature (BDD scenarios)/specs/api/openapi.yml (OpenAPI)/specs/schemas/*.json (JSON Schema)/specs/ux/tokens.json, /specs/ux/components/*.md/specs/ai/evals/goldens/*.jsonnpm run spec:lint: Spectral lint for OpenAPInpm run spec:schemas: Validate JSON schemas and tokensnpm run spec:test: Playwright BDD testsnpm run spec:diff: OAS diff for breaking changes# 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
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.
This project uses controlled deployments to avoid accidental deploys:
Preview Deployments (Pull Requests):
deploy:preview labelProduction Deployments (Main Branch):
main branch[skip deploy] in commit messageManual Deployment:
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 |
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:
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
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
| 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) |
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.
| 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.
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:
main branchdeploy:preview label)[skip deploy] in commit messageDeployment workflow: .github/workflows/deploy.yml
Environment config: docs/DEPLOYMENT.md (setup, troubleshooting, env vars)
# Setup (one-time)
npm install -g vercel
vercel link --yes
# Deploy preview locally
vercel
# Deploy production locally
vercel --prod --confirm
This project uses aggressive code splitting to minimize initial load time:
| 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)
# Generate interactive bundle visualization
npm run analyze
# Opens stats.html with treemap of all chunks
The analyzer shows:
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');
| 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 |
✅ DO:
npm run analyze❌ DON'T:
/publicvite.config.ts - Rollup manual chunks configurationservices/*Service.ts - Dynamic imports for OCR enginesutils/fileUtils.ts - Dynamic import for jsPDFpackage.json - Bundle analyzer scriptThe site ships no advertising and loads nothing from a third party at runtime. This is deliberate:
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.e2e/no-third-party.spec.ts fails if any route requests another origin or if the policy allows an ad host.SEO:
Performance:
See CONTRIBUTING.md for:
Quick checklist before submitting PR:
npm run lint # Fix linting issues
npm run test # Run full test suite
npm run build # Verify production build
This project is licensed under the MIT License - see the LICENSE file for details.
All dependencies are compatible with commercial use.
If you find this project useful, please give it a ⭐️ on GitHub!
Made with ❤️ by Parthiban Rajasekaran