John Aleman
All projects

Bookshelf Builder

A personal library app that reads the spines in a photo of your bookshelf and turns them into a catalog you can organize.

In development
The problem
Cataloguing the books you already own means entering every title, one at a time.
Who it’s for
People with shelves of physical books who want to know what they own, organize it and stop buying duplicates.
What I built
A photo-to-library flow: AI reads the spines, Google Books supplies the match, and a review step puts a person in charge of anything uncertain.
Key decision
The model is told to lower its confidence rather than guess. A low-confidence read goes to review even when the book match looks strong.

The problem

Back to contents

A home library is a real collection with no inventory. Typing titles, or scanning one barcode at a time, is too slow for a few hundred books.

A photo of a shelf solves the effort problem and creates a trust problem. Spines are narrow, faded and partly hidden, and an app that confidently adds the wrong book is worse than one that adds nothing. The experience has to make the AI’s uncertainty visible and cheap to correct.

Product experience

What the product does for the people using it, and why it works that way.

Back to contents
  1. Three honest states instead of one confident list

    Every detected book is labelled Matched, Needs review or Not found, along with how confidently its spine was read. Only matched books start selected, so adding the batch is one tap and nothing doubtful slips in by default.

  2. Recovery is part of the flow, not an error

    A row that needs review offers the closest alternative editions, a Search again action that uses the same matcher as the scan, and a way to edit the title or author. A wrong read costs a correction, not a restart.

  3. No duplicates, even when the spine and the catalog disagree

    Books already in the library are flagged “Already in your library” and cannot be added again. The comparison uses ISBN, or a normalized title plus the first author, so “J. R. R. Tolkien” and “J.R.R. Tolkien” count as the same person.

  4. Photos straight from the phone

    Up to five photos per scan, so a long wall can be covered. HEIC photos from an iPhone are decoded natively in Safari, and elsewhere by a converter that loads only when a HEIC file is picked. Images are downscaled in the browser before upload.

Engineering decisions

Each one as the decision, the reasoning, the trade-off accepted and the outcome.

Back to contents

Matching is deterministic code, not more AI

Decision
The vision model only reads spines. Matching against Google Books is a pure, unit-tested function that scores candidates by edit distance and word overlap, with explicit thresholds for matched (0.8) and needs review (0.45).
Why
Ranking rules that live in code can be tested, explained and tuned. Google’s own result order is not trusted: a test pins that behaviour.
Trade-off
Fixed thresholds will misjudge some edge cases that a model might have handled. Fallback queries and the review step absorb those.
Outcome
The same matcher serves the scan and Search again, so a manual retry behaves exactly like the original read.

Uncertainty flows from the model to the interface

Decision
The prompt asks the model to lower its confidence for uncertain or partly hidden text instead of guessing. Any detection below 0.5 confidence is marked Needs review, whatever the match score.
Why
A strong catalog match for a misread title is still the wrong book. The person holding the book is the only reliable judge.
Trade-off
More rows need a glance. That costs a few seconds, and silently adding wrong books would cost trust.
Outcome
The review screen shows how confidently each spine was read, so the AI’s limits are visible rather than hidden.

Protect the budget before the feature ships

Decision
Photos are downscaled to 2048 px on the longest edge and compressed in the browser. The server caps the request body, limits each user to 10 scans per 10 minutes, and caps concurrent Google Books lookups.
Why
Every scan is a paid vision call. One enthusiastic user, or one bug in a retry loop, should not be able to run up the bill.
Trade-off
The limiter is in process memory, so on serverless each instance counts separately. The code says so, and treats the limit as a guard rather than a quota.
Outcome
Upload size and cost per scan are bounded, and scanning and Google Books failures are reported to Sentry.

An assistant with no server and no API key

Decision
The Bookworm assistant understands requests like “add Dune and tag it SciFi” with Chrome’s built-in on-device model. The same library tools are also exposed to browser AI agents through WebMCP.
Why
A natural-language shortcut should not mean sending someone’s library to another server, or adding a key to manage.
Trade-off
It is only available in desktop Chrome with the on-device model, so the button is hidden everywhere else.
Outcome
The assistant and the agent tools call the normal GraphQL API, with the same sign-in and the same rules as the interface.

Outcomes and lessons

Back to contents

Shipped

  • Built and running in the repository: shelf photo scanning, Google Books matching, the review step, the library with search, tags and shelves, the Bookworm assistant, WebMCP tools and Sentry reporting.

Validated

  • Unit tests cover matching, deduplication, image preparation, validation and the scan service. Playwright covers scanning, the assistant, WebMCP and book detail, with an axe-core WCAG A/AA pass.

Known limits and next steps

  • Public launch.
  • Reading progress views. The schema already stores a reading status per book.
  • Product screenshots and a short scan walkthrough for this page.
  • Next.js
  • OpenAI vision
  • Google Books API
  • GraphQL Yoga
  • Drizzle ORM