← Rayna Liu

AI Moodboard

A weekly scrapbook that tags its own references.

Stack
React, Express, Gemini, Drizzle, Postgres / PGlite
Live
moodboard demo
Source
github.com/rainbow-rayna/ai-moodboard

Pin a screenshot to a day of the week and it comes back as a Polaroid, tagged with five to ten design terms Gemini reads off the image — the vocabulary you'd actually search by, like Brutalist, Modular Grid, or Muted Earth Tones. Everything is stored per ISO week, so paging back to last week finds it exactly as you left it.

Screenshot of a populated week goes here — ideally one where the tags are legible, since the tagging is the whole point.

Decisions worth explaining

The model version is pinned, deliberately

It would have been easier to point at a -latest alias. I pinned an exact model instead, because the failure mode of a floating alias is genuinely nasty here: when it gets retired or overloaded, every upload silently falls back to placeholder tags. The app keeps working, nothing errors, and the only symptom is that the output quietly gets worse. A pinned version fails loudly instead.

The API key cannot reach the browser

Vite is never handed the .env file, and nothing in src/ talks to Google directly. The browser uploads bytes to this app's own Express route, and that route is what calls Gemini. There's no path by which the key ends up in a client bundle — not by convention, but structurally.

Zero-setup Postgres

Leave DATABASE_URL empty and the server runs PGlite — real Postgres compiled to WebAssembly, in-process, writing to a local directory. Same SQL, same Drizzle migrations, no install step. Set the variable and it switches to a real server with the same migration files. Cloning the repo and running it takes one command, which is the difference between a project people try and one they don't.

Every environment variable is optional. With none set, the app still runs end to end — uploads work, and images get clearly-labelled fallback terms instead of analysed ones. That's what makes the public demo safe to leave running.

What I'd change

Add why you built this — what problem with your own reference-collecting it was meant to solve, and whether it actually did.