LensLab
Local image intelligence

Ask questions of your own image folders.

Label anything with words you invent. Search your library in plain English. Find near-duplicates. LensLab runs a vision model on your own machine — no API key, no upload, no per-image cost.

Runs on your machine · your pictures are never uploaded · no API key

A real 57-second capture of the running application — not a mockup.
The problem

Your folders know more than your filenames do.

A decade of photographs, screenshots, scans and exports sits on a disk somewhere, and the only ways in are the filename, the date, and whatever folder structure you had the discipline to maintain.

Tagging never happens

Manual tagging is a chore nobody finishes. The library grows faster than the labels do.

Fixed classifiers don't fit

Off-the-shelf models recognise a fixed list of classes. Your question is rarely on it.

Cloud vision has a bill and a boundary

Per-image pricing, an upload, and a copy of your library on somebody else's disk.

The product

One model, four questions, zero uploads.

LensLab runs CLIP locally. CLIP places pictures and short phrases into the same mathematical space, so a phrase you invent on the spot can rank a photograph you took years ago. Everything below is that one comparison, asked differently.

What is this?

Drop an image in. It scores against a built-in vocabulary across four facets — kind, subject, setting, look — and says plainly when it isn't sure.

Does it match my words?

Type any labels at all. No training, no fixed class list, confidence bars back in milliseconds.

Where is the one I mean?

Search the whole library in plain English, ranked by meaning rather than by filename.

What have I got twice?

Exact byte-identical matches and perceptual near-duplicates, reported separately rather than lumped together.

10.7/secimages ingested, one CPU core
114 msaverage search latency
48automated tests passing
0bytes leaving the machine

Throughput and latency were measured on the build machine at build time. The test count was re-verified on 2026-09-04: 48 passed, 1 skipped, 0 failed.

Architecture

Small enough to hold in your head.

A Python web application, a local model, and one SQLite file. The vector for each image is computed once at ingest and stored in its row — which is why describing a library tile costs a row read and four dot products rather than another look at the pixels.

YOUR BROWSER · 127.0.0.1 Pages and live fragments Server-rendered HTML swapped in place — no JavaScript application, no external requests APPLICATION Ingestwalk, decode, thumbnail,skip with a reason EncoderCLIP — images and textinto one shared space Identifybuilt-in vocabulary,four facets, honest bands Searchexact cosine rankingover every vector ON DISK · NEVER LEAVES SQLite — rows, vectors, thumbnails One writer. The embedding lives in the image row as a BLOB, so there is no index that can drift out of step with the truth. Model weights — hash-pinned Downloaded once, checked against a known SHA-256. A substituted or truncated file is refused, not loaded. Your image library is mounted read-only. LensLab reads your pictures and never writes to them.
Requirements vs. completion

What was asked for, and what actually got built.

Every row states its own evidence. Where something is not built, it says so and says why — a matrix showing everything complete is a matrix nobody believes.

RequirementWhat was built EvidenceStatus
Label an image with words the user invents Zero-shot label studio. Any labels, one per line, no training and no fixed class list. Renders confidence bars for one image, a contact sheet for many.
Presets can be saved and reused.
5 endpoint tests + browser walk Built
Tell me what an image is without me typing anything Identify scores against a built-in vocabulary across four independent facets, with a refinement pass that turns a category into a thing (“fruit” → “a mango”).
Says “No confident match” rather than asserting a wrong answer.
5 tests, both branches Built
Ingest a folder without dying on one bad file Batched folder walk with live progress, resumable, and a skip report naming every rejected file and the reason. Re-ingesting a folder is a no-op. 24/25 on a planted corpus Built
Search the library in plain English Exact cosine ranking across every stored embedding — brute force, which is exact and fast to roughly 100,000 images. 114 ms avg, 205 ms max Built
Find duplicates Byte-identical matches and perceptual near-duplicates, clustered and reported as separate categories rather than merged. both planted pairs found Built
“More like this” from a library image Ranks the library against one of its own tiles using the stored vector, and excludes the source image from its own results. 2 tests Built
Get the data back out Every result table exports as CSV or JSON. 3 tests Built
Nothing leaves the machine No fonts, scripts, styles or analytics load from anywhere; the favicon is inline so even that costs no request. The only outbound call in the product's life is the one-time model download, which is hash-pinned. asserted by test, not promised Built
Safe to point at a real folder Ingest roots are bounded by configuration, paths are resolved before they are trusted, the thumbnail route refuses traversal, and forgetting an image removes the row and never the file. 6 safety tests Built
Authentication Not built. LensLab is a single-user tool bound to the loopback interface. The demo you can try is gated by this site, not by the application. Publishing it means putting an authenticating proxy in front — the compose file says so at the line where a reader would be tempted otherwise. deliberate scope boundary Not built
Text and OCR extraction Reading text inside images needs a second model and a second pipeline. — V2
Libraries beyond ~100k images Brute-force ranking stays exact and quick to roughly 100,000 images. Past that it wants an approximate-nearest-neighbour index, which trades exactness for scale. — V2
Face clustering Deliberately excluded. Grouping people by face creates a biometric compliance surface that this product does not want and does not need. scope decision Out of scope
Roadmap

Where it goes next.

ItemNoteState
Zero-shot label studioType any labels — no training, no fixed class listShipped
Identify without labelsBuilt-in vocabulary, four facets, refinement passShipped
Folder ingest with live progressBatched, resumable, with a skip reportShipped
Semantic library searchExact cosine ranking over every embeddingShipped
Near-duplicate finderPerceptual clusters and exact byte matches, separatelyShipped
Paste-anywhere and batch contact sheetCtrl-V, or drop many at onceShipped
CSV / JSON exportEvery result table is exportableShipped
OCR / text extractionNeeds a second modelPlanned
Index for 1M+ imagesBrute force is exact to ~100kPlanned
Face clusteringDeliberately out — biometric compliance surfaceExcluded
Deployment

Two lanes, one codebase.

The same application runs both ways. The lanes differ only in where the model runs and whether the library is live — flipped with environment variables, never with a code change.

On-premises this demo

Your data and your model stay on your hardware. Egress is zero.

  • Runs under a process manager, restarts on boot
  • Bound to 127.0.0.1 — no public listener
  • Image library mounted read-only
  • Model weights local and hash-pinned

Container portable

The same image, somewhere else. Nothing to install beyond a container runtime.

  • docker compose up — publishes to loopback only by default
  • ./data mounted read-write for the library database
  • Your pictures mounted :ro — structural, not a promise
  • Every knob documented in .env.example

A cloud-hosted twin — the same build pointed at an API-backed model behind a keyless gateway, with a synthetic library instead of live data — is a supported shape but is not deployed today. Only the on-premises lane above is running.

Watch the demo.

LensLab is a local-first tool: the model and your images stay on the machine it runs on, which is the whole point of it. There is no hosted instance — but the walkthrough below is the real application, recorded end to end. Enter your email, we'll show you a one-time PIN, and the demo plays.

Read the documentation

Runs on your hardware · no API key · no per-image cost