Project 7 // _rypc
RateYourPkmnCards
Turning a huge card catalogue into a community — ratings, reviews and shareable binders.
client
personal product
timeline
2024
role
solo build · full-stack
scope
## the-problem
Pokémon TCG collectors have endless places to check what a card is worth, but nowhere opinionated to say whether a card is actually good — the art, the chase factor, how it feels to pull. That signal was scattered across Reddit threads, Discords and YouTube, and none of it was in one place.
The catalogue itself is huge and image-heavy: thousands of cards across hundreds of sets, with inconsistent naming and big artwork. Any community layer had to sit on top of a fast, searchable, image-friendly catalogue first — otherwise browsing would crawl.
## the-approach
- ▸normalized the full TCG catalogue — every set, card, type and rarity — into one searchable, filterable data layer
- ▸a rating + review system that rolls individual scores up into community averages and top-rated leaderboards
- ▸a binder builder: arrange favourite cards into customizable virtual binders and share them by link
- ▸accounts that tie each member's ratings, reviews and binders together
- ▸image-performance work — lazy loading, responsive sizes and caching — so browsing thousands of cards stays instant
// ranking — two Bayesian passes: votes first, then catalogue size
// (C × m + R × v) / (C + v). Two five-star votes shouldn't// outrank four hundred good ones, so sparse scores are pulled// toward the global mean until the votes earn their weight.export function calculateBayesianScore(averageRating: number,ratingsCount: number,globalMean = BAYESIAN_CONFIG.GLOBAL_MEAN, // 3.5confidence = BAYESIAN_CONFIG.CONFIDENCE_THRESHOLD, // 20 votes): number {if (ratingsCount === 0) return globalMean;if (averageRating < 0 || averageRating > 5) return globalMean;return (confidence * globalMean + averageRating * ratingsCount)/ (confidence + ratingsCount);}// Artists and sets get a second pass with the same shape, where// the catalogue is the vote count — one perfect card is not a// body of work.export const calculateCatalogAdjustedScore = (score: number, cardCount: number, mean: number, M: number,) => (score * cardCount + mean * M) / (cardCount + M);
## the-architecture
// architecture — catalogue in, ratings & binders out
web app
Next.js · SSR
- browse by set
- search + rarity filters
app API
ratings · binders · auth
- review + score writes
- aggregate rollups
catalogue sync
cards · sets · images
- normalizes card data
- keeps sets current
card source
TCG data
- every set + card
- official artwork
database
users · ratings · binders
- community scores
- per-user binders
## the-results
every set
in the catalogue
1000s
cards rateable
public
shareable binders
- ✓the whole catalogue — hundreds of sets — browsable and filterable in one place
- ✓community ratings surface genuinely top-rated cards, not just expensive ones
- ✓binders give collectors a reason to come back and something to share
// want something like this built for you?
start-a-project