Skip to content

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

OutcomeBase XPBonuses
Win100See below
Loss20None

Bonuses apply per valid band the winner played in the match:

BonusConditionXP
Long band nameBand name length ≥ 15 characters+10
Unknown bandfollowerCount < 10 000+20
Discovery bandFirst 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
LevelTotal XP needed
10
2150
3450
51 500
106 750
2028 500
50183 750

Rank Progression

12 named ranks tied to level thresholds:

LevelRank
1Garage Drummer
2Open Mic Opener
3Local Support Act
5Signed to an Indie Label
8Regional Headliner
10National Tour
15Festival Stage
20Gold Record
25Platinum Artist
30Arena Filler
40Hall of Fame Inductee
50Rock 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).