Mina is 15. She grew up in the US with Minecraft, Olivia Rodrigo and Stranger Things, and her grandmother in Seoul speaks almost no English. Their video calls keep getting shorter. Mina doesn't need another vocabulary app. She needs Korean shows and music she would pick anyway. Subtitles Off gives her a four-week plan built from her own taste. In week 1 it starts with BLACKPINK ("Because you like Olivia Rodrigo, 38% of the match"), the variety show Busted! and the family film Red Shoes and the Seven Dwarfs. By week 4 she is watching Ashfall with the subtitles off.

Her situation is common. Using two large surveys of immigrant families in Southern California, Rumbaut, Massey and Bean found that even Spanish, in the country's largest Spanish-speaking region, is expected to stop being spoken as a mother tongue by the third generation (Linguistic Life Expectancies, Population and Development Review 32(3), 2006). Adult learners hit a similar wall. "Watch shows in the language" is good advice, but the suggestions are usually the same few global hits, whatever the person actually enjoys. The hard part is finding your kind of show in their language. Cross-domain taste data is built for that problem.

(Mina, Sam and Jordan are the app's fictional example people. Their names are never sent to any API.)

What it does

  • Input: a target language (Korean, Spanish, Japanese or French), a level, two to five favorites from any domain (a game, an artist, a show, a book), and optionally the learner's age, "family mode" and genres to avoid.
  • Output: a four-week plan of TV, film and music from that language's world: Ear first, Catch the rhythm, Target-language subtitles, Subtitles off. Every pick says which favorite it comes from and how much, using Qloo's explainability. The agent's own sentence is shown separately, labelled as an agent note. Family mode adds an activity to share with relatives.
  • You can see the agent work. A live trace lists every Qloo request with its parameters and every agent step with its tool calls, tokens and time.
  • It asks when it should. If a favorite is ambiguous (The Office: US 2005 or UK 2001?), it asks before planning. If Qloo's popularity lead is clear (Minecraft the game, not a 2020 TV entry), it decides by itself.
  • Plain-English refinement. "No violence, she's 15, already watched Reply 1988" becomes genre exclusions, a rating limit and an excluded title. "Seen it" swaps one pick. Plans can be shared by link and printed.
  • "Why not just ask a chatbot?" A compare panel asks the same Claude model the same question without Qloo, including the learner's rating limit, then measures both lists the same way.

How Qloo is used

The app would not work the same without Qloo. Every pick is a Qloo entity, and the planner cannot submit an id Qloo did not return in that run. All calls run on the server with the hackathon key.

Step Endpoint What it does
Resolve favorites GET /search Each favorite becomes a Qloo entity. Same-title results are settled by Qloo popularity when the lead is clear, otherwise the user is asked.
Pin music to the language GET /v2/tags Artists have no country filter, so the agent finds the language's music genre tags (K-Pop, Rock en Español, J-Pop) and searches inside them.
Genres to avoid GET /v2/tags Turns "no violence" into genre tag ids for filter.exclude.tags.
Taste analysis GET /v2/insights, filter.type=urn:tag Shows the genres Qloo links to the favorites ("Your taste, as Qloo sees it").
Discover GET /v2/insights TV and film use the favorites as signal.interests.entities, filter.release_country, filter.content_rating from the learner's age, signal.demographics.age and feature.explainability. Artists use the same signals with filter.tags.
Widen when thin GET /v2/insights Fewer than three results in a domain: more countries, then the favorites' genre tags, then filters only, one stage at a time, each shown in the trace.
Compare GET /search + GET /v2/insights with filter.results.entities Scores the generic list and the plan against the same favorites, with explainability and content ratings from the same call.

Mina's live plan used 9 Qloo requests. A plan is capped at 18.

How we built it

Subtitles Off is a Next.js app on Vercel. The plan streams to the browser as events.

  1. Code does the parts that must be exact. Resolving favorites, finding music tags and the taste analysis run first, in code.
  2. Claude plans with Qloo tools. Claude Opus 5.5 gets three tools: discover, widen and compose_plan. On live data it calls discover for TV, film and music in one turn, then submits a shortlist with a reason per pick. A validator accepts only entity ids that Qloo returned in this run, rejects duplicates and checks the mix of TV, film and music. The loop uses the Anthropic TypeScript SDK with adaptive thinking at low effort, strict tool schemas, server-side refusal fallback, prompt caching and an append-only history. Mina's live plan took two Claude requests, and the three example plans took about 20 to 28 seconds end to end.
  3. Difficulty is a rule, not a guess. Code, not the model, orders the weeks: animation, reality and sitcoms first, thrillers and period pieces last, music every week. The app never writes sentences in the target language, because a wrong sentence would teach the wrong thing.
  4. It keeps working without Claude. If Claude declines, errors or runs past 35 seconds, a rule-based planner finishes the plan from the same Qloo results, and the plan says "Rule-selected". If the API refuses the key for good (for example when the credit runs out), Claude is switched off and later plans skip it, so the demo stays up through judging. A gateway model through the Vercel AI SDK is the second choice when no Anthropic key is set.
  5. It stays inside the hackathon limits. The key allows 5 requests a second and 10,000 a month. A shared rate gate keeps each instance under that, 429s are retried after the reset time, Qloo answers are cached for 24 hours, and finished plans are cached by their inputs. A daily cron warms the three example plans and logs a failure if Qloo stops answering.

Tests: 151 unit and integration tests (Qloo and Claude replayed from recordings), 5 browser end-to-end tests, a bundle check that no key reaches the browser, and live smoke tests against the real Qloo and Claude APIs.

Challenges we ran into

  • "Released in Japan" is not "Japanese". filter.release_country also matches titles that were only released there too: Pixar's Luca is listed with the United States and Japan, and a Korean plan picked up Hotel Transylvania 3. The app now asks for 30 results and keeps titles whose first release country is the target country.
  • Fuzzy tag search. Searching tags for "j-pop" also returned Jangle Pop and Jazz Pop, which brought in non-Japanese artists. Each language now has a strict pattern for the tag names it accepts.
  • Same-title favorites. Real data has a 2020 Minecraft TV entry and a second Stranger Things entry from 2010. We measured Qloo's popularity gaps on the example favorites and chose a 0.07 lead as the line between "obvious" and "ask the user". The Office (US 0.998, UK 0.975) is still asked.
  • Rate limits. One plan fires 9 to 18 requests, and the first live run hit the 5-a-second limit. We added the rate gate and retries.
  • An honest comparison. On raw Qloo affinity, Claude's generic list beat the plan for Mina. The plan works inside filters a generic list does not have to respect, so we added measures that do not depend on affinity, and gave the generic question the same rating limit. In three live runs for Mina, scored against the rule-based version of her plan, 5 of the 7 generic titles that Qloo has a US rating for fit her ratings (Memories of the Alhambra, TV-MA, came back twice, and 5 of the 12 titles have no rating in Qloo), against 6 of 6 for her plan. Qloo credited 1 of her 3 favorites for the generic list and 2 of 3 for the plan, and the generic list's average popularity percentile was 87 to 94% against 83%. The generic list still led on affinity, 0.91 against 0.885, and the panel shows that too.

Accomplishments that we're proud of

  • Every pick is a real Qloo entity with a reason Qloo itself gives, and the model cannot slip in a title Qloo did not return.
  • The agent is visible: every Qloo request and every Claude step is in the trace.
  • The comparison is fair and reports the row the plan loses.
  • The demo keeps working when the LLM fails or its credit runs out, and the three examples keep opening even if Qloo is briefly down.

What we learned

Taste data is most useful where a person's favorites and the target are in different domains: a game and a singer can point to a Korean variety show. The filters matter as much as the scores. A list that ignores age ratings and country of origin can look like a better match on paper.

What's next

  • More languages. Qloo covers many more countries, and each new language needs a country list and music tag pattern.
  • Books and podcasts, which Qloo has but without a country filter.
  • Streaming availability, which the app does not check yet.
  • Letting a learner mark what they finished, so the next four weeks build on it.

Built With

  • anthropic
  • claude
  • next.js
  • playwright
  • qloo
  • react
  • tailwindcss
  • typescript
  • vercel
  • vercel-ai-sdk
  • vitest
  • zod
Share this project:

Updates

Submission history