This guide covers common issues encountered during development and usage of the Client-Side OCR library, along with their solutions.
- PPU Model Issues
- Stack Overflow Errors
- Model Loading Issues
- Processing Errors
- Performance Issues
- Browser Compatibility
- Memory Issues
- Caching Problems
- Development Issues
Symptoms:
- PPU models return gibberish like "Sdrs", "NBQ", "Gdkkn", "Vnqkc" instead of actual text
- Models work but predictions are completely wrong
- English models producing unreadable output
Root Cause: PPU models use different preprocessing than RapidOCR models:
- Grayscale conversion uses only red channel (not standard luminance formula)
- No pixel inversion required
- Dictionary uses 0-based indexing (not 1-based with blank token offset)
Solution:
// In recognition.worker.v2.ts
if (isUsingPPUModel) {
// PPU models: Use only red channel as grayscale
const grayValue = r; // Not: 0.299*r + 0.587*g + 0.114*b
const normalizedValue = (grayValue / 255.0 - 0.5) / 0.5;
// Fill all three channels with the same value
normalized[outIdx] = normalizedValue;
normalized[targetHeight * tensorWidth + outIdx] = normalizedValue;
normalized[2 * targetHeight * tensorWidth + outIdx] = normalizedValue;
}
// In ppu-model-handler.ts
// Use direct dictionary lookup (0-based)
for (const idx of decoded) {
if (idx < charDict.length) {
text += charDict[idx]; // Not: charDict[idx - 1]
}
}Debugging Steps:
- Check preprocessing normalization values
- Verify dictionary indexing
- Compare with PPU reference implementation
- Test with simple text images first
Symptoms:
- Error: "Maximum call stack size exceeded" when processing large documents
- Occurs with documents that create large tensors (e.g., 1099x48)
- Browser tab crashes or becomes unresponsive
Root Cause:
- Spread operator on large arrays exhausts call stack
- Debug logging operations on large typed arrays
- No width limiting for PPU models
Solution:
// 1. Replace spread operator with loop
// Bad:
results.push(...batchResults);
// Good:
for (const result of batchResults) {
results.push(result);
}
// 2. Limit width for PPU models
if (isUsingPPUModel) {
const maxPPUWidth = 800;
if (resizeWidth > maxPPUWidth) {
console.warn(`PPU model: limiting width from ${resizeWidth} to ${maxPPUWidth}`);
resizeWidth = maxPPUWidth;
tensorWidth = maxPPUWidth;
}
}
// 3. Skip debug operations on large tensors
if (isUsingPPUModel && tensorWidth >= 500) {
console.log(`PPU large tensor: ${tensorWidth}x${targetHeight}, skipping debug output`);
// Skip array operations that could cause stack overflow
}Symptoms:
- 404 errors when fetching model files
- "Model not found" errors
- Initialization hanging indefinitely
Causes & Solutions:
-
Wrong base URL
// Ensure correct base URL for your deployment const ocr = createRapidOCREngine({ language: 'en', baseUrl: '/client-ocr/models' // Adjust for your setup });
-
Missing model files
- Verify all required files are in public/models/
- Check file permissions
- Ensure files are included in build
-
CORS issues
// For cross-origin model loading const ocr = createRapidOCREngine({ language: 'en', fetchOptions: { mode: 'cors', credentials: 'omit' } });
Solution:
-
Enable model caching:
const ocr = createRapidOCREngine({ language: 'en', cacheModels: true });
-
Use CDN for models:
const ocr = createRapidOCREngine({ language: 'en', baseUrl: 'https://cdn.jsdelivr.net/npm/client-side-ocr/models' });
Symptoms:
- Error immediately when trying to process image
- Workers not ready
Solution:
// Always wait for initialization
const ocr = createRapidOCREngine({ language: 'en' });
await ocr.initialize(); // Don't forget to await!
// Or check initialization status
if (!ocr.initialized) {
await ocr.initialize();
}Causes & Solutions:
-
Image too dark/light
const result = await ocr.processImage(image, { preprocessConfig: { contrast: 1.5, brightness: 10 } });
-
Wrong language model
// Detect language first const detectedLang = await detectLanguage(image); const ocr = createRapidOCREngine({ language: detectedLang });
-
Text too small
// Resize image before processing const resized = await resizeImage(image, { minWidth: 1024 });
Solutions:
-
Use mobile models
const ocr = createRapidOCREngine({ language: 'en', modelType: 'mobile' // Faster than 'server' });
-
Reduce image size
const optimized = await resizeImage(image, { maxWidth: 1280, maxHeight: 1280 });
-
Process in chunks
// For PDFs or multiple images for (const page of pages) { const result = await ocr.processImage(page); displayResult(result); // Give UI time to update await new Promise(resolve => setTimeout(resolve, 0)); }
Solutions:
-
Clean up after processing
// Process images one at a time for (const image of images) { const result = await ocr.processImage(image); // Process result... } // Cleanup when done await ocr.cleanup();
-
Limit concurrent processing
const results = await ocr.processBatch(images, { maxConcurrent: 2 // Limit to 2 at a time });
Error: "WebAssembly is not supported in this browser"
Solutions:
- Update browser to latest version
- Enable WebAssembly in browser settings
- Provide fallback message:
if (!window.WebAssembly) { showError('Please use a modern browser that supports WebAssembly'); }
Solutions:
- Check for HTTPS (required for Workers in some browsers)
- Ensure correct MIME types for worker files
- Test Worker support:
if (!window.Worker) { showError('Web Workers not supported'); }
Symptoms:
- Browser tab crashes
- "Out of memory" console errors
- Processing fails on large images
Solutions:
-
Process smaller regions
// Split large image into tiles const tiles = splitImageIntoTiles(image, 1024, 1024); const results = []; for (const tile of tiles) { const result = await ocr.processImage(tile); results.push(result); }
-
Free memory explicitly
// Clear references let result = await ocr.processImage(image); processResult(result); result = null; // Help GC // For canvas canvas.width = 0; canvas.height = 0;
Symptoms:
- Models download every time
- IndexedDB errors
Solutions:
-
Check IndexedDB support
if (!window.indexedDB) { console.warn('IndexedDB not supported, caching disabled'); }
-
Clear corrupted cache
await ocr.clearCache(); await ocr.initialize(); // Re-download
-
Handle quota errors
try { await ocr.initialize(); } catch (error) { if (error.name === 'QuotaExceededError') { await ocr.clearCache(); await ocr.initialize(); } }
Solution:
// Version your cache
const ocr = createRapidOCREngine({
language: 'en',
cacheVersion: 'v2.0.1' // Increment on model updates
});Symptoms:
- Changes not reflected
- Old code running
Solutions:
-
Disable service worker in development
// In vite.config.ts export default { plugins: [ VitePWA({ disable: process.env.NODE_ENV === 'development' }) ] };
-
Force refresh
- Chrome: Ctrl+Shift+R (Cmd+Shift+R on Mac)
- Open DevTools → Application → Service Workers → Update
Error: "Property 'map' does not exist on type 'Float32Array'"
Solution:
// Convert to regular array first
const array = Array.from(float32Array);
const mapped = array.map(v => v * 2);
// Or use TypedArray methods
const doubled = float32Array.map(v => v * 2); // TS 3.1+Solutions:
-
Configure TypeScript paths
// tsconfig.json { "compilerOptions": { "paths": { "client-side-ocr": ["./src/index.ts"], "client-side-ocr/*": ["./src/*"] } } }
-
Use correct imports
// For library users import { createRapidOCREngine } from 'client-side-ocr'; // For development import { createRapidOCREngine } from '@/core/engines';
- Cause: Dictionary mismatch or corrupted model
- Fix: Re-download models, verify dictionary file
- Cause: Old browser or polyfill needed
- Fix: Use polyfill or upgrade browser
- Cause: ONNX Runtime version mismatch
- Fix: Update to compatible version (1.19.2)
- Cause: Vite build issue or missing file
- Fix: Clear cache, rebuild, check build output
If you encounter issues not covered here:
- Check the GitHub Issues
- Enable debug mode for more info:
const ocr = createRapidOCREngine({ language: 'en', debug: true });
- Collect browser info:
- Browser version
- OS
- Console errors
- Network tab screenshots
- Create a minimal reproduction
- File an issue with details