Generation
The generation described in this section will explain how Minecraft Beta 1.7.3 does its Terrain Generation. A lot of this will be based on educated guesses and the decompilation provided by RetroMCP. Additionally, certain historical bits and pieces will be taken from the Minecraft Wiki.
The seed used throughout all of these comparisons is
3257840388504953787, the seed behindpack.png.
In any visualizations, the shown chunks range from
-1,-1to1,1. Additionally, up is always towards Negative Z, while right is always towards Positive X.
History
The first version to have the Beta-era generator was Alpha 1.2.0, and it was used up until Beta 1.7.3.
However, we can see that the general shape of the terrain remains the same by using the same seed and spawn location in Alpha 1.1.2 (pre-rework), Alpha 1.2.3 (post-rework) and Beta 1.7.3.
| A1.1.2 | A1.2.3 |
|---|---|
![]() | ![]() |
This similar pre-1.2.0 generation dates all the way back to Infdev 20100624, before which the world generation looked entirely different.
| Inf20100624 | A1.1.2 |
|---|---|
![]() | ![]() |
Facing the other way reveals some of the more glaring differences between the generators.
| A1.1.2 | A1.2.3 |
|---|---|
![]() | ![]() |
Between Alpha 1.2.3 and Beta 1.7.3 there only exist minor differences, mainly relating to the feature generation.
| A1.2.3 | B1.7.3 |
|---|---|
![]() | ![]() |
Biomes
To find out more about what Biomes exist and how they’re defined, check out the Biomes page.
This process utilizes 3 octaved Simplex Noise generators called Temperature, Humidity and Variation. This process is described here.
| Temperature | Humidity | Variation |
|---|---|---|
![]() | ![]() | ![]() |
Temperature, humidity and variation values after being modified by this function. These values are in the 0.0 - 2.0 range.
With this a 16x16 Biome Array is generated, where any block column can contain any of the 13 Biomes.
| Foliage Colors | Map Colors |
|---|---|
![]() | ![]() |
Biomes colored as per the biomes page.
Terrain Shape
Noise Octaves
The Beta 1.7.3 Terrain Generator has a shared Pseudorandom Number Generator, alongside 8 Octaved Perlin Noise Generators, 7 of which are relevant for World generation.
| Generator | Octaves | Scale |
|---|---|---|
| Low Noise | 16 | (684.412, 684.412, 684.412) |
| High Noise | 16 | (684.412, 684.412, 684.412) |
| Selector Noise | 8 | (684.412 / 80.0, 684.412 / 160.0, 684.412 / 80.0) |
| Continentalness | 10 | (1.121, 1.121, 0.5) |
| Depth Noise | 16 | (200.0, 200.0, 0.5) |
| Tree Density Noise | 8 | (variable) |
| Low | High | Selector | Continental | Depth |
|---|---|---|---|---|
![]() | ![]() | ![]() | ![]() | ![]() |
Colors adjusted be more visible.
Continentalness and Depth are 2D, while Low, High and Selector noise are 3D. As a result, only the lowest slice of each is shown. Tree Density is 2D as well, but only used during the population stage for trees.
Terrain Noise
The exact algorithm is a bit annoying to explain and feels very vibes-based. A simplified explanation boils down to the following process.
The following code is iterated over along the X,Z axes via a set of for loops. It’s responsible for sampling the 2D noises.
// Get temperature values
double temp = temperature[];
double humi = humidity[] * temp;
humi = 1.0 - humi;
humi *= humi;
humi *= humi;
humi = 1.0 - humi;
// Sample continentalness noise
double cn = (continentalness[xz] + 256.0) / 512.0;
cn *= humi;
if (cn > 1.0)
cn = 1.0;
if (cn < 0.0)
cn = 0.0;
cn += 0.5;
// Sample depth noise
double dn = depthNoise[xz] / 8000.0;
if (dn < 0.0)
dn = -dn * 0.3;
dn = dn * 3.0 - 2.0;
if (dn < 0.0) {
dn /= 2.0;
if (dn < -1.0)
dn = -1.0;
dn /= 1.4;
dn /= 2.0;
cn = 0.0;
} else {
if (dn > 1.0)
dn = 1.0;
dn /= 8.0;
}
dn = dn * double(max.y) / 16.0;
double elevationOffset = double(max.y) / 2.0 + dn * 4.0;
++xz;
After this follow the 3D noises. These are iterated over bottom to top.
double terrainDensity = 0.0;
double densityOffset = (double(y) - elevationOffset) * 12.0 / cn;
if (densityOffset < 0.0)
densityOffset *= 4.0;
// Sample the noises
double ln = lowNoise[xyz] / 512.0;
double hn = highNoise[xyz] / 512.0;
double sn = (selectorNoise[xyz] / 10.0 + 1.0) / 2.0;
// Lerp between low and high noise
if (sn < 0.0)
terrainDensity = ln;
else if (sn > 1.0)
terrainDensity = hn;
else
terrainDensity = ln + (hn - ln) * sn;
terrainDensity -= densityOffset;
// Reduce terrain density towards the top of the world
if (y > max.y - 4) {
double heightEdgeFade = double(float(y - (max.y - 4)) / 3.0f);
terrainDensity = (terrainDensity * (1.0 - heightEdgeFace)) + (-10.0 * heightEdgeFace);
}
terrainMap[xyz] = terrainDensity;
xyz++;
For more details, check out the implementation used by BetrockServer.
The result of this is placed into a 4x16x4 Double Array that describes our terrain at a reduced scale.
Interpolation
Minecraft uses Trillinear interpolation to interpolate between the generated density values, to scale them up to the final 16x128x16 chunk size.
The actual decision which blocks are solid or not comes down to the following function.
uint8_t blockType = BLOCK_AIR;
// If water is too cold, turn it into ice
double temp = temperature[columnIndex];
if(y < WATER_LEVEL) {
if(temp < 0.5 && y >= WATER_LEVEL - 1) {
blockType = BLOCK_ICE;
} else {
blockType = BLOCK_WATER_STILL;
}
}
// Decide which blocks are solid or not
if(noiseValue > 0.0) blockType = BLOCK_STONE;
// Write to chunk
blocks[blockIndex].type = blockType;
Some of the values appear to modify themselves for the next loop.
| Terrain | Terrain (water) |
|---|---|
![]() | ![]() |
Highest stone blocks. Color values have been tweaked to be more readable. Blocks at or below water level are marked in blue.
Biome Blocks
After the terrain shape has been generated, the chunk is transformed further by the Sand, Gravel and Stone Perlin Noise Generators.
| Generator | Octaves | Scale |
|---|---|---|
| Sand & Gravel Noise (Sand) | 4 | (1/32, 1/32, 1.0) |
| Sand & Gravel Noise (Gravel) | 4 | (1/32, 1.0, 1/32) |
| Stone Noise | 4 | (1/16, 1/16, 1/16) |
| Sand | Gravel | Stone |
|---|---|---|
![]() | ![]() | ![]() |
Sand, Gravel and Stone noise. Color values have been tweaked to be more readable.
The chunk is now iterated over in a nested x and z for-loop.
- The biome for the specified column is read
- Its checked if sand is possible to be placed here
- Its checked if gravel is possible to be placed here
- An integer is generated for if stone can be placed here
- The biomes appropriate top and filler block is chosen (e.g. Grass and Dirt)
For this the current block column is iterated over from top to bottom, from 127 down to 0.
// Place Bedrock at bottom with some randomness
if (y <= 0 + rand.nextInt(5)) {
SetBlock(BLOCK_BEDROCK, position);
continue;
}
int8_t currentBlock = GetBlock(position);
// Ignore air
if (currentBlock == BLOCK_AIR) {
stoneDepth = -1;
continue;
}
// If we counter stone, start replacing it
if (currentBlock == BLOCK_STONE) {
if (stoneDepth == -1) {
if (stoneActive <= 0) {
topBlock = BLOCK_AIR;
fillerBlock = BLOCK_STONE;
} else if (y >= WATER_LEVEL - 4 && y <= WATER_LEVEL + 1) {
// If we're close to the water level, apply gravel and sand
topBlock = GetTopBlock(biome);
fillerBlock = GetFillerBlock(biome);
if (gravelActive)
topBlock = BLOCK_AIR;
if (gravelActive)
fillerBlock = BLOCK_GRAVEL;
if (sandActive)
topBlock = BLOCK_SAND;
if (sandActive)
fillerBlock = BLOCK_SAND;
}
// Add water if we're below water level
if (y < WATER_LEVEL && topBlock == BLOCK_AIR) {
topBlock = BLOCK_WATER_STILL;
}
stoneDepth = stoneActive;
if (y >= WATER_LEVEL - 1) {
SetBlock(topBlock, position);
} else {
SetBlock(fillerBlock, position);
}
} else if (stoneDepth > 0) {
--stoneDepth;
SetBlock(fillerBlock, position);
if (stoneDepth == 0 && fillerBlock == BLOCK_SAND) {
stoneDepth = rand.nextInt(4);
fillerBlock = BLOCK_SANDSTONE;
}
}
}
Caves
Caves are not noise based, but instead work by carving the terrain out between a random amount of nodes. It gets its own dedicated PRNG object when initialized.
Setup
The world seed and the relevant chunk position are used to determine the seed of the cave.
int carveExtent = 8;
// Use the world seed to init the PRNG
rand->setSeed(world->seed);
long xOffset = rand->nextLong() / 2 * 2 + 1;
long zOffset = rand->nextLong() / 2 * 2 + 1;
// Iterate beyond the current chunk by 8 chunks in every direction
for (int cXoffset = cX - carveExtent; cXoffset <= cX + carveExtent; ++cXoffset) {
for (int cZoffset = cZ - carveExtent; cZoffset <= cZ + carveExtent; ++cZoffset) {
rand.setSeed(((long(cXoffset) * xOffset) + (long(cZoffset) * zOffset)) ^ world->seed);
this->GenerateCaves(cXoffset, cZoffset, cX, cZ, c);
}
}
From here, a number in the range of 0 - 42 is generated, by feeding the generation result into itself 3 times and adding 1 after each result.
int numberOfCaves = rand.nextInt(rand.nextInt(rand.nextInt(40) + 1) + 1);
This number is reset to 0 if the next random number between 0 and 15 isn’t equal to 0.
Carving
The resulting number of caves are then iterated over.
double xOffset = double(cXoffset * CHUNK_WIDTH_X + rand.nextInt(CHUNK_WIDTH_X));
double yOffset = double(rand.nextInt(rand.nextInt(120) + 8));
double zOffset = double(cZoffset * CHUNK_WIDTH_Z + rand.nextInt(CHUNK_WIDTH_Z));
int numberOfNodes = 1;
if(rand.nextInt(4) == 0) {
this->CarveCave(cX, cZ, c, xOffset, yOffset, zOffset);
numberOfNodes += rand.nextInt(4);
}
The resulting number of nodes is then iterated over, while still inside of the cave loop.
float carveYaw = rand.nextFloat() * float(M_PI) * 2.0F;
float carvePitch = (rand.nextFloat() - 0.5F) * 2.0F / 8.0F;
float tunnelRadius = rand.nextFloat() * 2.0F + rand.nextFloat();
this->CarveCave(
cX, cZ, c,
xOffset, yOffset, zOffset,
tunnelRadius, carveYaw, carvePitch,
0, 0, 1.0D
);
This algorithm is difficult to not express as just code, and I hate that.
After this, the lighting is updated and the heightmap is generated.
Heightmap
The initial heightmap generation boils down to iterating through every xz coordinate, from the top to the bottom, and stopping if any block with an Opacity that isn’t 0 is run into. Additionally, it keeps track of the lowest block height it has encountered up to that point.
Population
Continue onto the population page to find out more.




















