Semi-random thoughts and tales of tinkering
Until now, we've been hand-building our worlds. But real Minecraft generates entire worlds automatically using math! In this lesson, you'll learn about noise functions — the magical math that creates realistic terrain. 🌍✨
JavaScript has Math.random(), which gives a random number between 0 and 1.
But if we used pure randomness for terrain, we'd get something ugly — blocks jumping
wildly up and down with no smooth hills.
Pure random terrain — way too chaotic! 😵
What we want is smooth randomness — random values that change gradually, creating natural-looking hills and valleys. This is called noise.
Smooth noise terrain — now THAT looks like Minecraft! 🏔️
A noise function takes a position and returns a smooth random value between -1 and 1. Nearby positions get similar values, creating smooth transitions:
// Simple noise using sin waves at different scales
function simpleNoise(x, seed) {
return Math.sin(x * 0.02 + seed) * 0.5
+ Math.sin(x * 0.05 + seed * 2) * 0.3
+ Math.sin(x * 0.1 + seed * 3) * 0.2;
}
A seed is a starting number that determines the entire world. The same seed always creates the same world — that's how Minecraft lets you share world seeds with friends!
const SEED = Math.floor(Math.random() * 999999);
// This seed will always create the same terrain:
// Seed 12345 → always the same hills, caves, trees
// Seed 67890 → completely different, but also always the same
We use noise to calculate a height map — the surface height at each column:
function generateWorld() {
for (let col = 0; col < WORLD_WIDTH; col++) {
// Use noise to get surface height
const surfaceRow = Math.floor(
60 + simpleNoise(col, SEED) * 15
);
for (let row = 0; row < WORLD_HEIGHT; row++) {
if (row < surfaceRow) {
setBlock(col, row, 0); // Air above surface
} else if (row === surfaceRow) {
setBlock(col, row, 1); // Grass at surface
} else if (row < surfaceRow + 4) {
setBlock(col, row, 2); // Dirt layer
} else {
setBlock(col, row, 3); // Stone deep down
}
}
}
}
// After generating terrain, add trees
for (let col = 3; col < WORLD_WIDTH - 3; col++) {
// Use noise to decide where trees go (not every column!)
if (simpleNoise(col * 5, SEED + 100) > 0.3) {
const surfaceRow = findSurface(col); // Find the grass block
if (getBlock(col, surfaceRow) === 1) { // Only on grass
// Trunk (3-5 blocks tall)
const height = 3 + Math.floor(Math.abs(simpleNoise(col * 7, SEED)) * 3);
for (let h = 1; h <= height; h++) {
setBlock(col, surfaceRow - h, 4); // Wood
}
// Leaves canopy
for (let lx = -2; lx <= 2; lx++) {
for (let ly = -2; ly <= 0; ly++) {
if (Math.abs(lx) + Math.abs(ly) <= 2) {
setBlock(col + lx, surfaceRow - height + ly, 5); // Leaves
}
}
}
col += 5; // Space between trees
}
}
}
Ores appear deep underground. Rarer ores appear deeper:
// Coal: appears below surface + 5
// Iron: appears below surface + 15
// Diamond: appears below surface + 30
if (row > surfaceRow + 5 && noise(col * 0.5 + row * 0.7) > 0.7) {
setBlock(col, row, COAL_ORE);
}
if (row > surfaceRow + 15 && noise(col * 0.5 + row * 0.7) > 0.85) {
setBlock(col, row, IRON_ORE);
}
if (row > surfaceRow + 30 && noise(col * 0.5 + row * 0.7) > 1.0) {
setBlock(col, row, DIAMOND_ORE);
}
Click the button to generate a new world with a random seed:
Seed: —
The key insight is layering:
Each layer is called an octave. Real Minecraft uses 4-8 octaves of noise!
* 15 multiplier to make terrain flatter or more extreme