Basic mechanics of experience and levels
At the heart of any progression system is a simple idea: the player performs actions, gains experience, and when enough experience is accumulated, a transition to a new level occurs. Let's start with the simplest version in JavaScript:
class Player {
constructor() {
this.level = 1;
this.experience = 0;
this.experienceToNextLevel = 100;
}
gainExperience(amount) {
this.experience += amount;
while (this.experience >= this.experienceToNextLevel) {
this.levelUp();
}
}
levelUp() {
this.experience -= this.experienceToNextLevel;
this.level++;
this.experienceToNextLevel = Math.floor(this.experienceToNextLevel * 1.5);
console.log(`Congratulations! You have reached ${this.level} level!`);
}
}
const player = new Player();
player.gainExperience(150); // We gain experience and increase the levelPay attention to the formula for calculating experience for the next level. We multiply the current value by 1.5, which creates an exponential growth curve. This is an important point — if each level requires the same amount of experience, the game will quickly become boring.
Various progression curves
There are several popular approaches to calculating the required experience. Linear progression, where each level requires a fixed amount of experience, is suitable for short games. The exponential curve, as in the example above, creates a feeling of gradual complication. The logarithmic curve makes the first levels fast, and the subsequent ones slow down.
Here is an example with different formulas:
class ProgressionSystem {
// Linear progression: 100, 200, 300, 400...
static linear(level, baseXP = 100) {
return baseXP * level;
}
// Exponential: 100, 150, 225, 337.5...
static exponential(level, baseXP = 100, multiplier = 1.5) {
return Math.floor(baseXP * Math.pow(multiplier, level - 1));
}
// Polynomial: 100, 400, 900, 1600...
static polynomial(level, baseXP = 100) {
return baseXP * Math.pow(level, 2);
}
// Hybrid formula (as in Pokemon)
static hybrid(level) {
return Math.floor(1.2 * Math.pow(level, 3));
}
}
// Let's see how the experience requirement grows
for (let level = 1; level <= 5; level++) {
console.log(`Level ${level}:`);
console.log(` Linear: ${ProgressionSystem.linear(level)}`);
console.log(` Exponential: ${ProgressionSystem.exponential(level)}`);
console.log(` Polynomial: ${ProgressionSystem.polynomial(level)}`);
}Which formula to choose?
It depends on your game. For mobile casual games, they often use fast progression at the beginning to capture the player, then slow down the pace. In hardcore RPGs, progression can be slow and demanding from the start.
Adding rewards for the level
Leveling up without rewards is a missed opportunity. Players should feel that their progress means something. Let's add a reward system:
class Player {
constructor() {
this.level = 1;
this.experience = 0;
this.health = 100;
this.attack = 10;
this.defense = 5;
this.skillPoints = 0;
}
levelUp() {
this.level++;
// Increasing the basic characteristics
this.health += 20;
this.attack += 3;
this.defense += 2;
this.skillPoints += 5;
// Every 5th level gives a special reward
if (this.level % 5 === 0) {
this.unlockNewAbility();
}
// Restoring health when leveling up
this.currentHealth = this.health;
console.log(`
🎉 Level ${this.level}!
Health: ${this.health}
Attack: ${this.attack}
Defense: ${this.defense}
Skill points: ${this.skillPoints}
`);
}
unlockNewAbility() {
const abilities = [
'Fireball',
'Ice Arrow',
'Shield of Power',
'Lightning strike'
];
const abilityIndex = Math.floor(this.level / 5) - 1;
if (abilityIndex < abilities.length) {
console.log(`✨ Ability unlocked: ${abilities[abilityIndex]}`);
}
}
}Visualization of progress
It's not enough to just store numbers — players need to see their progress. Let's create a simple progress bar:
class ProgressBar {
static draw(current, required, width = 20) {
const percentage = current / required;
const filledWidth = Math.floor(percentage * width);
const emptyWidth = width - filledWidth;
const filled = '█'.repeat(filledWidth);
const empty = '░'.repeat(emptyWidth);
const percentText = Math.floor(percentage * 100);
return `[${filled}${empty}] ${percentText}% (${current}/${required})`;
}
}
const player = new Player();
console.log('Experience:', ProgressBar.draw(player.experience, player.experienceToNextLevel));
player.gainExperience(75);
console.log('Experience:', ProgressBar.draw(player.experience, player.experienceToNextLevel));For web games, you can use HTML and CSS to create more beautiful progress bars. Here's a quick example:
class UIManager {
static updateExpBar(player) {
const percentage = (player.experience / player.experienceToNextLevel) * 100;
const expBar = document.getElementById('exp-bar');
const expText = document.getElementById('exp-text');
expBar.style.width = `${percentage}%`;
expText.textContent = `${player.experience} / ${player.experienceToNextLevel} XP`;
}
static showLevelUp(player) {
const notification = document.createElement('div');
notification.className = 'level-up-notification';
notification.textContent = `Level ${player.level}!`;
document.body.appendChild(notification);
// Animation of appearance and disappearance
setTimeout(() => notification.classList.add('show'), 10);
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => notification.remove(), 300);
}, 2000);
}
}Prestige and Rebirth System
Many modern games use a prestige system — when a player reaches the maximum level, they can start over, but with bonuses. This adds replayability:
class PrestigeSystem {
constructor() {
this.prestigeLevel = 0;
this.prestigePoints = 0;
}
canPrestige(player) {
return player.level >= 100;
}
prestige(player) {
if (!this.canPrestige(player)) {
console.log('Insufficient level for prestige!');
return false;
}
this.prestigeLevel++;
this.prestigePoints += Math.floor(player.level / 10);
// We reset the player, but keep the bonuses
const expBonus = 1 + (this.prestigeLevel * 0.1); // +10% experience for each prestige
const statBonus = this.prestigeLevel * 5; // +5 to starting stats
player.reset(expBonus, statBonus);
console.log(`
🌟 Prestige ${this.prestigeLevel}!
Experience bonus: +${(expBonus - 1) * 100}%
Bonus to characteristics: +${statBonus}
Prestige points: ${this.prestigePoints}
`);
return true;
}
// Continuous improvements for prestige points
buyPermanentUpgrade(upgrade) {
const upgrades = {
'double_xp': { cost: 10, effect: 'Double experience' },
'start_level_10': { cost: 15, effect: 'Start from level 10' },
'bonus_gold': { cost: 20, effect: '+50% gold' }
};
const selected = upgrades[upgrade];
if (!selected) return false;
if (this.prestigePoints >= selected.cost) {
this.prestigePoints -= selected.cost;
console.log(`Purchased: ${selected.effect}`);
return true;
}
return false;
}
}Balancing progression
The most difficult part is to balance the system correctly. The player should feel the progress, but not too fast. A few tips: the first five levels should be completed quickly to capture the player and show the mechanics. The middle levels are the main content, here the pace should be comfortable. Late levels may require serious effort, which creates a sense of achievement.
It is important to test on real players. What seems balanced to a developer may be too fast or slow for a regular player. Use analytics to track where players get stuck or quit the game.
Here is a simple logging system for analysis:
class Analytics {
static logLevelUp(player, timeSpent) {
const data = {
level: player.level,
timeSpent: timeSpent,
timestamp: new Date().toISOString()
};
// In the real game, we send it to the server
console.log('Analytics:', data);
// Saving locally for analysis
const history = JSON.parse(localStorage.getItem('levelHistory') || '[]');
history.push(data);
localStorage.setItem('levelHistory', JSON.stringify(history));
}
static getAverageLevelTime() {
const history = JSON.parse(localStorage.getItem('levelHistory') || '[]');
if (history.length === 0) return 0;
const total = history.reduce((sum, entry) => sum + entry.timeSpent, 0);
return total / history.length;
}
}Advanced techniques
Modern games often use several parallel progression systems. For example, character level, skill level, combat rating, and seasonal progress. Each system gives the player something new and keeps them engaged at different stages of the game. Daily and weekly goals are also popular, which give additional experience and rewards, encouraging players to return to the game regularly.
Don't forget about feedback. Each player's action should show progress: experience numbers, animations, sound effects, and visual effects when the level increases. All this creates a feeling of satisfaction and motivates you to continue playing.
Conclusion: The system of levels and progression is a whole science that combines programming, game design and player psychology. A properly implemented system keeps players engaged for hours, creating that "one more level" effect. Experiment with formulas, test on real players, and don't be afraid to change the balance after the release.
🎓 Keep learning!
You can explore this topic in greater depth and learn how to develop games, web applications, and much more at Codice — our educational platform with courses for beginner developers.
And we also have a cool Telegram channel with a friendly community where developers discuss code, share experiences, and help each other grow.
