I think an outfit is a response to an occasion. Not a random assembly of clothes -- a set of decisions about a specific moment. That's why I built a wardrobe app years ago.
It holds over 400 items, each with its history: where I bought it, what the tailor changed, what it does and doesn't go with. Beautifully organised, and completely silent. An archive with no archivist.
Faced with the daily question of "what should I wear?", I still had to do all the work. My app could show me the pieces, but the wisdom (the intuition to connect the clothes to the moment) was all in my head. I decided it was time to change that. It was time to extract the messy, intuitive, human rules of my personal style and forge them into cold, hard code. I always wanted to see how that would turn out.
This is the story of that four-week journey.
Part 1: The Archivist vs. The Accountant
Before an engine can provide intelligent analysis, it needs consistent, high-quality data. As I prepared to build the recommender, I audited my own process and discovered a critical flaw. My existing system treated every outfit like a museum piece. It required a name, notes, and a rating. It's a workflow perfectly suited for archiving a carefully constructed, compliment-worthy ensemble.
But not every outfit is a work of art. Sometimes, an outfit is just... what you wore. A simple t-shirt and jeans for a Tuesday working from home. These combinations are critically important data points for an analytics engine (they reveal what I actually wear), but the creative overhead of "archiving" them felt like a chore. The friction was too high, and as a result, these mundane-but-important outfits often went unlogged, leaving my data incomplete.
The app needed to be two things: an Archivist for the outfits worth remembering, and an Accountant for the ones that just happened.
This led to the "Log Today's Wear" feature: a minimalist, single-button interface for the Accountant. It's a purpose-built tool for a single job: rapidly logging the items I wore today for data analytics, without any of the creative overhead. It turned a chore into a satisfying, two-click daily ritual. With this new, frictionless flow of data, the foundation was set.
Part 2: The Prerequisite of Purpose
Before the system could offer a single piece of advice, it had to understand my core philosophy. This is where the "Outfit of the Day" page became the focal point of my project. I spent a few days just jotting down ideas on the notebook I carry around with me. I also put together some UI mockups on my Procreate app. I took a break from development during those days.
I wanted to design it not as a simple selector, but more as a deliberate ritual that respects the when and why. Before asking for a recommendation, the tool forces me to define the context: What is the exact temperature range for the day? What is the occasion?
Each occasion carries its own constraints: a formality range, how much you can get away with expressively, how much you'll be moving around. "Chamber Music" and "Jazz Club" are both evening events with live music and almost nothing else in common, and the app should know that.
With this framework in place, the stage was set. I had a way to ask the right questions. Now, I needed to build an engine that could provide the right answers.
Part 3: Translating Intuition into Logic (with an AI Consultant)
Here's the hard part: how do you turn the feeling of a good outfit into a score_outfit() function? I used an AI assistant for this, not to write the rules, but to keep asking me why until I could state them precisely.
- The Temperature Containment Rule: My intuition told me that an outfit suitable for 10-20C (yes I use Celcius, sue me) is a terrible choice for a 15-25C day, because that last 5C of uncovered range will be miserably hot. We translated this into a scoring function: an outfit only gets a perfect score if the day's temperature range is fully contained within the outfit's comfort zone. The score is penalized based on the size of the "uncovered" gap.
I was struggling with how to penalize formality mismatches. I asked the assistant, 'How can I mathematically model that being slightly underdressed is okay, but being very underdressed is a disaster?' It suggested exploring non-linear penalties, which led me directly to the idea of the exponential 'Formality Cliff' function.
- The Formality Cliff: A simple linear score for formality felt wrong. Wearing a "Very Casual" outfit to a "Smart Casual" event isn't just a small step off; it's a fashion faux pas. The solution was an exponential penalty. An outfit one step outside the required formality range has its score divided by 10. Two steps out, and it's divided by 100. This creates a steep "cliff" that heavily punishes bad combinations, just as my gut would.
Part 4: The Birthday Party Problem: Engineering Variety
An early version of the recommender had a glaring flaw: it was boring. For any given occasion, it would almost always suggest my single highest-rated "staple" outfit. This is logical, but it's not how people dress.
I imagined a common scenario: I'm going to a friend's birthday party. I want to wear something fun and that I feel great in, but I certainly don't want to wear the exact same thing I wore to the last three parties, even if it is my favorite. The system needed to understand the human desire for variety.
The solution was the "Recency Penalty Heuristic". When looking for a saved outfit, the engine first identifies a pool of all valid candidates that meet the day's requirements. Then, it ranks them by when they were last worn and applies a penalty. The most recently worn outfit has its score cut in half. The second-most recent gets a smaller penalty, and so on. The result is that it rotates, and things I'd forgotten I owned resurface.
Part 5: Building Outfits That Don't Exist Yet
The last step was getting it to assemble something new, item by item, rather than recalling something I'd already worn. Which meant writing down what makes an outfit cohere:
-
Color Harmony: I already document the primary and accent colors of every piece of clothing I own in Hex format. But... I needed to pin down which color group it actually belongs to. The engine now uses the CIELAB Delta E (dE2000) color science formula to calculate the perceptual distance between colors, allowing it to "fuzzily" identify if a color is a shade of off-white, charcoal, or beige. This lets it enforce a classic style rule: a coherent outfit should have one main color, supported by any number of these identified neutrals.
-
Probabilistic Layering: The system now understands how to layer for the weather. On a day with a large temperature swing, it knows a removable Outerwear piece is a better choice than a Knitwear piece and will probabilistically favor it. On a consistently cool day, it leans towards a cozy sweater. It also understands that a knitwear piece demands an undergarment, automatically adding one to the final outfit.
-
The Gauntlet: The engine generates twenty plausible outfits, scores each against every rule above, and shows me only the winner. The "re-roll" button runs the tournament again.
Four Weeks Later
I wasn't trying to automate getting dressed. That's one of the better parts of the morning and I'd like to keep it.
What I wanted was something to argue with -- a second opinion that has actually read all 400 entries, including the ones I've forgotten. That's what it does now. Most of its suggestions I reject. Every so often it puts two things together that I wouldn't have, and it's right.
The hardest part wasn't the code. It was being forced to say out loud, precisely enough to compile, why one outfit works and another doesn't. Turns out I only half knew.