Fair isn't a fact. It's a feeling.
Players are not statisticians, and they shouldn't have to be. People are wired to see patterns in noise: three misses in a row "feels" rigged, and a coin that lands heads four times "must" land tails next. Real randomness is streaky, and streaks feel unfair, even when the math is perfectly honest.
So games lie. Not to cheat you, but to make the experience match what a fair game should feel like. As a systems designer, a surprising amount of my job is deciding which lies to tell, how big they should be, and how to keep them invisible.
Sid Meier told one of the best-known versions of this story in his 2010 GDC keynote. Playtesters of Civilization Revolution were furious when a unit with 3-to-1 odds lost a battle. The math was correct, since a 75% chance loses one time in four, but players felt cheated. The team bent the odds toward what players expected, because a game that's technically fair but feels rigged has failed at fairness.
The goal of a fair system isn't to be fair. It's to be believed.
Lie #1: Randomness with a memory
The most common lie is randomness that remembers. True random dice have no memory: a 25% critical hit chance is 25% every swing, no matter how many swings in a row have missed. Many games swap that for a pseudo-random distribution (PRD), where every miss quietly raises your odds and every hit resets them. Warcraft III and Dota 2 use this for critical strikes and bashes.
The trick is tuning the starting chance so the long-run average still matches the advertised number. For a 25% crit, the chance starts around 8.5% and climbs by that much after every miss. You still crit about one time in four, but the streaks disappear: a drought can never be longer than 11 misses, and you'll rarely see two crits back to back.
Try it: 40 attacks at a 25% crit chance
Gold squares are crits. Same average, very different feel.
True random
Pseudo-random (PRD)
In code, the whole system is a counter:
// The chance grows with every miss and resets on a hit.
// C is solved so the long-run average still equals the advertised 25%.
const float PrdC = 0.0847f;
int AttacksSinceCrit = 0;
bool RollCrit()
{
AttacksSinceCrit++;
if (FMath::FRand() < PrdC * AttacksSinceCrit)
{
AttacksSinceCrit = 0;
return true;
}
return false;
}
The same idea shows up all over games once you know to look for it:
- Bag randomizers. Modern Tetris deals pieces from a shuffled bag of all seven shapes, so you can never go long without the piece you're waiting for.
- Pity timers. Card and loot games guarantee a top-rarity drop within a set number of openings. Hearthstone has long done this for Legendary cards.
- Streak breakers. Strategy games like XCOM have been known to quietly boost your aim after repeated misses on easier difficulties.
Lie #2: Forgiveness windows
Have you ever jumped a split second after running off a ledge and still made it? That's coyote time, named after the cartoon coyote who hangs in the air until he looks down. The game lets you jump for a few frames after you've technically left the ground, because your brain pressed the button when it saw the edge, not when your feet left it.
Its twin is input buffering: press jump slightly before you land and the game remembers, then jumps the instant you touch down. Celeste's developers have described a whole toolkit of these, and they're a big part of why its brutally hard platforming still feels fair.
const float CoyoteTime = 0.10f; // jump still allowed this long after leaving a ledge
const float JumpBuffer = 0.12f; // a press this early still counts on landing
float TimeSinceGrounded = 0.f;
float TimeSinceJumpPressed = 1.f;
void OnJumpPressed() { TimeSinceJumpPressed = 0.f; }
void Tick(float DeltaTime)
{
TimeSinceGrounded = IsGrounded() ? 0.f : TimeSinceGrounded + DeltaTime;
TimeSinceJumpPressed += DeltaTime;
if (TimeSinceJumpPressed <= JumpBuffer && TimeSinceGrounded <= CoyoteTime)
{
Jump();
TimeSinceJumpPressed = JumpBuffer + 1.f; // consume the press
TimeSinceGrounded = CoyoteTime + 1.f; // no second jump from the same ledge
}
}
Players never notice these windows. They only notice when a game doesn't have them, and the controls suddenly feel "stiff" or "unresponsive" without anyone being able to say why.
In practice: Grimhook
I built this kind of forgiveness throughout Grimhook's movement. The grapple had aim assist: you didn't have to be looking directly at a grapple point to latch onto it, because a forgiveness window caught near-misses and connected you to the point you were clearly going for. Jumping and dashing had their own windows as you approached a grapple point, so chaining moves together didn't depend on frame-perfect timing.
Wall running got the same treatment. Just before you actually made contact, the player was magnetized to the wall, so a slightly off-angle approach still became a clean run instead of a slide into the abyss. In a game built around speed, those windows are the difference between feeling fast and fighting the controls.
Lie #3: The last sliver of health
The most dramatic lies happen at the edge of failure. In a 2017 thread started by designer Jennifer Scheurle, developers shared some of their favorite hidden mechanics. Assassin's Creed and DOOM give the last part of the health bar more weight than it shows, so you survive longer than the bar suggests. In System Shock, your final bullet in a clip does extra damage.
BioShock creator Ken Levine has said that enemies' first shots are designed to miss, warning you of a threat before it hurts you (its AI designer later added that the real rule is a little more nuanced). Players remember the fight they survived by a hair, not the hidden buffer that let them.
I saw the flip side of this in my UX research on DOOM Eternal. Players dying at low health often said, "I didn't even realize I was that low until I died." The health system was honest, but the player's perception of it wasn't keeping up. Fairness isn't only about the numbers; it's about whether the player understood the danger in time to act on it.
Designing your own honest lies
For developers, here's the checklist I use before adding a hidden rule:
- Find where players blame the game. Playtest recordings and telemetry show the moments players call "cheap" or "rigged." Those are the only places a lie belongs.
- Lie in the player's favor. A good lie gives the player the outcome they intended. It never takes away an outcome they earned.
- Keep it small enough to be invisible. If players can feel the system working, it stops being forgiveness and starts being a crutch, or worse, a tell they'll exploit.
- Keep the average honest. PRD still crits 25% of the time. Advertised numbers should stay true over the long run, even when the short run is shaped.
- Tune it with data. Track how often the lie triggers. A coyote window that saves 2% of jumps is polish; one that saves 40% is hiding a level design problem.
Where the lie stops being honest
The same tools that make a game feel fair can also be pointed at the player instead of working for them. A hidden rule that nudges people toward choices they wouldn't otherwise make uses the exact same math as coyote time. The difference is who the lie serves.
The test I keep coming back to is simple: if players found out about this rule, would they thank you or feel betrayed? Nobody is angry to learn about coyote time. That's an honest lie.
Good systems design isn't honest about the math. It's honest about the experience.