The problem
New York has more events on any given night than a person could evaluate in a week, and almost none of them are relevant to any particular person. We ran 20 user interviews before writing product code. Two findings shaped everything after.
The first was fragmentation: 87% of the 18–35 New Yorkers we spoke to used three or more apps to figure out what to do — Eventbrite, TimeOut, Instagram, Resy, a group chat. The second was sharper, and it was a negative result. Zero interview subjects asked for another social feed. People did not want more to browse. They wanted an answer.
“We spend most of our time looking into activities we end up not even going to. We just get overwhelmed and give up.”
Sam, 23 · user interview“Honestly? I don’t want another social media app. I just want to know what’s good this weekend.”
Recurring theme across 20 interviewsThat pushed us away from a feed and toward a ranked deck: one card at a time, ordered by predicted fit. Which creates a cold-start problem. A recommender with no signal cannot rank, so our first instinct was the obvious one — ask the user up front.
What I built
The client is React Native on Expo; the backend is Supabase. The interesting parts are the ingest pipeline and the scoring loop.
Six live scrapers pull from Dice, Resident Advisor, Luma, Fever, NYC museums and Eventbrite, refreshed every three days via GitHub Actions. Claude Sonnet finds what the scrapers miss — pop-ups, sample sales, gallery openings. Everything ingested is then cut hard: an LLM rubric rejects tourist traps, corporate spam and kids’ events, which removes roughly 85% of incoming inventory. What survives gets a vibe score from 1–10 via gpt-4o-mini; anything under 5 never loads.
Aggregators need the firehose because their economics depend on total inventory. We don’t, which is the whole design premise: rejection is the product.
On the client, every swipe carries intent. Each gesture updates four independent signals — category, tag, borough, price band — and the deck re-ranks in under 200ms.
| Gesture | Intent | Signal effect | Weight |
|---|---|---|---|
| Right | Going | Category, each tag, borough | +0.15 / +0.08 / +0.06 |
| Left | Not now | No taste effect; resurfaces in 2–5 days | 0.00 |
| Down | Not interested | Category penalty; 3 strikes hides permanently | −0.05 |
| Up | Inspect | Opens detail, deliberately no signal | — |
Cold start blends quality and timing for roughly the first 20 swipes, then hands over to personalized ranking. That threshold is what confidence measures — an internal diagnostic, built to check the recommender had enough signal before we trusted it. It turned out to be the most important number in the product.
What the data said
We launched with no paid acquisition. The App Store funnel was healthy — 157 impressions, 79 product page views, 18 first-time downloads, a 20.5% conversion against Apple’s typical 5–7%. With 23 seeded TestFlight users still active, that put us at 41 people.
Then I pulled the onboarding funnel expecting a drop-off curve, and got something that didn’t resolve.
The mechanism was in our own design and we hadn’t noticed it. Cold start hands over to personalized ranking after about 20 swipes. Twenty swipes takes under two minutes. The taste-setter was asking users to spend ninety seconds declaring preferences the deck would infer from two minutes of ordinary use.
The onboarding flow wasn’t a bottleneck we needed to widen. It was a question the product was already answering by watching.
The findingThis surfaced because two independent sources agreed. A heuristic UX audit flagged the flow as skippable friction; separately, the scoring code showed confidence accumulating for users who had never touched it. Neither alone would have been convincing — the audit could have been my taste, and the score could have been a bug. The finding lives in the agreement between them.
Every figure here comes from a launch cohort of 41 users. The percentages are honest but the denominators are small: 60% is roughly 25 people, and the zero is a genuine zero rather than a rounding artifact. I’d re-run all of it at 1,000 users before treating any of it as settled — but the direction was clear enough to act on, and a design decision that costs every new user ninety seconds does not need p < 0.05 to be worth revisiting.
What I'd do next
Cutting the taste-setter is the obvious move and the least interesting one. The more useful question is what else in the product asks for information it could observe instead.
- Cut the taste-setter entirely; keep one optional neighbourhood prompt at first save, where intent already exists
- Surface confidence to the user as a reason — you’re seeing this because you saved three like it — turning an internal diagnostic into trust
- Instrument the inverse: which users never reach confidence, and what they have in common
- Re-run the audit against the scoring code each quarter, since that pairing is what caught this
The broader lesson I took into later work: instrument the implicit path before you build the explicit one. We built the questionnaire because it was the legible solution, and only discovered it was redundant because we happened to have logged the alternative.