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';consttext=awaitimageToText('invoice.png');console.log(text);pip install locrfromlocrimportimage_to_texttext=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.