Back to portfolio

Shelfie

1 / 2

The project

Leiden University Libraries (UBL) checks its shelves once a year to find books that are missing or in the wrong place. For each shelf, a librarian prints the expected book list and compares it by hand against what’s actually there. With 40,000 to 150,000 books per location, this takes a team of about six librarians roughly six weeks, and involves a lot of bending, lifting, and repetitive checking. Misplaced books stay “available” in the catalogue while nobody can actually find them, which creates extra work and frustrated visitors.

Shelfie replaces the printed lists with a mobile app. A librarian photographs a shelf, and a fine-tuned vision model detects each individual book spine and reads its call number through on-device OCR. Our own parsing and ordering engine, built around Library of Congress and Mathematical Subject Classification codes, then works out which books are missing or misplaced by comparing what was scanned to what should be there, without needing a rigid shelf-by-shelf mapping (shelves are reorganised constantly, so we designed around the first and last book on a shelf instead). Librarians see the results immediately, including a cropped image of every detected spine, and can correct anything the model got wrong on the spot. A web dashboard gives an overview of ongoing verification sessions and flagged anomalies.

For UBL, this turns a six-week, physically demanding manual process into something librarians can do shelf by shelf with a phone, while still keeping a human in the loop to catch and fix mistakes.

Sorting books one spine at a time.

The customer

Our client was Leiden University Libraries (UBL), and our main point of contact was Jaap van der Plas. UBL runs multiple library locations across Leiden and The Hague, including one of the largest underground book archives in the world.

Jaap had no strict technical requirements and was happy to let us propose the approach, but he was very involved in shaping the project’s direction. A tour of the Science Library early on turned out to be one of the most useful meetings of the whole project: it’s where we learned there’s no fixed mapping between books and shelves, which shaped how the entire ordering engine works. Later in the project, an in-person visit to the UB with Jaap, Annika, and Vincent gave us clearer scope (focusing on the math library) and clarified exactly how the data should be structured. We settled into a rhythm of biweekly email updates and monthly in-person meetings, which kept things predictable and meant we were rarely surprised by new requests.

The team

The Librarians consisted of six people: Max Harrell, Benas Kliostoraitis, Bart Wijnands, Matas Kasiukevicius, Thom Ticheler, and Laura Faas. Laura was Scrum Master, running sprint ceremonies and handling stakeholder communication, and Max was Product Owner, managing the backlog and client contact. Work was otherwise split roughly by component: vision system, parsing engine, mobile app, web app, and backend, tracked across 8 sprints on a GitHub Project board with 33 backlog items.

Our biggest struggle was integration. For two sprints in a row, we had a working vision pipeline, a working parser, and a working app UI, but no way to actually connect them, and at one point we went into our second-to-last sprint with no runnable application at all. We turned it around by isolating all the backend and client communication behind a single API layer instead of wiring things together directly, which finally let every piece talk to each other cleanly. Going from “nothing runs” to a fully working end-to-end system, on real Android phones, with a full week to spare before the deadline, is genuinely the thing we’re proudest of.

The technologies

  • Kotlin - mobile application, built with Compose Multiplatform
  • TypeScript (React) - web dashboard for tracking sessions and anomalies
  • Python - vision pipeline, call-number parsing/ordering engine, and backend API
  • FastAPI - API layer connecting the mobile and web clients to the database
  • YOLO11-seg - fine-tuned model for detecting individual book spines, running on-device
  • PaddleOCR / Paddle Lite - reads call numbers off detected book spines, also on-device
  • Supabase (PostgreSQL) - database hosting, authentication, and role management
  • GitHub Actions - CI (linting, tests, Android build) and CD (publishing an Android build)
  • Ruff, pytest - Python linting and automated testing (also used in CI)