-
Pick what you love, get personal picks, see why: onboarding, a For You card, and the Qloo explainability behind it.
-
Press play: the official trailer, a gameplay video or an artist’s channel, loaded from YouTube only after you ask.
-
One taste profile, one card at a time: a recommendation that cites picks from other categories.
-
Music, games, and how each round was found: artists and games from the same profile, and the panel that shows the queries and rules.
-
Compare with a friend, then see the agent's steps (the technical trace is one click away): nothing is sent until you press Send.
Most recommendation apps know what you like in one category. TasteMate connects your tastes across movies, music, and games to help you discover what you'll love next.
Inspiration
Most of what we use to decide what to watch, hear or play stays inside one category: your film ratings do not help you find an artist, and your game library does not help you find a movie. Qloo's taste graph links entities across those categories, so we built TasteMate around one question: can one picture of your taste, kept in one place, explore all three?
What it does
TasteMate keeps one taste profile across movies, music and games and uses it to show you one recommendation at a time, with the evidence Qloo returned for it.
Taste onboarding. A new visitor is asked to pick a few things they love: "Let's get to know your taste. Pick a few things you love. We'll take it from there." The covers of movies, artists and games come from a starter deck; one tap likes a work, a second tap takes the like back. Onboarding can be skipped, a visitor can search for a work or use the IMDb / Letterboxd import instead, and "Try a sample taste" likes a fixed set of 4 movies, 3 artists and 3 games and rates 24 more works with different scores (7 movies, 8 artists, 9 games) in one tap, so the first round is personal and Compare with a demo friend has a score. The IMDb and Letterboxd imports preview the file in the browser and send only the rows you select (up to 20 per import). With fewer than three strong likes the app does not claim personal picks: it shows starter picks marked "Getting to know you" and says how many more likes are needed.
Unified Taste Profile. Every work has one latest event, and that event decides what it means. A like, a rating of 8–10, or a "seen" scored 8–10 is a strong signal; "Want to…" is a weak one; ratings of 6–7 and pressing Next are neutral and change nothing; a rating of 5 or lower, or "Not interested", is never used as a signal and the work is not shown again. At most 10 works go to Qloo per query, chosen so that one category cannot drown the others: on For You each category gets at most 4 signals in turn; on a single tab, that tab's category gets at most 6 and each other category 1–2 first, with leftover places going to the other categories. Within a category, strong signals come first: the first half of the places go to the most recent ones, the second half to the highest-scored ones. With 100 high-rated movies, 5 artists and 3 games, For You uses 4 movies, 3 artists and 3 games.
Discovery Queue. The top of the screen switches between For You (with a sparkle icon), Movies, Music and Games; the choice is remembered, and switching never deletes a like. Every tab uses the whole profile, not only that category's history. For You asks Qloo for movies, artists and games with the same signal (3 queries) and shows the results in turn; a tab asks one query. A round is 10 cards, one at a time; works you already rated or answered, and works with the same title, are left out. The buttons follow the category: Want to watch / listen / play, Seen / Listened / Played (then tap 1–10), Not for me, and Next. A music card is an artist, not a song. A score on a card is saved as a rating in the right category, so Compare and the recommendation agent see it too. At the end of a round you see how many you saved, rated and skipped, and "Next 10" builds the next round from the latest signals. If you pressed Next on every card, the next round does not repeat them: cards from your last two rounds that you did not answer move to the back of the list (never dropped, so a round is always full), with no new taste signal and no new Qloo query. Every card can open a YouTube player after you press play, when Wikidata has a trailer, another video, or a game or artist channel and YouTube's oEmbed check says it can be embedded; a movie or game with none gets a play button that looks for the official trailer on YouTube when you press it (cached, capped per day); if nothing is found, the card has a YouTube search link. Movie cards also link out to Netflix and JustWatch to check where to watch.
"Why this?" Each card says "Because you liked …" with up to three of your picks, from any category (for example an artist can be recommended because of a movie and a game). Expanding "Why this?" lists those picks with the share Qloo's explainability reported for each, exactly as returned; the bars are only scaled to the largest of the three, and nothing is shown that Qloo did not send.
"How TasteMate found this." A panel in the card's scrolling area on a desktop, or behind "Details" on a phone, lists, for that round: the signals sent to Qloo per category, how many Qloo queries the round needed and how many of them the cache answered ("Asked Qloo 3 times (2 answered from cache)"), the suggestions each query returned and kept, how many works were left out, and the order the cards were interleaved in. It states that this is TasteMate's fixed exploration rules, not an AI model thinking.
Friends, one level down. Compare, the inbox of recommendations and quick ratings for a friend sit under a separate Friends tab; they are not needed to use Discovery. Movies, music and games are compared separately; album and song ratings are collapsed to artist-level ratings for music. For each direction, one person's linked 8+ favorites become Qloo's signal and the other person's eligible recent ratings become candidates; code compares Qloo's ranking with the scores that person actually gave, using Spearman rank correlation. Code repeats this in the other direction and maps the mean correlation to a 0–100 ranking-agreement index, not a probability of liking a title or a measure of friendship. A comparison needs at least three linked favorites, five eligible candidate ratings and variation in the candidate scores in both directions. The Shared-ratings baseline uses only works both people rated and cannot compute a correlation for a pair with zero shared titles; Mei and Kai are fictional demo profiles that show a zero-overlap pair, not evidence of human outcomes.
The friend recommendation agent. A comparison result offers a domain-specific Recommend button that starts a draft workflow. In the default Clef mode, code lists the legal next actions (rating reads, mode choices, eligible mood-tag choices) and the Clef-flash model only chooses among them; a choice that was not offered is an error and the workflow re-runs on the fixed template planner. Clef receives step names, enums, counts, success states, the mode, the domain and the ids of the mood tags Qloo offered, without titles, scores, names or handles. There are three recommendation paths: shelf ranks my own eligible 8+ works for a friend; bridge discovers works neither person rated; cross can use a friend's movie taste to discover artists or games. Reasons show Qloo links when available; missing explainability does not become a claim that both friends supplied a reason. A mood line can steer discovery through Qloo tags; Clef may choose a returned legal tag or none. The draft is an editable message with an open Agent steps timeline that shows what the agent did and which Qloo calls it made; sending is a separate user action, not an agent tool. Default Clef messages use templates; the optional Gemini mode may supply a message that passes the implemented checks, with a template used when it does not.
How we built it
Cloudflare Workers hosts the Hono API and the static browser UI; D1 stores application data; the profile, queue and comparison engines are TypeScript. The Qloo integration uses insights (discovery with explainability and ranking), search, entity lookup by id, and tag search. Application code builds the profile, picks the signals, filters and interleaves the results and counts how many queries a round needed; the production configuration selects the real Qloo adapter. Responses are cached in D1 for 24 hours, and every Qloo call is reserved against a per-network, per-guest and site-wide daily budget before it is made.
Third-party services and data
- Qloo Taste AI API: discovery with explainability, search, entity lookup, and mood-tag lookup. Qloo response data (entity ids, picture links, the starter deck) is kept only in our private D1 database (the response cache and the
onboarding_decktable); it is not in the public repository, which holds only titles of works. - Cloudflare Workers AI / Clef-flash: the default constrained action-selection planner for the friend recommendation agent, alongside Workers hosting and D1 storage. It receives step names, counts, success states, the mode, the domain and mood-tag ids, not titles, scores or names. The Discovery queue itself uses no language model.
- Google Gemini (optional): an alternative planner and checked-message path for the friend agent, subject to the implemented configuration, age and region gates; it does not compute the compatibility index. It receives what the steps found (titles, scores, the reason for each pick, the draft message) with names and handles replaced, and for a mood request the mode and the names and ids of the Qloo tags offered, never the typed mood line.
- Wikidata: one batched lookup of the Wikidata ids of the movies, games and artists on screen, for YouTube trailers, other videos and channel ids; if that lookup fails or is slow, the card keeps a search link. At most 400 Wikidata requests per UTC day.
- YouTube: click-to-load embedded playback (a trailer, a game video, or an artist channel's popular uploads), the public oEmbed endpoint (the server asks whether a video can be embedded; public ids only), the YouTube Data API search (only when you press play on a movie or game with no known video; the title of the work is sent, nothing about you; results cached for at most 30 days) or an outbound search; what plays, and whether it plays, is controlled by YouTube.
- Letterboxd / IMDb export files: user-selected browser-side import inputs, not a connection to the user's service account.
- Qloo images: when a card or deck cover has a picture, the browser loads it from images.qloo.com, Qloo's image server, with no Referer; other image hosts are not loaded.
- Netflix / JustWatch: outbound title / search links for the viewer to check where to watch; the app does not fetch those sites to verify availability.
Challenges
We tested the Qloo API before designing around it:
- Mixed signals work. One query with movies, artists and games as signals returned results for every target type, and Qloo's explainability listed signals of all three types for each result; with no weights, each signal carried about 1/N of the reported influence, so the number of signals per category is the only lever. That is why the profile selects signals per category instead of sending everything.
- A query with no signal was refused in our probe. A movie insights call with no signals and explainability turned on returned HTTP 400, and we did not isolate which part was refused. A new visitor therefore gets a starter deck labeled as not personal, and Qloo is not asked until there are three strong likes.
- We could not use weights. Our attempt to send weighted signals was rejected by the hackathon endpoint (HTTP 400) and we did not find an accepted format in the time we had, so signals are sent as plain lists and counted equally.
- Game results often come without a picture. In a raw check of Qloo's game results, one of five carried an image link. When cards come back without one, the app asks Qloo once, by entity id, for the pictures of that round's game candidates and shows a plain title card if there is still none.
- Keeping the evidence honest. Qloo provides candidates and evidence, code selects, filters and counts, and the model (only in the friend agent) chooses among legal actions. The Discovery screens say which is which, and small samples, unresolved entities and missing explainability are shown as such instead of being hidden.
- Daily limits. The demo guards its Qloo budget with per-network, per-guest and site-wide daily limits, plus a separate small pool for judges; when a budget is used up the app says so and starter picks still work.
- Privacy. A guest's shelf is public by default: anyone with the handle can see the name, the rated titles, the scores and when they were rated. The import card, the Rate screen and the privacy page say so.
No accuracy or user-outcome result is claimed here.
Accomplishments
One taste profile across three categories drives a one-card-at-a-time queue whose reasons and query counts can be inspected on the card. The Discovery rules (signal rules, per-category selection, cold start, picture look-up, Next 10 rotation, sample taste) are covered by automated tests.
What we learned
Cross-category recommendations only feel earned if the reason is visible: a returned link, a ranking-based fallback and missing evidence have to look different, and the number of signals per category has to be chosen by the app because Qloo treats them equally.
What's next
- Let the Clef planner decide, among a short list of legal options that code supplies, which category the next round should lean towards; a choice that is not on the list would fall back to the current fixed rule. (The friend agent already works this way.)
- An optional Genre filter, once the basic flow has been used by more people.
- Observing whether people can complete the flow and understand its evidence, and keeping the unsuccessful cases. This is a plan, not a result.
Try it
No sign-up. Open https://tastemate.theroyatlas.workers.dev, press Pick what you love or Skip for now. Either button creates a sandbox guest account that follows three fictional friends (Mei, Kai and Lin). If you skip, the "or try a sample taste" link at the top fills in a sample taste profile so you can see personal picks right away.
- Start with a sample taste. On the onboarding screen ("Let's get to know your taste") press Try a sample taste (or tap a few works you love yourself). The first For You round is 10 personal picks; each card says "Because you liked …", and Why this? shows which of your picks Qloo linked to it and the share Qloo reported for each.
- Switch tabs (Movies, Music, Games) and give feedback: Want, or "Seen" / "Listened" / "Played" and a score (two taps), or "Not for me". Finish the round and press Next 10. How TasteMate found this (in the card's scrolling area on a desktop, behind "Details" on a phone) lists the signals, the Qloo queries (and how many the cache answered), what was left out, and says it is fixed rules, not an AI model.
- Friends → Compare with Lin, Kai or Mei. Open the Friends tab and compare with any of the three fictional friends: movies, music and games each show a compatibility score, because the sample ratings give both directions enough works.
- Recommend → Agent steps. Press Recommend under a score: the friend agent shows its Agent steps (which Qloo queries it ran, what code kept or dropped, and the draft message); technical details (the planner, the mode and the function names) sit behind a closed Technical trace.
- Start from your own taste (optional). Start fresh on the "Sample taste" bar clears the sample likes and ratings (a rating you gave yourself stays) and returns to onboarding.
Daily limits are in place (per network, per guest, site-wide); if a budget is used up, the app says so and starter picks still work. A guest can delete their data with Shelf → Delete my data. Movies, artists and games on the Discover tab come from the real Qloo API; only the three demo friends are fictional profiles. Judges: the link in the "public URL to your project" field opens a judge guest with the sample taste already applied and its own Qloo budget.
Built With
- axe-core
- cloudflare-d1
- cloudflare-workers
- css
- hono
- html
- javascript
- qloo
- typescript
- vitest
- wikidata
- workers-ai
- wrangler
- youtube
Log in or sign up for Devpost to join the conversation.