A tiny, self-contained party trick you can run on your own machine and share with friends over a link. Open the page and it:
- Asks for camera access (a real browser permission prompt).
- Shows a live "AI scanner" overlay — "Face not found" until a face is in frame, then glowing corner brackets lock onto it like a sci-fi HUD.
- Auto-snaps a photo once a face holds steady for ~1.2s and saves it straight to a local folder in this project — no database, nothing uploaded to any third party.
- Shows a loading state, then asks for location access (a second, separate real browser permission). If allowed, the visitor's exact Google Maps link is appended to a local text file.
Everything happens because the visitor explicitly approves two standard browser permission dialogs — there is no hidden capture.
This is meant for consenting friends who know they're about to get "scanned" — e.g. sharing the link and telling them what it does, or using it as a party photo-booth gag. Please don't:
- Send the link to people without their knowledge of what it does.
- Use the location data for anything beyond the joke.
- Deploy this against strangers or in any context without clear consent.
The browser will always show its own native "Allow camera / Allow location" prompt — this project cannot and does not bypass that.
- Live in-browser face detection (face-api.js, running entirely client-side — no frames are sent anywhere until the final snapshot).
- Animated scanning overlay with mirrored video, scanline, and corner-bracket face tracking.
- Auto-capture once a face is held in frame, with a camera-flash effect.
- Photos saved directly to disk as
.jpgfiles — no database, no cloud storage, no third-party API calls. - Optional geolocation capture after the photo, written as a plain-text Google Maps link.
- Fully self-hosted:
face-api.jsand its model weights are bundled inpublic/vendorandpublic/models, so the page doesn't depend on any external CDN once loaded.
- Node.js + Express — static file serving and two small JSON API routes.
- face-api.js (TensorFlow.js) — client-side face detection.
- Plain HTML/CSS/JS on the front end — no build step, no framework.
- Node.js 18+ (tested on Node 22).
- A webcam and, for the location feature, a device that can report its position.
- HTTPS for the deployed URL (see Location caveat below) — a dev tunnel URL satisfies this automatically.
git clone <this-repo-url>
cd image_capture
npm install
npm startThe server starts at http://localhost:3000. Open it in a browser to try it yourself before sharing it with anyone.
To let friends open it from their own devices, expose your local port
with a tunnel. Using the devtunnel CLI:
devtunnel host -p 3000 --allow-anonymousThis prints a public https://...devtunnels.ms URL — send that link to
friends. Camera and location prompts only work over HTTPS (or
localhost), so a tunnel URL like this is required for remote visitors.
Alternatively, in VS Code: run the server, open the Ports tab, forward
port 3000, set its visibility to Public, and share the generated URL.
| What | Where | Format |
|---|---|---|
| Photos | captures/*.jpg |
One file per capture, named capture-<ISO timestamp>-<random suffix>.jpg |
| Locations | captures/locations.txt |
One appended line per allowed location: timestamp, matching photo filename, and an exact https://www.google.com/maps?q=<lat>,<lng> link |
Nothing is written to a database, and nothing leaves your own machine
except what you choose to do with these files afterwards. The captures/
folder is gitignored (except a .gitkeep placeholder) so real captures
never accidentally get committed or pushed to GitHub.
If a visitor declines the location prompt (or it times out), the photo is still kept — the app doesn't retry or nag for location.
.
├── server.js # Express server: static hosting + capture/location APIs
├── package.json
├── public/
│ ├── index.html # Page markup
│ ├── css/style.css # Scanner UI styling & animations
│ ├── js/app.js # Camera, face detection, capture & geolocation flow
│ ├── vendor/face-api.min.js # Bundled face-api.js library
│ └── models/ # Bundled tiny face detector model weights
└── captures/ # Saved photos + locations.txt (gitignored)
| Route | Method | Body | Behavior |
|---|---|---|---|
/api/capture |
POST | { image: "data:image/jpeg;base64,..." } |
Decodes and writes the photo to captures/, returns { ok, filename } |
/api/location |
POST | { filename, lat, lng, accuracy } |
Appends a line with the Google Maps link to captures/locations.txt, returns { ok, mapsLink } |
PORTenvironment variable — change the port the server listens on (defaults to3000).FACE_HOLD_MS/RETRY_AFTER_MSin public/js/app.js — how long a face must stay in frame before auto-capturing, and the cooldown before another capture is allowed.
- "Camera access was denied" — the visitor blocked the permission prompt; they'll need to allow it in their browser's site settings and reload.
- Location prompt never appears / silently fails — geolocation only
works over HTTPS or
localhost. Plainhttp://LAN addresses won't trigger it. - No face detected even when visible — check lighting; the tiny face detector model works best with a clearly lit, front-facing face.
MIT — do whatever you like with this, just use it responsibly.