Skip to content
All builds

DrydownFragrance Recommender

What it does for you

A fragrance recommender that walks a note graph from your own collection and explains every pick.

Drydown screenshot

Screenshots

Drydown screenshot 1

In more detail

Drydown looks at the fragrances you own and how you rate them, then recommends new ones with a plain reason for each pick. It says which notes pulled a bottle in and which of your favorites it resembles. It also tries hard not to hand you ten copies of the same scent.

Honest limits

The live Claude call, file import through the picker, clipboard copy and the PNG download were not clicked through by hand. The Claude request and parser are tested against a mock only. The phone layout was checked at 512 px, the narrowest headless Chrome would render, not on an actual phone.

How I built it

Claude Code built it on September 23, the big tandem day when Codex was working its own list in parallel. This one did not go smoothly the first time. The first builder died right after writing the core, with no UI and no tests. The API connection kept dropping long responses mid-stream. So a second run picked up the core that survived and finished the job with every write under about 4 KB: twelve small test files, a verify runner, and one small module per UI view.

I directed the spec. Import from Fragrance Vault (project 26) instead of copying it. Use a fictional catalog only, 160 invented bottles. Make every explanation name notes and past favorites. And give the optional Claude mode no way to invent a bottle. I also picked the look: walnut and amber, Young Serif with Hanken Grotesk.

Under the hood, a note graph is weighted by how often notes show up together, a taste model built from ratings, wears and sold bottles seeds personalized PageRank, and the results get re-ranked for variety. Claude Code screenshotted each view and fixed what it saw, like a squashed graph and overlapping labels.

What I learned

  • Personalized PageRank needs a baseline. Raw scores mostly track how common a note is, so affinity is the log ratio of personal to global PageRank. Without that, vetiver and musk win for everyone.
  • Low ratings have to push notes negative, and heavy wear should amplify love but not dislike. A 3/10 bottle worn ten times is still a 3/10.
  • Variety is cheap and measurable. The MMR re-rank cut how similar the list was to itself while mean relevance stayed within 5 percent of a plain top 10, and the tests assert both sides of that trade.
  • A seeded force layout is testable like any pure function. Small disconnected pieces drifted to the corners until the core pulled them back toward the main cloud.
  • Guard the model's output, not just its input. Claude mode keeps only ids that were in the 20 candidates it was sent.

What carried forward

This is a real import, not a copy. Drydown loads vault.js straight from #26 Fragrance Vault (Sillage, a T0 build from a few days earlier), uses its note vocabulary for the catalog, reads a Vault collection as input and exports a wishlist back to it. A small CRUD app became the data layer for a T3 recommender, which is the kind of reuse I wanted from the start.

If I took it further

I would learn the note graph from real co-wear data in Fragrance Vault instead of catalog co-occurrence alone. I would also let the variety slider show the relevance cost live as you drag it.

#092 Built with Claude Code September 23, 2026 387 automated checks

Want one like this?