docsGamification System

Gamification System

Watermelon uses a dual-loop progression system to keep users engaged: XP-driven level progression and rank-tier prestige.


XP System

Earning XP

ActionXPNote
Play a song+5Minimum 30s listen
Complete a song+15Listen to >80% of track
Daily login streak+20Multiplied by streak multiplier
Create a playlist+25Per new playlist
Discover new artist+10Artist not played before
Maintain streak (3d)×1.53-consecutive-day bonus
Maintain streak (7d)×2.0Weekly bonus
Maintain streak (14d)×2.5Fortnight bonus
Maintain streak (30d)×3.0Monthly bonus

Level Formula

XP Level = GREATEST(1, FLOOR(SQRT(xp_total / 100.0)))
LevelXP RequiredMultiplier
101.0×
21001.1×
34001.2×
52,5001.4×
1010,0001.9×
2040,0002.9×
3090,0003.9×

The multiplier visually reinforces progression even at low levels.

XP Descriptor

fun xpDescriptor(xp: Int): String = when (xp) {
    0 -> "Fresh listener ✨"
    in 1..(100 / 2) -> "Gaining momentum 🔁"
    in (100 / 2)..(100 * 2) -> "Solid start 👊"
    in (100 * 2)..(100 * 5) -> "Groove locked ✨"
    in (100 * 5)..(100 * 10) -> "Addicted 🎶"
    else -> "Music is life 💖"
}

Rank Tiers

Rank is computed automatically from hours listened via PostgreSQL triggers.

HoursTier NameEmoji
0Seed Listener🌱
5Sprout Wave🍃
15Pulse Rider🎧
35Echo Drift🌊
60Resonance🎶
100Vinyl Hunter📀
160Frequency Soul🎵
250NovaBeat🌌
350Harmonic Flow💿
500Reverb X🔥
700Soundrift
950Celestia Tone🌠
1200Wave Architect🎼
1600Spectrum Lord🌈
2000Eternal Echo👑

Achievement Badges

Stored in achievements table and unlocked via triggers on user_actions and play_sessions.

Unlock Rules (trigger-driven)

BadgeTrigger Condition
🎵 First ListenFirst PLAY action
🦉 Night OwlPlay between 02:00–05:00
🧭 Explorer10 unique artists in history
📋 Playlist ProCreate 5 playlists
🐦 Early BirdPlay before 07:00
🏃 Marathonerhours_listened >= 50

Badge Display

// ProfileScreen.kt — vertical card with emoji + description
Card(
    modifier = Modifier.size(170.dp, 120.dp),
    colors = CardDefaults.cardColors(
        containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.6f)
    )
) {
    Column(horizontalAlignment = Alignment.CenterHorizontally) {
        Text(badge.emoji, fontSize = 32.sp)
        Text(badge.name, fontWeight = FontWeight.Bold)
        Text(badge.description, fontSize = 11.sp)
    }
}

Streak System

  • Streak = consecutive days with at least one play session
  • Tracked in profiles.streak_days
  • Daily notification: “Your streak is at 3 days 🔥“

Leaderboard

Updated hourly via cron or trigger:

SELECT
    id,
    display_name,
    email,
    hours_listened,
    songs_played,
    rank_tier,
    xp_level
FROM profiles
WHERE is_banned = false
ORDER BY hours_listened DESC
LIMIT 50;

Live at: /leaderboard


Profile Screen

The profile screen surfaces all gamification data:

  • Header — Avatar (DiceBear), name, rank badge
  • Stats Grid — Rank, Level, Hours, Songs, Multiplier
  • Streak — Current / longest streak
  • Achievements — Horizontal scrolling badge cards
  • XPSection — Progress bar to next level with descriptor

For the SQL migration scripts, see the Database Schema guide.