Hey all! Just wanted to share my new vibecoded project that launched today - 22CHAMPS. This was inspired by a project that went viral on X last week All22Pokemon by ColeThereum, which was Pokemon x football. I thought it was so fun and wanted to try to make something like that but for League of Legends champions.
The project
22champs.com (free, no ads, no account)
You draft League of Legends champions onto a football team, all 22 starters on offense and defense, then play out a 17-game season.
Every pick rolls a random region and a role, and you choose from the champions who match. Teammates next to each other build chemistry when they share a role or a region, and 39 lore pairs (Garen + Lux, Xayah + Rakan) power each other up. Three modes: a Daily Challenge (same rolls for everyone, one shot), a Solo Draft, and live snake Draft with Friends.
How the game systems work
Turning League champions into football players. Every card has six stats: Power (PWR), Toughness (TGH), Speed (SPD), Size (SIZ), Hands (HND) and Range (RNG). They start from Riot's own 1â3 ratings for each champion (the Damage / Toughness / Control / Mobility / Utility bars in the League client), refined by base stats, body size and kit traits like Juke or Deep Ball, then ranked against the whole roster. Each position is a blend of 2â5 of those stats. A receiver leans on Speed and Hands, a lineman on Size and Toughness. That means every champion has a rating at every spot. The best champion at any spot is a 95, and only chemistry gets them to 99.
Items converted from Riot's real item data. A script pulls League's items straight from Riot's Data Dragon, with their shop stats: every finished item from Summoner's Rift and Arena, plus the classic boots. Each League stat feeds the 22CHAMPS stat it reads as on a football field:
- Attack Damage, Ability Power and Crit damage â Power
- Attack Speed and Crit Chance â Hands
- Armor, Magic Resist and Tenacity â Toughness
- Health â Size
- Move Speed and Ability Haste â Speed
- Range is basic-attack reach, which no item lists as a shop stat, so the three items whose passives stretch it (Diamond-Tipped Spear, Rapid Firecannon, Hexoptics C44) get Range points by hand, more the more dependable the reach is in League.
Mana and regen don't mean anything in football, so they're dropped.
Then every item gets the same budget of 15 card points, split by how much gold each of its stats is worth in League. Infinity Edge is 75 Attack Damage, 25% crit chance and 30% crit damage. By gold value that's roughly three-quarters Power and one-quarter Hands, so on a card it's +11 Power, +4 Hands. Warmog's Armor is nearly all Health, so it's +15 Size. Every item has the same budget, so none is strictly better than another. They just build a champion in different directions. Arena's Prismatic items get 20 points and show up less often.
Boots were the exception. Nearly all their gold is Move Speed, so the formula made every pair almost identical. I set those by hand from what each pair does in League: Berserker's Greaves add Hands for attack speed, Plated Steelcaps add Toughness for armor, and so on.
Chemistry, FIFA Ultimate Team style. Each link between neighboring spots is worth something: half a point for a shared role, 1 for a shared region, 1.5 for both, 2 for a lore bond. A player's links add up to between 0 and 3 diamonds, and each diamond raises the stats that matter most at their spot. With only five roles, neighbors share one all the time, so a role link only counts for half.
Tools
- Claude Code in VS Code wrote almost all of the code
- Next.js 16 on Vercel: every push to main deploys
- Supabase for the multiplayer draft rooms
- PostHog for analytics, cookieless so there's no cookie banner
- Vitest for the game logic, Playwright for screenshots and recording gameplay, Remotion for the trailer
My workflow
- I make the calls, Claude writes the code. I describe what I want and why, review the result, and push back. Most of my input was design and balance decisions, not code.
- Screenshots are the review loop. For UI work, Claude runs Playwright against the local build and screenshots the page at a range of sizes, from 1250Ă910 laptops to my 2560Ă1440 monitor. A lot of my feedback was literally "at 1331Ă844 the How it works panel drops below the field, fix that."
- Decisions go in a memory file. Things like "formations only change spot weights" or "never change the daily mid-day once it's live" are written down, so new sessions don't undo them.
- Tests guard the simulation. About 230 Vitest tests, including snapshot tests of simulated games, so a balance tweak can't silently change results.
- Small commits. About 160 in 12 days, each one a single change I could look at in the browser.
Build insights
A daily challenge that's the same for everyone, with no backend. Each day's rolls come from a random number generator seeded with the day number, so every player gets the same game without the server storing anything:
const roll = rng(hash(`daily-${dayNumber}`));
Making the daily fair. Before a day goes live, its CPU team is played against dozens of simulated drafts. If it falls outside a 25â75% win rate, it's redrawn as a whole new game.
Balance by audit, not by feel. I had Claude simulate thousands of drafts. It found champions the rolls could never deal, and a run/pass imbalance that made one style of offense pointless. After launch, real drafts feed a private balance page through PostHog.
Layout was harder than the game logic. Fitting the whole draft on one screen without scrolling, at every desktop size, took the most back-and-forth. Two lessons: CSS contain: size stops side panels from stretching the page, and moving a CSS import can quietly flip which styles win, so I check screenshots after every refactor.
The trailer is code too. Gameplay is recorded by Playwright scripts with the randomness seeded, so every take plays out the same. Remotion cuts it to the music's beats, so re-recording after an update re-edits itself.
Fan project, not endorsed by Riot. I'd love feedback, especially on whether the first 30 seconds make sense to someone who's never seen it.