No cloud APIs. No paywalls. No intermediaries. Just text from images, running locally everywhere.
locr is an open-source standard for local image-to-text extraction. One core engine compiles to native binaries and WebAssembly, then ships with lightweight wrappers for npm and pip.
The engine is 100% Rust (ocrs + RTen), so it runs on ARM/x64, Linux/macOS/Windows, browsers, and edge without installing native OCR binaries. No cloud. No phone home.
| Surface | Engine today | Notes |
|---|---|---|
| Rust / C ABI / WASM | pure-Rust ocrs + RTen | models embedded at build time, SHA-256 verified |
Python (pip install locr) |
pure-Rust via PyO3 when the wheel is present | optional locr[fallback] uses system Tesseract |
JavaScript (npm install locr) |
tesseract.js (temporary) | WASM core ships next — migration in progress |
The C ABI and Rust core are the source of truth. npm is honest about the temporary bridge so nobody gets surprised on day one.
npm install locrimport { imageToText } from 'locr';
const text = await imageToText('invoice.png');
console.log(text);pip install locrfrom locr import image_to_text
text = image_to_text("invoice.png")
print(text)[dependencies]
locr-core = "0.1"use locr_core::{self, EnhanceOptions};
// `shared()` caches the engine — use this in hot paths / batch OCR.
let locr = locr_core::shared()?;
let text = locr.image_to_text(&image_bytes)?;
// Superpower: quality score + auto-enhance when the score is low.
// If score < 0.55, locr retries with contrast/brightness/saturation/etc.
let result = locr.image_to_text_auto(&image_bytes, EnhanceOptions::default())?;
println!("{} (score={:.2}, via {})", result.text, result.score, result.transform.as_str());Consume the frozen C ABI in crates/locr-ffi/include/locr.h. Release artifacts are built by CI on every tag.
#include "locr.h"
char *text = NULL;
if (locr_image_to_text(bytes, len, &text) == LOCR_OK) {
printf("%s\n", text);
locr_free_text(text);
}locr/
├── crates/locr-core # Rust engine (trait + ocrs backend, shared() cache)
├── crates/locr-ffi # Stable C ABI (cdylib + staticlib)
├── crates/locr-wasm # wasm32 target
├── packages/locr-js # npm wrapper (tesseract.js bridge → WASM next)
├── packages/locr-py # pip wrapper (PyO3, optional Tesseract fallback)
└── .github/workflows # cross-platform CI/CD
| Platform | Native | WASM | npm | pip |
|---|---|---|---|---|
| Linux x64 | ✅ | ✅ | ✅ | ✅ |
| Linux ARM64 | ✅ | ✅ | ✅ | ✅ |
| macOS x64 | ✅ | ✅ | ✅ | ✅ |
| macOS ARM64 | ✅ | ✅ | ✅ | ✅ |
| Windows x64 | ✅ | ✅ | ✅ | ✅ |
| Browser / Edge | — | ✅ | ✅* | — |
* npm currently uses tesseract.js; pure WASM path is next.
- Images are processed on-device.
- Rust / C / WASM runtime path makes no network requests (models bundled + SHA-256 pinned at build time).
- No subscription tiers.
- Open standard: anyone can implement the same
locr.hABI. - Optional Python Tesseract fallback is opt-in (
pip install 'locr[fallback]'), never a hard dependency.
- Recognition models are Latin/English-oriented today; multi-language opts land before ABI 1.0 freeze.
- First-call latency pays for model load; subsequent calls reuse a process-wide engine (
shared()/ OnceLock). - WASM binary with bundled models is large (~10–20MB). CDN + IndexedDB cache is the planned browser path.
Code: MIT — see LICENSE.
Bundled OCR models: CC-BY-SA 4.0 — see crates/locr-core/NOTICE-MODELS.
Built with 🔥 by KevRojo & the locr community.