Worn

Scores what you almost bought against what you already own.

A mobile-first wardrobe PWA that catalogues what you own, composes outfits from it, and scores a prospective purchase against the gaps you actually have.

2
ingestion paths
2
Groq models
v1.0.0
shipped

A closet is a dataset nobody has bothered to model

Everything here follows from treating a wardrobe as structured data rather than a photo album. Once items carry type, colour, warmth and wear history, an outfit is a query, a daily suggestion is that query with weather and occasion bound in, and “should I buy this” becomes a comparison against a set you already hold. The PWA shell exists because the moment you need any of this is standing in front of a rail, on a phone, often without signal.

Decisions

Two ingestion paths, because one of them is not yours to control

Adding an item from a product link is the fast path, and it fails whenever a retailer blocks scraping — which is a decision they make, not one you can engineer around. So a photo path runs beside it through a vision model, and the link path falls back to it rather than erroring. The feature degrades to slower instead of to broken.

The model may only use clothes that exist

An outfit generator that invents a garment is worse than useless, because it looks right until you go to wear it. Composition is constrained to items actually in the wardrobe and the result is validated against that set before it is shown, so a hallucination fails closed rather than reaching the screen.

Scoring a purchase is a product decision wearing a model

The pre-purchase advisor asks how much a candidate duplicates what you own and which gap it closes. The hard part was never the inference — it was deciding what redundancy means when two shirts are the same colour but different weights, and being willing to tell the user not to buy something.

The interesting constraint was taste, not throughput

Pulse is an argument about infrastructure: local-first, sync, nothing waiting on a network. Worn is an argument about judgement — what to store, what counts as a duplicate, when to say no. It needed less engineering and more deciding, and the deciding was harder.

Built with

  • React 18
  • TypeScript
  • Vite
  • Tailwind
  • Framer Motion
  • TanStack Query
  • Zustand
  • Supabase · Postgres · RLS · Storage
  • Groq · gpt-oss-120b · qwen3.6-27b vision
  • Workbox PWA