πΈ A digital disposable camera for events. Guests scan a QR code β no app to install β snap a limited roll, and the whole gallery reappears when the event ends. Free for small events, open-source, and self-hostable.
π Hosted version: snapdini.com Β· π How to use it: docs/GUIDE.md
- A limited roll per guest. The scarcity is the feature β a fixed number of shots makes people think before they press the shutter, which is the whole reason disposable cameras are back.
- A real camera in the browser. Tap-to-focus, flash, lens picker, photo shapes, and optional video clips. Photos are saved on the device first and uploaded one at a time, so a bad venue wifi queues rather than loses β and a queue still draining after the event has ended is still accepted, because a photo belongs to the event it was taken at. A phone turned sideways with rotation lock on writes the picture on its side, so the shutter straightens it as it takes it; clips are turned server-side without re-encoding them, and anything older can be rotated by hand.
- A reveal you choose. Instantly as photos land, when the event ends (after a delay or at an exact moment you pick), or manually when you press the button. Optional moderation holds every photo until you approve it.
- One shared gallery, plus share links scoped to the whole gallery, your favourites, or photos you hand-pick. Downloads as a zip, or straight into the phone's own photo library β and you can take just the highlights, just the ones guests hearted, or both.
- Guests heart each other's photos. A tap on anything they can see, counted live, and those counts become a download of their own. On by default; a host can switch it off per event.
- A guest list that tells you what arrived. Add guests by hand or import a spreadsheet, send branded invites, and see what happened to each one β delivered, bounced, reported as spam β in the mail server's own words. (Per-recipient delivery state needs Mailgun and a webhook key; without them an invite honestly reads "sent, delivery unknown".) Guests can ask for their own photos and get them when the gallery opens, and a one-click unsubscribe plus a suppression list keep the sending domain out of trouble.
- Print for the tables. A poster designer β A6 through A2, rendered at whatever the paper it is going on can actually resolve β that also prints trick cards: a short list of shots for guests to hunt down and tick off in the camera as they take them.
- A slideshow of the night, rendered server-side with your own music.
- Themes, a custom
/e/<name>link, co-hosts, and a retention window after which the photos and guest data are deleted.
Every page and control is written up in the page & feature guide.
No build step. Pulls the prebuilt images and runs them with Postgres + nginx.
Before you start you need two things:
- Docker Engine with the Compose v2 plugin (
docker compose versionshould answer). Nothing else β no Node, no Postgres, no ffmpeg on the host; they are all inside the images. - A public HTTPS URL pointing at the box, terminated by a reverse proxy you run (Traefik, Caddy,
nginx, a tunnel β your choice). This is not optional polish: browsers only hand a page the camera
and the microphone on a secure origin, so over plain
http://on anything butlocalhostthe guest camera simply never opens.
mkdir snapdini && cd snapdini
curl -O https://raw.githubusercontent.com/paytah232/snapdini/main/app/docker-compose.yml
mkdir nginx && curl -o nginx/default.conf https://raw.githubusercontent.com/paytah232/snapdini/main/app/nginx/default.conf
curl -o .env https://raw.githubusercontent.com/paytah232/snapdini/main/app/.env.example
# the upgrade helper β it checks the traps that `pull && up -d` alone will not
curl -o upgrade.sh https://raw.githubusercontent.com/paytah232/snapdini/main/app/scripts/upgrade.sh && chmod +x upgrade.sh
# edit .env β at minimum set BASE_URL (your public https URL) + POSTGRES_PASSWORD
docker compose pull
docker compose up -dFour containers come up β app (the API), web (the SvelteKit front end), db (Postgres) and
nginx (the one that publishes the port). Snapdini answers on HTTP_PORT (default 8080); point
your TLS / reverse proxy at that, and make sure the address it serves is exactly what you put in
BASE_URL.
- Fully free, no limits, when self-hosted. Billing, email, the admin panel and analytics stay
off until you add their keys (
STRIPE_*,MAILGUN_*,ADMIN_*,GTAG_ID/MSUET_IDin.envβ all optional; no tag id β zero third-party tracking)..env.exampledocuments every setting. - Set
SEO_INDEXABLE=1on exactly one deployment β the public one named inBASE_URL. Unset meansnoindex, which is the right default for a staging copy and the reason a preview host cannot outrank your real one. - Pin a version with
IMAGE_TAGin.env(e.g.IMAGE_TAG=1.5.0; defaultlatest). Point at your own registry withIMAGE_PREFIX. - Upgrades: run
./upgrade.sh [version], or see UPGRADING.md. DB migrations apply on boot, butpull && up -dalone is not always enough β a release can add settings that must go in both.envanddocker-compose.yml(compose passes env explicitly, so a variable missing from it is silently ignored), and it never updates yourdocker-compose.ymlornginx/default.conf.
Guests can upload a selfie and pull out just the photos they appear in. It is off in the hosted service and shipped off by default, because face templates are biometric data and the compliance picture varies by jurisdiction β but if you run your own instance for your own events, it is a genuinely good feature and it is fully built.
Point MACHINE_LEARNING_URL at an Immich machine-learning
container. It talks over plain HTTP with image bytes in the request, so the ML container needs no
access to your photo storage β no shared volume, no bind mount. It can live on the same box or a
different one.
machine-learning: # add to your docker-compose.yml
image: ghcr.io/immich-app/immich-machine-learning:release
volumes: [ model-cache:/cache ] # models only β never your photos
restart: unless-stoppedMACHINE_LEARNING_URL=http://machine-learning:3003With it unset the feature does not exist: no host toggle, no guest control, no privacy-policy section, and the endpoints return 503. With it set, it is still off per event until a host turns it on, and then off per guest until that guest ticks an unticked consent box. A guest's selfie is deleted in the same request that creates their template; every other face in a photo is compared and discarded in memory. "Stop and delete" removes the template and every match it produced, and keeps working even if you later switch the ML container off.
Think carefully before turning this on for anyone other than yourself. Face templates are biometric data in most jurisdictions. Two things in particular need an answer you are comfortable with: you necessarily process the faces of guests who never consented in order to find the one who did, and events often include minors. Take your own advice on it β this project cannot give you legal advice for your country.
Express + TypeScript + Postgres (Drizzle ORM) for the API, SvelteKit for the web app, behind nginx β all in Docker. Building from source, developing, or publishing your own images? See the development & maintainer guide.
| docs/GUIDE.md | Every page and every control, grouped by who uses it. Start here to learn the app. |
| UPGRADING.md | The traps pull && up -d does not close, plus per-release notes. |
| docs/DEVELOPMENT.md | Architecture, the dev stack, the gotchas worth knowing before you change something, and how releases are cut. |
| docs/DELIVERY-TRACKING.md | Turning on Mailgun delivery tracking: the webhook signing key, which events to subscribe, and how to tell it is working. |
| TESTING.md | The manual QA checklist, and what the automated suites already cover. |
Snapdini is free and self-hostable. If it's useful to you and you'd like to support the work, you can buy me a coffee β β much appreciated, never required.
Released under the GNU AGPL-3.0. You're free to self-host, modify and redistribute it; if you run a modified version as a network service, you must make your source available under the same licence. Β© 2026 paytah232.
The poster designer bundles five typefaces β Playfair Display, Cormorant Garamond, Great Vibes,
Sacramento and Jost β each under the SIL Open Font License 1.1, which
covers redistributing them with the application. Per-family copyright notices are in
web/static/fonts/README.md. The OFL is separate from the AGPL and
applies only to those files.




