A mobile-first PWA that helps librarians verify Library of Congress call numbers are in correct shelf order. Point your phone camera at a shelf of books, tap Scan, and the app flags any books that are out of sequence.
- Camera capture — uses the rear-facing camera via
getUserMedia - OCR — Tesseract.js (v5) extracts text from spine label images
- LC call number parsing — handles class letters, class numbers (integer + decimal), two cutter numbers (sorted as decimals per LC rules), and year
- Shelf order checking — highlights out-of-order books in red and tells you where they should go
- Manual editing — correct OCR mistakes inline, or add call numbers the scanner missed
- PWA — installable on mobile, service worker for offline static asset caching
Call numbers are compared in this priority:
- Class letters — alphabetically (
A < AC < B < BF < U < UA) - Class number — as integer (
5 < 35 < 100 < 1000) - Class decimal — as decimal (
.5 < .7 < .75) - First cutter digits — as decimal (
.C65 < .C946, i.e..65 < .946) - Second cutter digits — same decimal rules
- Year — chronologically
- Frontend: Vanilla HTML/CSS/JS, no framework
- OCR: Tesseract.js v5 via CDN
- Server: Express.js (serves static files; required for Railway)
├── server.js Express server, reads PORT from env
├── public/
│ ├── index.html Single-page app (camera / processing / results screens)
│ ├── style.css Dark mobile-first styles
│ ├── app.js Camera, OCR, LC parsing, sorting, UI logic
│ ├── manifest.json PWA manifest
│ └── sw.js Service worker
└── package.json "start": "node server.js"
npm install
npm start
# Open http://localhost:3000Camera access requires HTTPS in most browsers — use a tunneling tool like ngrok for local mobile testing, or deploy and test from the live URL.
Push to GitHub and connect the repo in Railway, or:
railway login
railway init
railway up
railway domainThe app reads process.env.PORT automatically.
- OCR accuracy — Tesseract.js struggles with book spine labels due to varied fonts, lighting, and narrow label widths. Preprocessing the captured image (grayscale conversion, contrast enhancement, adaptive thresholding) before passing it to Tesseract significantly improves results.
- Label segmentation — the app uses blank-line gaps in the OCR output to separate individual labels; dense or noisy OCR output may merge multiple labels together.
- Partial call numbers — some labels omit cutters or years; the parser handles these but flags them as uncertain.