Embeddings · August 8, 2026 · 5 min read

The Title Was in the Embedding

A user noticed that combining with Rust kept returning games with “Rust” in the name. That turned out to be one visible symptom of a much broader problem: the vectors encoded what a game was called alongside what it was.

Game Combiner represents every game as a 1024-dimensional vector built from its text. Combine two games and we take the midpoint, then look for real games near it. The whole thing rests on those vectors meaning something.

The document we embedded looked like this:

def doc_for(name, genres, tags_json, short, desc):
    ...
    parts = [name]          # ← the game's title, first paragraph
    meta  = ["Genres: ...", "Tags: ..."]
    body  = description

The title went in first, as its own paragraph. Which means a chunk of every vector's meaning was the literal string of the name.

How it showed up

The clearest evidence came from asking a simple question: what are Minecraft's nearest neighbours in vector space? If the embedding captures gameplay, the answer should be crafting and building games.

0.891  Minecraft: PlayStation 3 Edition
0.812  Minecraft
0.798  Minecraft: Nintendo Switch Edition
0.759  Minecraft: Playstation Vita Edition
0.751  Minecraft: New Nintendo 3DS Edition
0.748  Minecraft: PlayStation 4 Edition
0.732  Minecraft: Xbox 360 Edition
0.708  Minecraft: Xbox One Edition

Eight out of eight are the same game on different consoles. The vector had learned "this is called Minecraft," not "this is a voxel sandbox." For contrast, No Man's Sky — whose entry came from a source with proper descriptive tags — had neighbours like Stars End, Endless Sky and Cubic Odyssey. Actual space games.

Testing before committing

Removing the title means re-embedding the entire catalog, which is an hour of GPU time and a full redeploy. Worth checking the hypothesis first on a handful of games rather than assuming.

We embedded the same four games twice, once with the title and once without, and compared:

Candidatesim to NMSsim to Minecraft
Cubic Odyssey, with title0.7130.502
Cubic Odyssey, without title0.7700.513

Dropping the name moved Cubic Odyssey's similarity to No Man's Sky from 0.713 to 0.770 — a real sharpening of the gameplay signal, measured before spending the GPU hour.

It's worth being honest that this did not flip the result that prompted the investigation. Cubic Odyssey still lost, because the deciding factor was the Minecraft side, and that was a separate defect about tag quality. Two problems wearing one coat.

The fix, and the part we kept

parts = []              # title no longer embedded
if genres: parts.append(f"Genres: {genres}.")
if tag_str: parts.append(f"Tags: {tag_str}.")
if body: parts.append(body[:6000])

The name argument stays in the function signature, because callers pass positionally and because same-series suppression is handled separately — by comparing titles at scoring time, not by hoping the embedding does it implicitly. That distinction matters: title similarity is a real signal for "these are the same series," and a bad one for "these play alike." Keeping them in separate mechanisms means each can be tuned without disturbing the other.

Why it was so easy to miss

Including the title feels obviously correct. It's the most identifying text a game has, and in a search index it would be the single most valuable field. The trap is that Game Combiner isn't search. Nobody types a name and expects that game back — they drop two games in and expect a third that plays like both.

For that job, the name is pure noise, and worse, it's confident noise: titles in the same series share tokens, so the model gets a strong, consistent, entirely irrelevant signal that clusters ports and sequels together and crowds out the games that actually belong.

The general lesson is dull but keeps proving itself: a field being informative in principle doesn't make it informative for your task. The way to find out is to look at what the neighbours actually are.