Loot Table Calculator: Drop Rates & Pity Timers

simulator beginner ~8 min
Loading simulation...
Legendary: 1% — expect 1 per 100 drops

With weights 60/25/10/4/1, legendary items have a 1% drop rate. On average, a player needs 100 drops for one legendary, but 5% of players will go 299+ drops without one — the long tail of geometric probability.

Formula

Drop probability = item_weight / total_weight
Expected drops for item = total_weight / item_weight
P(no drop in N tries) = Math.pow(1 - probability, N)
95th percentile dry streak = Math.ceil(Math.log(0.05) / Math.log(1 - probability))

The Mathematics of Treasure

Every time a monster dies in Diablo, a chest opens in Genshin Impact, or a card pack is cracked in Hearthstone, a weighted random number generator consults a loot table. These tables are simple in concept — each item has a weight, and the probability equals its weight divided by the total — but their psychological effects are profound. The variable ratio reinforcement schedule they create is the same mechanism that makes slot machines addictive.

Weighted Probability Tables

A typical loot table assigns weights like 60 (common), 25 (uncommon), 10 (rare), 4 (epic), and 1 (legendary). With total weight 100, a legendary has a 1% drop chance per kill. This means on average you need 100 kills for one legendary — but 'on average' hides enormous variance. Some players get lucky on their first kill; others go 300+ kills without one. This simulator runs thousands of drops so you can see the actual distribution.

The Pity Timer Solution

Pure random loot creates a frustration problem: geometric probability has a long tail. With a 1% drop rate, about 5% of players will go 300+ drops without a legendary — enough to make them quit. Modern games solve this with pity timers: guaranteed drops after N attempts. Genshin Impact's system increases the base 0.6% rate dramatically after 73 pulls, reaching near-certainty at 90. This caps the worst-case experience while preserving the excitement of random drops.

Designing Reward Schedules

Great loot design balances three forces: anticipation (the excitement of possible rare drops), satisfaction (the dopamine hit when one actually appears), and fairness (ensuring no player is punished by extreme bad luck). The weights you choose determine the average cadence of rewards; the pity timer determines the maximum drought. Adjust the sliders to see how these parameters interact — and why game designers obsess over these numbers.

FAQ

How do loot tables work in video games?

Loot tables assign weights to items of different rarities. When a drop occurs, the game generates a random number and maps it to the weighted table. A common item with weight 60 out of total weight 100 has a 60% chance. These tables are the mathematical backbone of Diablo, Destiny, Genshin Impact, and every RPG with randomized loot.

What is a pity timer in gacha games?

A pity timer guarantees a rare drop after a certain number of unsuccessful attempts. Genshin Impact guarantees a 5-star character every 90 wishes, with increasing probability after 75. This prevents the worst-case scenario where unlucky players never receive rare items despite spending extensively.

Why do games use weighted random instead of equal probability?

Rarity creates value perception. If all items dropped equally, nothing would feel special. The dopamine response to a rare drop depends on it being unexpected. Game designers carefully tune weights to create the 'almost got it' feeling that drives continued engagement — a direct application of variable ratio reinforcement schedules.

How do you calculate the chance of NOT getting a legendary in N tries?

The probability of missing a legendary in N drops is (1 - p)^N, where p is the drop rate. For a 1% drop rate over 100 drops: (0.99)^100 ≈ 36.6% chance of getting zero legendaries. For 299 drops: (0.99)^299 ≈ 5% — meaning 5% of players will have this 'dry streak' or worse.

Sources

Embed

<iframe src="https://homo-deus.com/lab/game-design/loot-tables/embed" width="100%" height="400" frameborder="0"></iframe>
View source on GitHub