XP & Ranks
XP is awarded at match end via fireMatchRewards(). The reward system uses an event ledger (rewardEvents table) to prevent double-awarding if the function is called more than once for the same match.
XP Awarded Per Match
| Outcome | Base XP | Bonuses |
|---|---|---|
| Win | 100 | See below |
| Loss | 20 | None |
Bonuses apply per valid band the winner played in the match:
| Bonus | Condition | XP |
|---|---|---|
| Long band name | Band name length ≥ 15 characters | +10 |
| Unknown band | followerCount < 10 000 | +20 |
| Discovery band | First time this player has played this band across all games | +5 |
Example
Winner played 3 bands:
- "Radiohead" (9 chars, 5M followers, played before) → +0 bonuses
- "Tame Impala" (11 chars, 3M followers, first time) → +5 discovery
- "The Microphones" (15 chars, 8 500 followers, first time) → +10 long + +20 unknown + +5 discovery = +35
Total: 100 (base) + 0 + 5 + 35 = 140 XP
Singleplayer
- Singleplayer flag is passed to the XP service
- Loss XP is not awarded when there is no loser (bot has no XP)
Level Formula
XP required to reach level n:
text
xpForLevel(n) = (n × (n - 1) / 2) × 150| Level | Total XP needed |
|---|---|
| 1 | 0 |
| 2 | 150 |
| 3 | 450 |
| 5 | 1 500 |
| 10 | 6 750 |
| 20 | 28 500 |
| 50 | 183 750 |
Rank Progression
12 named ranks tied to level thresholds:
| Level | Rank |
|---|---|
| 1 | Garage Drummer |
| 2 | Open Mic Opener |
| 3 | Local Support Act |
| 5 | Signed to an Indie Label |
| 8 | Regional Headliner |
| 10 | National Tour |
| 15 | Festival Stage |
| 20 | Gold Record |
| 25 | Platinum Artist |
| 30 | Arena Filler |
| 40 | Hall of Fame Inductee |
| 50 | Rock Legend |
Ranks are configurable via the admin /config/rewards endpoint.
REST Endpoint
GET /xp/me — requires Clerk JWT
json
{
"totalXp": 340,
"level": 3,
"rankName": "Local Support Act",
"currentLevelXp": 40,
"xpNeededForNextLevel": 110
}Admin Config
XP values are configurable via the admin panel (PUT /config/rewards):
typescript
{
baseWinXp: 100,
baseLossXp: 20,
bonuses: {
longBandName: { minLength: 15, xp: 10 },
unknownBand: { maxFollowers: 10000, xp: 20 },
discoveryBand: { xp: 5 }
},
levelMultiplier: 150,
ranks: [ { level: 1, name: "Garage Drummer" }, ... ]
}Changes take effect immediately (loaded into memory on update).