pt-perkasa-pilar-utama/ppu-paddle-ocr/skill-ppu-paddle-ocr
Claude Code and agent skill by pt-perkasa-pilar-utama/ppu-paddle-ocr
- community
Checking that the SKILL.md file is still in the repository.
Use this skill whenever the user is writing TypeScript/JavaScript code that imports `ppu-paddle-ocr`, `ppu-paddle-ocr/web`, or `ppu-paddle-ocr/mobile`, or asks about PaddleOCR / PP-OCRv6 / PP-OCRv5 in JavaScript runtimes - text detection, text recognition, OCR on receipts/invoices/documents, ONNX Runtime OCR, multilingual OCR (Latin, Cyrillic, Arabic, Indic, CJK, Thai), WebGPU OCR in browsers, browser-extension OCR, React Native / Expo OCR on iOS and Android (Skia + onnxruntime-react-native), I…
Install
npx vskill@latest install pt-perkasa-pilar-utama/ppu-paddle-ocr/skill-ppu-paddle-ocrSKILL.md
Writing code with ppu-paddle-ocr
ppu-paddle-ocr is a TypeScript SDK around the PP-OCR ONNX models (PP-OCRv6 by default; v3-v5 presets available). One package, one API, every JavaScript runtime - Node.js, Bun, Deno, browsers, browser extensions, and React Native (iOS/Android). It pairs a text-detection model with a text-recognition model and decodes them with a character dictionary; the package ships the PP-OCRv6 tiny multilingual models by default (small/medium presets for full-dictionary coverage) and downloads them on first run.
The two things that bite people first are (1) picking the right entry point for the runtime, and (2) understanding that the service has a real lifecycle - you must await initialize() before recognize(), and call destroy() to free ONNX sessions. Everything else is configuration.
The three entry points
| Import path | Runtime | Canvas backend | Processing engine | Model loading | |
|---|---|---|---|---|---|
ppu-paddle-ocr | Node.js, Bun, Deno servers and CLIs | @napi-rs/canvas via ppu-ocv | "opencv" (default, more accurate) or "canvas-native" | Cached on disk under ~/.cache/ppu-paddle-ocr | |
ppu-paddle-ocr/web | Browsers and browser extensions | HTMLCanvasElement / OffscreenCanvas | Always "canvas-native" - OpenCV.js is not bundled in the web entry point | Fetched via fetch() every page load (relies on HTTP cache) | |
ppu-paddle-ocr/mobile | React Native (iOS / Android) | @shopify/react-native-skia via ppu-ocv/canvas-mobile | Always "canvas-native" - no OpenCV on RN | Fetched via fetch() each initialize() (no built-in disk cache) |
Hard rules:
- The peer dependency depends on the entry point: install
onnxruntime-nodeforppu-paddle-ocr,onnxruntime-webforppu-paddle-ocr/web, andonnxruntime-react-native+ `@shopify/react-native-skia` forppu-paddle-ocr/mobile. All are optional peer deps so the install only pulls what you actually use. - Setting
processing.engine = "opencv"on the/webor/mobileimport is silently ignored -canvas-nativeis the only available backend off Node/Bun. PaddleOcrService.downloadModels()andservice.clearModelCache()are Node/Bun-only (they touch the filesystem). On the web and mobile builds they are not defined.- The
/mobileentry needs native modules, so it requires a dev client or `expo prebuild` - not Expo Go. Targets RN >= 0.74 / Expo SDK >= 51 (Hermes). Inference is CPU by default; passsession: { executionProviders: ["nnapi"] }(Android) or["coreml"](iOS) to opt into hardware acceleration. There is no WebGPU on RN.
When the user describes their runtime in any concrete way ("Bun CLI", "Cloudflare Workers", "Chrome MV3 service worker", "Vite frontend", "Expo app"), bind the import path to that constraint immediately rather than echoing a generic snippet.
The lifecycle contract
Every program follows the same four beats:
initialize() is what fetches the models, parses the dictionary, and builds the detection + recognition InferenceSessions. Until it resolves, recognize() will lazy-init on the first call (it's safe but slower and harder to reason about - prefer to await initialize() explicitly at startup).
destroy() calls release() on both ONNX sessions and nulls them out. In a long-lived server, do not destroy after every request - instantiate one PaddleOcrService per process, keep it warm, share it across requests, and only destroy on graceful shutdown. Each initialize() re-downloads the model from disk into the WASM/native ONNX runtime, which is expensive.
isInitialized() is available as a cheap "is this service ready?" check before sending it to a request handler.
What recognize() accepts and returns
recognize(image, options?) accepts:
string- must be either an absolute filesystem path (Node/Bun) or anhttp(s)://URL. Relative paths are rejected with an explicit error. On web and React Native, only URLs work.ArrayBuffer- raw image bytes (PNG/JPEG/WEBP, anythingppu-ocvcan decode). The most universal input, and the usual one on React Native (a captured/bundled frame).- A canvas -
@napi-rs/canvasCanvason Node/Bun,HTMLCanvasElement/OffscreenCanvason web, or a Skia-backed canvas on React Native. Useful when you've already preprocessed the image and want to skip a re-decode.
Returns one of:
Pass { flatten: true } to get the flat shape - useful for search indexing or when you don't care about line structure. Default is grouped (lines).
If detection finds zero boxes, recognize() returns an empty result (text: "", lines: [] or results: [], confidence: 0) - it does not throw. Callers should branch on result.lines.length === 0 (or result.text === "") rather than wrapping in try/catch.
Detection-only inference: detect()
For a single image, the package entry points also export one-shot ocr() and detect() functions that initialize and destroy a service automatically. Use PaddleOcrService for repeated work so model sessions are reused.
When the user only needs layout - where the text is, not what it says - point them at detect(image, options?) instead of recognize(). It runs just the detection model and returns { boxes: Box[] } in original image coordinates. Same accepted inputs as recognize(), available on all three entry points, and also exposed as the CLI detect command and the serve app's POST /v1/detect.
DetectOptions extends DetectionOptions, so any tuning field (maxSideLength, minimumAreaThreshold, paddings, mean, stdDeviation) can be overridden per call without touching the service defaults. Platform limits: saveCropsTo is ignored on web/mobile (no filesystem); crop: true throws on React Native (the Skia canvas has no PNG encoder). detect() results are not cached - the result cache only covers recognize().
The result cache
recognize() caches results in-memory keyed by a hash of the input image bytes. This means calling recognize() twice with the same ArrayBuffer (or the same canvas serialised the same way) returns the cached result instantly - including the same flatten/grouped shape.
Cache is bypassed in two cases:
options.noCache === trueoptions.dictionaryis set (custom dictionary always re-runs)
Custom dictionaries disable the cache because the cache key only hashes the image, not the decoding alphabet - re-using a cached entry decoded with a different dict would return wrong text.
When benchmarking or iterating on detection/recognition options, set noCache: true or you'll see suspiciously fast "second runs" that are actually just cache hits.
Recognition strategies - the speed/accuracy lever
Three strategies control how detected boxes are batched into recognition inferences:
| Strategy | Inferences | Best for | |
|---|---|---|---|
per-box | One per box (most) | Isolated regions with no line structure; recognizes each region alone. | |
per-line | One per visual line (fewer) | Default. Line context fixes short-label misreads; fewer inferences. | |
cross-line | Bin-packed batches (fewest) | Throughput-sensitive workloads with dense text. Slight accuracy hit possible. |
The strategies are within ~10% on speed; per-line is the default because merged line context reads short low-contrast labels more reliably than isolated crops and needs fewer inferences. Switch to cross-line for maximum throughput on dense pages, or per-box when regions are truly independent.
Set globally on construction, or override per call:
crossLineWidthFactor only matters for cross-line - it's a width multiplier on the bin-pack target. Larger packs more lines per batch (faster, slight accuracy loss); smaller keeps lines isolated (slower, closer to per-line accuracy). Default 1.0 is a sane starting point.
When the user says "make it faster" without naming a strategy, suggest cross-line for dense pages; per-line is already the default. When they say "make it more accurate", point them at a larger model (V6_SMALL_MODEL, V6_MEDIUM_MODEL, or a _server_ variant - see below) or at detection tuning.
Processing engines - opencv vs canvas-native
processing.engine controls the image-processing backend used for detection preprocessing and recognition crop resizing.
"opencv"(default on Node/Bun): usesppu-ocvwith OpenCV.js. More accurate box geometry, especially around tilted/rotated text. Requires OpenCV.js WASM (loaded viappu-ocv)."canvas-native": pure canvas operations viappu-ocv/canvas. Lighter weight, no OpenCV dependency. Slight accuracy loss on the detection side (~1-2% on receipts).
The /web entry point always uses canvas-native regardless of this flag - OpenCV.js is not in the web bundle. If a user is on the web build and asks for OpenCV-grade accuracy, the answer is "preprocess upstream with ppu-ocv/web" (which does ship OpenCV), then pass the cleaned canvas into recognize().
Default models, the cache, and how to warm it
By default, initialize() fetches three files from huggingface.co/snowfluke/ppu-paddle-ocr-models and caches them under ~/.cache/ppu-paddle-ocr (Node/Bun only):
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskill-ppu-paddle-ocr/SKILL.md
Popularity
- Repository stars
- 113
- Repository forks
- 14
- Repository npm downloads, 7 days
- 3,045
- 7-day trend score
- 42
Stars and forks are for the whole GitHub repository, not this skill alone. Downloads are for the repository's npm package. Refreshed 9 Jun 2026, 5 months ago.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/pt-perkasa-pilar-utama/ppu-paddle-ocr/skill-ppu-paddle-ocr)