Inspiration
Finding something to do on a free evening is surprisingly hard.Ticketing apps are sorted by popularity or by who paid for placement, so the small pottery class, the indie film screening or the local band that sounds like your favourite artist gets buried. On the other side, small organizers guess what their audience wants, write one promo blurb, and hope. I wanted one app that works for both sides: attendees find experiences that fit their taste, budget and calendar, and organizers see real demand and learn which wording actually brings people in.
## What it does
For attendees
- Browse and search in plain language. "pottery this Sunday under 1500" turns into editable filter chips. Filters cover dates, budget (fees included), party size and time of day.
- Taste-aware ranking. Pick interests (artists, films, books, places). Qloo then reorders events that already pass your hard filters and explains why each one might suit you.
- Real booking flows. Ticket tiers, fee breakdowns, seat maps for films and theatre, QR tickets and cancellation. Seats can never be oversold.
- A planner. Give it dates, area, budget, group size and an optional dinner stop. It returns up to three feasible plans with the trade-offs spelled out. When nothing fits, it tells you which constraint is blocking.
- Group boards. Share a link, friends join, vote and set a shared budget and date window.
- Wishes. Describe an event you wish existed. Organizers see the demand and can propose an event in response.
For organizers
- Create and manage events, sessions and ticket tiers.
- Demand clusters built from opted-in wishes, plus Qloo's "this crowd also loves."
- Title lab. Claude drafts title and hook variants anchored to each event's Qloo "for fans of" links. A bandit learns which variant works for each taste cluster.
## How I built it
- Next.js 15 (App Router) with server components and route handlers, styled with Tailwind CSS 4.
- Turso (libSQL) through Drizzle ORM. All SQL lives in repository modules, and every schema change is a migration.
- Qloo Insights API for entity search, taste-based ordering restricted to our catalog's mapped IDs, explainability, genre tags and audience affinity. Responses are cached for 24 hours in the database, duplicate in-flight requests are merged, and calls are throttled to the hackathon key's limits.
- Claude (Anthropic API) drafts promo variants and scores their likely click appeal. Deterministic templates take over when no key is set.
- Vitest with 90+ unit tests running against a throwaway local database.
- Deployed on Vercel.
### The experimentation engine (LOLA-inspired)
I based the promo experiments on LOLA: LLM-Assisted Online Learning Algorithm for Content Experiments (Ye, Yoganarasimhan & Zheng).
The Title lab uses a UCB variant whose exploration bonus is scaled by the variance, which suits single-digit click rates:
I extended LOLA in four ways:
- Claude writes the variants, one per Qloo "for fans of" anchor.
- A separate bandit runs for each taste cluster.
- Bookings are the reward, with clicks as the fallback while bookings are sparse.
- A variant is only called "likely best" once every variant has 200+ impressions.
A simulated audience of 2,000 synthetic visitors, whose click odds follow Qloo affinity, shows the bandit converging faster than an equal-split A/B test. Simulated numbers are stored separately and never mixed into real metrics. ## Challenges we ran into
- Qloo scores don't compare across queries. Affinity from one query isn't on the same scale as another's. We only compare positions within a single query, and taste only reorders events that already passed the hard filters. It never overrides price, date or availability.
- Staying inside API limits. At 5 req/s and 10,000 requests a month, caching, deduplication, throttling and a monthly budget cap were essential, not optional.
- No overselling. Each booking is one atomic statement,
UPDATE … SET sold = sold + qty WHERE sold + qty <= capacity, plus a unique index on seats and idempotency keys against double submits. - Keeping LLM copy honest. Generated titles could imply an artist was performing, or invent urgency. Every variant passes deterministic rules and a second Claude review before an organizer sees it.
- Group planning constraints. Combining several people's budgets, date windows and seat needs into options that genuinely work for everyone was harder than it looked.
## What we learned
- LLM predictions are a useful prior, not ground truth. Bandits let real behaviour overrule them quickly.
- Being honest in the UI helps: saying "Qloo unavailable", "Sample listing" or "no plan fits because of your budget" builds more trust than quietly relaxing constraints.
- Keeping pure logic (search parsing, planner, pricing, bandits) separate from I/O made it far easier to test.
## What's next
Real payments and inventory, organizer email verification, actual travel times instead of straight-line estimates, and calibrating Claude's priors against past experiments, as the full LOLA paper does.
Built With
- anthropic
- claude
- date-fns
- drizzle-orm
- libsql
- llm
- multi-armed-bandit
- nextjs
- node.js
- qloo
- qrcode
- react
- sqlite
- tailwindcss
- thompson-sampling
- turso
- typescript
- vercel
- vitest
- zod
Log in or sign up for Devpost to join the conversation.