Inspiration

Every joke, ad and speech is written for a room the writer has never been in. A touring comic tells the same bit in Atlanta and Brooklyn. An agency runs one campaign in ten markets. A best man writes a toast for a family he barely knows. The writing is fine, but the cultural references only land with the writer.

You can ask an LLM to "make this work for Atlanta, ages 45–60", but it can only guess, and its guesses often lean on stereotypes. We wanted an agent whose decisions are grounded in real taste data, where the LLM does the writing and Qloo decides what lands.

What it does

Read the Room rewrites a stand-up set, wedding toast, ad or sales pitch so its cultural references land with a specific audience.

  1. Paste your text and describe the room ("Atlanta, ages 45–60", "Brooklyn, 25–34").
  2. The agent finds every cultural reference (shows, artists, brands, people, films) and looks each one up in Qloo's taste graph.
  3. Qloo scores how strongly each reference lands with that room. References show as Lands, Okay, Misses or No data. A reference Qloo doesn't know stays grey and is never guessed.
  4. For weak references, Qloo suggests same-type replacements that share tags and score high for the room. The LLM may only choose from that list and rewrites the sentence in the same tone and length.
  5. Every swap is re-scored by Qloo and kept only if it beats the original by at least 0.10.

In the stand-up example for Atlanta, 45–60, "The Price Is Right" isn't a standout, so it becomes "To Tell the Truth" (affinity 0.97, sharing the tags Nostalgia, Humorous and Non fiction). The Room Score goes from 49 to 72.

Other features:

  • Why this swap? Click any swap to see Qloo's affinity before and after, the shared tags and the length change.
  • Qloo-grounded vs LLM only. One tab shows what a plain LLM writes with no taste data, so you can see what Qloo adds.
  • Who it lands with. A heatmap of the same references scored by Qloo for 7 age and gender groups.
  • Compare rooms. The same text tuned for two audiences, side by side.
  • What this room loves. Qloo's top TV shows, artists, movies and brands for the audience.
  • Room from an event link. Paste an event page and the agent describes the audience for you.
  • Upload a recording. Drop in an audio or video clip of a set and Gemini transcribes it into the text box.
  • Tour presets. Paste a tour-dates or ticketing link, or the list of dates and venues, and every stop becomes a room (city, plus an age range when the venue says 21+ and so on).
  • Batch mode. Tune the same text for up to 5 rooms at once, compare Room Scores and swaps in one table, open any room, and download a CSV. Built for agencies localizing a campaign and comics planning a tour.
  • Suggest new references. For lines with no reference, the agent can add one, picked only from Qloo's audience snapshot for that room.
  • Keep or revert each swap, share links with preview images, and a public REST API (POST /api/v1/rewrite).

How we built it

  • Next.js 16 + React 19 + Tailwind CSS 4, deployed on Vercel.
  • Qloo Taste AI API: /search resolves references to entities, and /v2/insights scores affinity for the room (location + age + gender signals), finds swap candidates (filter.tags with intersection and then union, filter.exclude.entities), builds the audience snapshot and powers the heatmap.
  • LLM (Gemini, with Claude as an option) for four narrow jobs: parsing the room, extracting references, choosing among Qloo's candidates and writing the sentence. All outputs are validated with Zod schemas. Gemini's audio and video understanding also transcribes uploaded recordings.
  • The agent loop: parse → extract → resolve → score → find candidates → rewrite → verify. It runs for at most 3 rounds within a budget of 20 Qloo calls, and every step streams live to the UI timeline.
  • Rules enforced in code: the LLM never produces an affinity number and can't pick a replacement that isn't in Qloo's candidate list.
  • Upstash Redis for a 7-day Qloo response cache, precomputed demos, share links and rate limits, with an in-memory fallback.
  • Vitest: 32 tests covering the pipeline, the Qloo client, LLM output validation, the event-link parser, the heatmap and batch mode. Typecheck and lint pass.

Room Score is the mean Qloo affinity of the references Qloo knows:

$$\text{Room Score} = \operatorname{round}\left(100 \times \frac{1}{n}\sum_{i=1}^{n} a_i\right)$$

A reference counts as strong when \( a_i \ge 0.90 \) and weak below \( 0.80 \). We calibrated these thresholds by probing the live API.

Challenges we ran into

  • Location filters out global names. When a request includes a city, Qloo returns only entities that stand out locally. Friends, Drake and Toyota disappeared in Atlanta, Brooklyn and London, and location weight and radius didn't change this. We treat a resolved but omitted reference as "not a standout" (weak) and never show it as a fake score. The heatmap leaves location out so global names can still be compared.
  • Resolution gotchas. "Blockbuster" has no brand match, and an untyped search returned a Blockbuster store in Oregon as a place. "Toyota Camry" resolves to "Toyota". "Gary Vee" needed the official name. We added type-family fallbacks, name-similarity rules and official-name resolution.
  • API details that differ from the docs. Docs say video_game, but the live API only accepts urn:entity:videogame. Tag IDs are called tag_id in /search but id in /insights. Unknown parameters fail silently with an empty 200.
  • Keeping the LLM honest. Left to itself, a model will drift from the candidate list or change the sentence length. We now validate every pick against Qloo's list, cap length changes at ±10% and allow "keep the original" when no candidate fits.
  • Free-tier limits. Gemini's free tier sometimes hangs, so we added timeouts, retries, a lighter fallback model, response caching and per-IP rate limits.
  • Qloo's data terms. Qloo's response data can't be stored in a public repo, so precomputed demos live in Redis and in a git-ignored folder, and tests use hand-written fixtures.

Accomplishments that we're proud of

  • A clear split of responsibility: Qloo decides, the LLM writes, enforced in code rather than in a prompt.
  • Every swap is verified by re-scoring. A swap that doesn't improve the score is thrown away.
  • An honest UI: unknown references are shown as unknown, not guessed, and the LLM-only tab shows the difference Qloo makes.
  • It loads instantly from precomputed examples, needs no login, and every number on screen can be traced to a Qloo call.

What we learned

  • Taste is very local. The same reference can score 0.95 with 18–24s and 0.62 with 55+.
  • LLMs are good writers and poor judges of what an audience likes. Grounding the judgment in data gave better and more defensible rewrites.
  • Probing the live API before building, and writing down every surprise, saved hours later.

What's next for Read the Room

  • Full-length set uploads by sending large files straight to storage instead of through the app (today the cap is 4 MB).
  • Live ticketing integrations (venue capacity, ticket tiers, buyer demographics) to make tour presets more precise.
  • Saved rooms and tours, so a touring act can rerun the whole tour whenever the set changes.
  • A "joke heatmap" across a whole tour: which bits land in which cities.

Built With

Share this project:

Updates

Submission history