Game Dev 101
Progress
0/22

Lesson 17 of 22, in world 6: Gameplay Systems

Randomness & Procedural Generation

Time
About 20 min
Builds on
Tilemaps

Randomness keeps games fresh: loot drops, enemy spawns, critical hits, and entire levels in roguelikes and Minecraft. But “random” in games is a design tool, and raw Math.random() is rarely what you want unshaped.

Seeded randomness

A pseudo-random number generator (PRNG) produces a sequence that looks random but is fully determined by its seed. Same seed → same sequence → same level. That gives you shareable level codes (“seed 1337”), daily challenges where everyone plays the same map, reproducible bug reports, and replays.

JavaScript
const rng = seeded(1337)     // a tiny PRNG such as mulberry32
rng() // 0.2147…  always the same first number for seed 1337
rng() // 0.8836…

Procedural generation

Procedural content generation (PCG) builds content with algorithms. The classic recipe: start with noise, then apply rules to shape it into something meaningful.

LiveCellular automata caves

Step 0 is pure random noise. Each smoothing step applies one rule — 'become a wall if most of your neighbours are walls' — and blobs of cave emerge. Same seed → same cave, every time. Change the seed for a new level.

JavaScript
for (let step = 0; step < STEPS; step++)
  grid = grid.map((row, y) => row.map((cell, x) => {
    const walls = countWallNeighbours(grid, x, y)    // 0..8
    return walls >= 5 ? WALL : walls <= 3 ? FLOOR : cell
  }))
LiveWhite noise vs. smooth noise
Height from

Math.random() for every column gives jagged nonsense. Smooth (value) noise picks random heights only every N pixels and blends between them — that's the idea behind Perlin/simplex noise used for terrain, clouds and caves. Layering two scales adds detail.

Shaping randomness for fun

  • Weighted tables: common 70%, rare 25%, legendary 5%.
  • Shuffle bags: put items in a bag, shuffle, draw until empty, refill. Tetris does this so you never wait 30 pieces for a line piece.
  • Pity timers: increase the chance after each failure, so bad luck streaks have a limit.
  • Random ranges, not raw random: speed = 100 + rng() * 20 adds variety without chaos.
JavaScript
function weightedPick(table, rng) {     // [{ item: 'sword', weight: 5 }, …]
  let r = rng() * table.reduce((sum, e) => sum + e.weight, 0)
  for (const e of table) if ((r -= e.weight) < 0) return e.item
}

Key takeaways

  • Seeded PRNGs make randomness reproducible and shareable.
  • Procedural generation = noise + rules + clean-up.
  • Smooth noise for natural shapes; weighted tables and shuffle bags for fair-feeling drops.

Check yourself

Q1. Two players enter the same seed. What do they get?

Q2. In the cave demo, what do smoothing steps do?

Q3. Tetris guarantees each piece appears once every 7 draws. That is a…

Exercises

  1. Set smoothing steps to 0, then increase one at a time. At which step does it start looking like a cave?
  2. Write a shuffle-bag function for loot: bag(items) returns a draw() function.
  3. How would you guarantee the player’s start and the exit are connected in a generated cave? (Hint: flood fill, lesson 18.)