Point a camera at a handwritten lab notebook page and get a dashboard that checks itself. Rows stream in as the model reads them, deterministic math verifies every cell, and you end up reviewing the two fields that deserve it instead of all forty.
Try it: labshot.vercel.app · Source: github.com/tornike14/labshot
- Digitize a prep sheet from a phone photo in well under a minute, watching the table build row by row
- Drop a stack of pages, or a folder of photos, and let them run through a queue two at a time, with each finished page landing in the sidebar as its own session
- Catch transcription errors and on-page arithmetic slips: every row recomputes mass = mmol × MW and flags cells where the written and computed values disagree
- Catch wrong molecular weights: each written MW is compared against PubChem, with hydrate corrections applied the way a chemist would apply them
- Ask for a second read: an independent pass re-reads the page from scratch and reports only where the two readings disagree, like a witness countersigning an entry
- See the 3D structure of each recognized reagent, resolved by name from PubChem
- Export the verified table as JSON or CSV with every check result attached, ready for a spreadsheet, an ELN, or a training dataset
- Revisit past extractions from the session sidebar, stored in your browser only
- Bring your own Anthropic key and the hourly limit and batch cap both go away. The key stays in your browser and goes straight to Anthropic, never through labshot's servers, which you can confirm in your browser's network tab
- Run the whole thing against a local vision model when pages must not leave the building
Warning
The hosted demo and the default configuration send every photo you drop to Anthropic's API. That is a third party: images and extracted text transit their servers and are retained under their data policy, typically for a period of days to weeks. Do not upload pages containing proprietary compounds, unpublished results, or anything your lab would not email outside the building.
For confidential work, run the app yourself and set EXTRACTION_BASE_URL to a local OpenAI-compatible server (Ollama, vLLM, LM Studio). The same code then runs a local vision model and no page ever leaves your network. Everything else is already local: verification is plain TypeScript in the browser, PubChem receives compound names only, and saved sessions live in your browser's localStorage, never on a server. The app itself stores nothing.
Extraction is table stakes. The real problem is trust: nobody wants to eyeball 40 transcribed cells against a photo to find the one wrong digit. A prep sheet's columns are arithmetically related, though. Mass in grams should equal mmol times molecular weight, and mmol divided by equivalents should be roughly constant down the table. So the app recomputes every row and flags only the cells where the written value and the computed value disagree, typically leaving you with one or two cells to check instead of the whole page. The extraction prompt transcribes exactly what is written and never corrects it, which is what makes the check meaningful.
Three independent check classes cover each other's blind spots:
- Arithmetic. Catches transcription errors in numbers. Runs client side, instantly, per row.
- Database. Each written MW is compared against PubChem. Hydrates are handled properly: La(NO3)3·6H2O is looked up as anhydrous lanthanum nitrate and corrected by six waters, so the database value lands on the written 433.01 instead of flagging a false mismatch. This catches cases where the scientist used a wrong MW on the page.
- Second read. A separate model pass re-reads the image without trusting the first transcription and reports per-field disagreements. This catches misread names, missed rows, and mangled procedure steps, which no arithmetic can see.
A disagreement is never auto-corrected. The tool flags, the human decides, and the original photo stays on screen for exactly that reason.
git clone https://github.com/tornike14/labshot
cd labshot
npm install
cp .env.example .env.local # add your ANTHROPIC_API_KEY
npm run dev
Drop one or more photos of prep sheets on the page, or pick a folder of them, then start the run. JPEG, PNG or WebP, which is what the vision API accepts. A photo too large to send is resized in the browser first rather than turned away, so a picture straight off a phone is fine. HEIC is not supported and is reported as such, so export or convert those first. One batch is capped at 3 pages by default, which matches the hosted demo. Set NEXT_PUBLIC_BATCH_LIMIT=0 in .env.local for no cap once you are running on your own key. Pages extract two at a time either way.
One streaming call feeds everything. The route handler at app/api/extract/route.ts sends the image to the model with a Zod schema, and partial JSON streams back to the client. The reagent table mounts rows as they parse, the verification pass runs client-side per completed row, and the molecule viewer starts resolving reagent names against PubChem the moment the first name lands. Nothing waits for anything else.
A batch is the same call per page, run through a small client-side queue at two pages at a time so the hourly limit is not spent in one burst. Each page saves itself as a session the moment it finishes, so the sidebar fills up during the run and can be sorted to float flagged pages to the top.
The provider sits behind one env var. By default extraction runs on Anthropic's hosted API; set EXTRACTION_BASE_URL to any OpenAI-compatible server and the same code runs a local vision model instead.
- No bounding boxes. Click-a-cell-to-see-the-source-crop would be the right UX, but vision models don't return reliable pixel coordinates yet. The original photo stays visible under the dashboard instead.
- Hydrates and organometallics sometimes miss on PubChem. The app searches by common name, and names like Zr(acac)4 don't always resolve. Chips grey out when that happens.
- Bad handwriting produces bad transcriptions. The model flags digits it isn't sure about, and the second read catches more, but a confidently wrong reading in both passes will only get caught if the arithmetic breaks.
- The per-IP rate limit is in-memory, so it resets on redeploy. Good enough for a demo, not for production.
- One page type. Reagent tables and numbered procedures parse well; condition grids, plots, and spectra are not extracted yet.
MIT. Use it, fork it, run it inside your lab.