I Built a Search Engine to Find Hidden Gems on Steam
Steam doesn't have a "not enough games" problem. It has a "you already know about the good ones" problem.
The store is brilliant at showing you what already won. Hades. Balatro. Whatever your friend spammed in Discord last week. The whole machine runs on momentum, and momentum almost always means: this game already has a lot of eyes on it.
What I wanted was the opposite. Games that are good, specific, and still waiting for their person. Small studios making something earnest. Titles with 200 thoughtful Steam reviews instead of 200,000. Games I'd never think to type into a search bar.
So I built IndieDex: a search-first catalog with one north star. Help the next unfound game get discovered. Not re-export the ones that already had their moment.
Here's what that actually took.
The real problem isn't search. It's what you're searching with.
Type "cozy farming game with a melancholy story" into a typical storefront and you'll get whatever is already trending in that vibe bucket. Popularity shows up before relevance even gets a vote.
For hidden gems, that's backwards. You don't just need semantic search. You need search that understands intent, knows when you're naming a reference game, and has real opinions about what "quality but unknown" looks like.
Every game gets a fingerprint, not a tag soup
Raw Steam listing text isn't enough. Marketing copy oversells. Player reviews undersell (or oversell in a completely different direction). Tags are useful and noisy at the same time.
For each game, IndieDex builds a fingerprint: an LLM-synthesized portrait pulled from listing content, taxonomy, and social signals like Steam review sentiment. That text gets embedded into a vector. That's what we actually search against.
One choice I'm proud of: we keep disagreement instead of averaging it away. Marketing might call a game "relaxing." Players might call it "stressful but fair." Both can live in the fingerprint. The gap between them is signal, not noise to smooth over.
Fingerprints rebuild offline in batch jobs. Search stays fast and cheap at anonymous scale.
One pipeline doesn't fit every question
This was the biggest lesson: treat different queries like different products.
IndieDex routes searches before touching vectors:
"Games like Celeste"
Don't embed the literal string "games like Celeste". Resolve Celeste to a catalog entity, then search using that game's fingerprint embedding (and exclude Celeste itself). You're finding neighbors in game-space, not in English-syntax-space.
"Celeste"
Run hybrid keyword + vector search so exact titles surface even when embeddings get fuzzy.
"Roguelikes under $15 from the last year"
Structured filters (price, release date, tags) hit SQL directly. No embedding. Cheaper, faster, sharper.
"Cozy games after a long day"
Pure vibe search: embed the query, find semantic neighbors across the catalog.
A classifier sits above the vector index and picks the path. Expensive work only runs when it's the right tool.
Precompute neighbors. Rerank with policy.
Live nearest-neighbor search across hundreds of thousands of embeddings per query doesn't scale on a budget. This project is deliberately cost-conscious.
Similarity candidates are precomputed offline: for each game, we store top-N nearest neighbors in Postgres with an HNSW index. "Games like X" reads a bounded set, not the whole catalog.
Ranking doesn't freeze at write time. A read-time rerank applies tunable multipliers over ~50 candidates. Discovery policy changes. You want to adjust how hard you tilt toward smaller studios without recomputing the entire similarity graph.
Rank for discovery, not for fame
Uncomfortable truth: most ranking signals default to popularity.
Steam review count is a useful quality signal until it isn't. A game with 50,000 reviews isn't hidden. It's famous. If your engine keeps surfacing those titles, you built a worse Steam.
IndieDex stacks soft, multiplicative nudges at rerank time:
Studio-size tilt. We classify studios (indie, mid-size, AAA) and gently penalize larger ones on ranked surfaces. Not a hard filter. A relevant blockbuster can still show up. It just shouldn't beat an equally relevant indie title on marketing gravity alone.
Megahit demotion. Very high review volume gets softly pushed down on discovery surfaces. The goal isn't "low reviews = better." It's "don't let already-famous games eat the slots meant for the next earnest title."
Anti-spam axes. Thin engagement, bargain-bin pricing patterns, stale legacy releases, settled "Mixed" reception: small multipliers, not ban hammers. They stack so close calls get decided without walls.
Quality intent is different. When someone asks for "best roguelikes" or "masterpieces," that's explicit. Acclaimed reception can boost there. The system reads the ask.
Popularity is opt-in. Demoting external fame is the default on discovery.
Serendipity on purpose
Search is the front door. Not every great find starts with a query.
"I'm Feeling Weird" pulls from a curated pool of odd, delightful search queries and runs them through the same routing pipeline. Structured dice roll.
Daily Mystery Pick hard-excludes AAA and mid-size studios, requires positive Steam reception, and targets 150–2,000 reviews: enough signal the game is probably good, not enough that you've definitely already heard of it.
These aren't gimmicks. They're guardrails for when the user doesn't have words yet.
Stack, briefly
Next.js app. Convex for user state (lists, reviews, auth). Postgres on Neon for the catalog. Fingerprints synthesized with DeepSeek, embedded with Gemini, searched with pgvector + HNSW. Hot paths cached so anonymous traffic doesn't melt the bill.
Catalog data flows one way (ingestion → Postgres). The browser only sees shaped DTOs through a BFF. At catalog scale, allow-lists beat "fetch everything and strip fields later."
What stuck with me
Semantic search finds similar games. It doesn't find overlooked ones. You need routing, policy, and ranking that encode what "hidden" means for your product.
Precompute the expensive graph. Rerank the cheap policy. Similarity is relatively stable. Discovery philosophy changes.
The moat is data quality, not UI chrome. Anyone can wrap the Steam API. Fewer people normalize a catalog, synthesize fingerprints, classify studios, and regression-test golden queries every time the ranker moves.
Steam will keep winning at "what's already hot." I'm building for the game you'd have loved if it had crossed your path.
Building something in discovery or recsys? I'd love to hear how you're handling the popularity problem. Just want your next obsession: indiedex.com. No account required to search.
Top comments (0)