06 / In development · Private web app

Zuzy’s
World

The camera’s flaws are the material, not the bug list.

My three-year-old has her own camera, and it produces close-ups, motion blur, ceilings, fingers over the lens, and toys pressed against the glass. Zuzy’s World doesn’t correct for any of that — it is built around it. Every photo becomes a small interactive toy: swipe to browse, tap to hear a playful spoken caption, guess what the blurry red thing was, and hear a tiny story stitched from three of her pictures. She can’t read a word, so nothing asks her to. Press and hold gates the grown-up controls; everything else is a swipe or a tap.

A

A prompt that refuses to describe a photograph

The captions are the actual product, and the tone rules behind them are long and absolute on purpose: never mention blur or framing, a finger over the lens is a finger monster, a dark frame was taken inside a cave, nothing frightening, five to twelve words. A regex guard rejects and retries any caption that slips into describing the photograph instead of the world inside it.

No caption is ever asked to identify who is in the picture. A cast of people, pets, and toys is defined ahead of time and tagged during review, so the model is told who is present and writes accordingly — it never guesses from the image. That is a hard rule, not a convenience, and it is the reason there is no facial recognition anywhere in this project. A one-line note added during review becomes material the model writes from rather than a line it recites: measured on one real photo, that is the difference between “A big person leans down close and smiles hello!” and “Dada bends way down close and smiles at you!”

The system prompt is byte-identical across every call and carries a cache breakpoint, so after the first photo of a run its input cost drops by roughly 90%.

B

A camera with no EXIF still gets exact times

This camera writes no EXIF at all — 0%, no date, no make, no model. Ordering and grouping still work because timestamps resolve through four tiers, and parent mode shows which one each photo actually used.

FOUR WAYS TO DATE A PHOTO. ONLY ONE OF THEM WORKS HERE.

  1. EXIFDateTimeOriginal. Preferred, and absent on every single file.
  2. CARD DATEFilesystem birth time — exact, free, and what actually gets used. Trusted only when clearly older than the modified time, since a match there just means it is the copy time.
  3. BURNED-IN STAMPThe yellow date printed into the corner, read off the caption-size image, with one cheap magnified-crop look (~$0.005) if that comes back empty.
  4. MODIFIED TIMERecorded but excluded from grouping: a bulk card copy stamps hundreds of photos within seconds and would fuse the library into one fake adventure.

Her camera’s clock was never set either — but measured across the real library, the burned-in stamp and the card date agree to within two seconds over three months, a constant four-hour offset with no drift, even across a daylight-saving boundary. So the order of the photos and the gaps between them are exact; only the calendar date is fiction, and anywhere a date is shown to a human it is labelled “camera clock” rather than presented as fact.

She also shoots in long runs — 330 of 381 consecutive pairs land within 90 seconds of each other — so a naive gap rule produced a single 42-photo “adventure.” Runs longer than eight photos are split at their largest internal pause instead, which puts the break where she actually stopped.

C

The most loaded lines had the worst voice

Captions, mystery questions, and adventure narrations are rendered to audio at import time with a per-register performance direction rather than one flat voice setting. The small set of interactive lines that aren’t tied to a specific photo — the greeting, “I love it!”, the mystery celebration, the silly sound words — used to be assembled live in the browser with speech synthesis, which was exactly backwards: the most emotionally loaded moments were the ones getting the robot.

They are pre-rendered once now and served from storage. Editing a phrase changes its hash, so the next render pass re-records only what changed. Browser speech synthesis still exists, but only as the fallback for anything not yet rendered — which is what keeps the app usable before that pass has ever run.

iOS Safari also blocks audio playback outside a user gesture, per element, so the app keeps one shared audio element for the whole session and unlocks it with a silent buffer inside the very first tap. Otherwise narration goes quiet partway through a visit, which to a three-year-old means the app is broken.

D

Nothing is deleted, and every step resumes

A photo rejected during review stays in the database rather than being removed, so a later re-import can’t resurrect something already trashed. Every import step is resumable and idempotent — a run that dies halfway through, from a rate limit or a closed laptop, costs nothing to restart, because photos already imported are skipped by content hash.

The book itself is meant to be openable by family, not indexed by anyone: robots.txt disallows everything and every response carries noindex, nofollow, noarchive and a no-referrer policy. The grown-up side — hide and show photos, edit captions, the review queue — sits behind a shared password checked at the edge, and a missing password fails closed rather than quietly opening the door.

WHAT IT TAUGHT ME

The interface for someone who can’t read is really just “does the button make a sound, and does the sound make sense.” The whole product lives or dies on whether a three-year-old works out the swipe and the speaker icon without being shown. Hand it to her without explaining it; if she doesn’t, the buttons are too small or the audio didn’t fire.