2.9 KiB
2.9 KiB
Tesseract.js Client-Side Debugging Pattern
The Problem
Tesseract.recognize() in the browser has multiple silent phases before "recognizing text":
loading tesseract core— downloads WASM from CDNinitializing tesseract— sets up the engineloading language traineddata— downloadseng.traineddatafrom CDNinitializing api— final setuprecognizing text— actual OCR (the only phase with progress %)
If any phase fails (CSP block, CDN timeout, worker crash), the promise never resolves or rejects. The UI just shows "Running OCR..." forever.
The Fix: Promise.race + Full Phase Logger
// Show ALL phases, not just "recognizing text"
const ocrResult = await Promise.race([
Tesseract.recognize(preprocessed, 'eng', {
logger: m => {
console.log(`[OCR] ${m.status} ${m.progress ? Math.round(m.progress*100)+'%' : ''}`);
if (m.status === 'recognizing text') {
progressText.textContent = `OCR: ${Math.round(m.progress*100)}%`;
} else {
progressText.textContent = `Tesseract: ${m.status}...`;
}
},
tessedit_pageseg_mode: '4'
}),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('OCR timed out after 90s')), 90000)
)
]);
Why this works:
Promise.racebreaks the hang — after 90s, it rejects with a clear error- Full phase logging shows EXACTLY where it's stuck ("loading language traineddata..." = CDN issue)
- The console gets timestamped
[OCR]logs for post-mortem debugging
Performance: Adaptive Upscaling
3× upscale on HD screenshots creates massive canvases (5760×3240). Use adaptive scaling:
// 2x for large images, 3x for small ones
const scale = (img.width * img.height > 1000000) ? 2 : 3;
canvas.width = img.width * scale;
canvas.height = img.height * scale;
This cuts pixel count by ~55% on typical screenshots while keeping 3× for small crops.
Second OCR Pass (Date Extraction)
The date OCR pass uses PSM 11 (sparse text) and runs on the same preprocessed blob. Apply the same pattern with a shorter timeout (30s):
const dateOcrResult = await Promise.race([
Tesseract.recognize(preprocessed, 'eng', {
logger: m => { /* same pattern */ },
tessedit_pageseg_mode: '11'
}),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Date OCR timed out')), 30000)
)
]);
Ollama Cold-Model 502 Retry
The VPS proxy may return 502 when the local model is unloaded (KEEP_ALIVE=5m). The model takes ~3-6s to load. Retry once after a 5s delay:
for (let attempt = 0; attempt < 2; attempt++) {
const resp = await fetch(endpoint, { ... });
if (resp.ok) break;
if (resp.status !== 502 && resp.status !== 503) throw new Error(...);
await new Promise(r => setTimeout(r, 5000)); // wait for model load
}