All projects06 / 08

fælles.

A shared trip photo and video app with resumable uploads and a map.

retired2026

06retired

  • automated tests146
  • phone families tested2
  • container, one Node + SQLite1

01 / Problem

A group chat squeezes every photo

On a trip to Denmark with two iPhones and two Androids, the group chat compressed everything and videos failed over roaming data. I built one shared album of originals that opens with a link and a four-digit PIN, with no accounts.

fælles. library on a phone: a trip summary, a featured memory film, filter chips and a day section with a row of thumbnails above a bottom navigation bar

02 / How it works

One container, one database file

A Fastify server accepts resumable uploads, queues each file in a durable job queue, and uses exiftool, libvips and ffmpeg to read the capture time and GPS and make thumbnails and previews. Everything sits in one SQLite file and one storage folder. Server-sent events push new items to every phone, and a map groups photos by where they were taken.

fælles. PIN screen: a welcome heading, four code boxes and a numeric keypad

03 / The hard part

Four phones, four ways to fail

iOS can take a long time to copy originals out of the photo library, so each pick keeps its own file input alive until the upload ends. On Android I moved the upload bytes to the browser's download manager so they continue with the screen locked, and copied files into plain blobs because Chrome sent empty bodies for input-backed files. Photos from iOS 26 had no previews until I moved the image to a base that ships a newer HEIC library.

04 / Result

Tested end to end, then retired

146 automated tests cover the server and the upload flow, with end-to-end runs at phone and desktop sizes. Capture times are tested against synthetic files for each phone family, PIN attempts are throttled and uploads are refused before the disk fills. The trip ended and I retired the app and archived the repository.

fælles. 'Who's this?' screen: four circular avatars with placeholder names to choose from

How it works

From a phone to a shared album

One container and one database file. Originals go in, and every phone in the group sees them arrive.

The same flow as a list, in order.

  1. Phone: Pick photos and videos. Opened with a link and a four-digit PIN, no accounts. Goes to Resumable upload.
  2. Upload: Resumable upload. Continues after a dropped connection. Goes to Media processing.
  3. Process: Media processing. Queued; reads capture time and GPS, makes previews. Goes to SQLite file + storage folder.
  4. Store: SQLite file + storage folder. Originals untouched. Goes to Live updates, Map, Library.
  5. Show: Live updates. Server-sent events to every phone. End of the path.
  6. Show: Map. Photos grouped by where they were taken. End of the path.
  7. Show: Library. By day, full-size originals. End of the path.

Stack, by layer

Backend
Node 22Fastifytus resumable uploadsSQLite (WAL)
Media
exiftoollibvipsffmpeg
Frontend
React 19ViteZustandMapLibre
Quality
VitestPlaywright

Live from the fælles. repoOffline, last known values

Commits
n/a
Last commit
n/a
Service
Retired