Shaping terrain
A world's shape comes from one number per block, the density. Where it is above 0 the generator places stone, and where it is below 0 it leaves air, or water below sea level. A DensityFunction is the formula for that number, and shaping terrain means writing formulas.
In JSON a formula is a deep tree of add and mul objects. In MMS it is arithmetic, and the editor draws the result as you type. This guide is a set of recipes to combine. For the resources around the terrain (dimension, biome, surface), follow the custom dimension tutorial first.
See what you are doing
Put the cursor on a Noise, a DensityFunction or a NoiseSettings and open the Preview tab of the web editor (in VS Code, click Preview above it). It draws the function as a slice of the world: the raw density, the blocks it makes, or a 3D view. The picture redraws on every edit, so changing a number and watching is the normal way to work.
Every sample on this page is a file of its own. Paste one into a new project to see it.
Ground with a height
Almost every terrain starts from a gradient: solid at the bottom, air at the top.
/** 1 at the bottom of the world, 0 at y 64, -1 at y 192. */
DensityFunction slope = Gradient(
axis = y,
from_coordinate = -64,
to_coordinate = 192,
from_value = 1,
to_value = -1,
)
On its own this is a flat world with its surface at y = 64, where the value crosses 0. Everything else is added to it.
Hills
Noise is a smooth random value between about -1 and 1. Adding it to the slope moves the surface up where the noise is high and down where it is low:
/** Rolling hills, a few hundred blocks across. */
Noise rolling {
base_octave = -7
amplitude_modifiers = [1, 0.5, 0.25]
}
DensityFunction slope = Gradient(
axis = y,
from_coordinate = -64,
to_coordinate = 192,
from_value = 1,
to_value = -1,
)
/** The surface rises and falls about 32 blocks around y 64. */
DensityFunction hills = slope + Noise(rolling, 1, 0) * 0.25
Three numbers do the work:
base_octavesets the size of the shapes. Each step down doubles it:-7gives features about 128 blocks across,-8about 256.amplitude_modifiersadds detail. Each entry is another layer of noise at half the size, with that strength.[1]is smooth, and[1, 0.5, 0.25]has bumps on its bumps.- The multiplier sets the height. The slope changes by 1 over 128 blocks, so
* 0.25moves the surface up to 32 blocks either way.
The arguments after the noise are its scale across (xz_scale) and up (y_scale). A y_scale of 0 makes the noise the same at every height, so the ground has no overhangs. With 1, the noise changes with height too, and hills grow overhangs and floating bits.
Caves
Caves are a second formula that is below 0 where there should be air. Min takes the lower of two values, so a block is solid only when both say so:
Noise cave_noise {
base_octave = -5
amplitude_modifiers = [1, 0.5]
}
DensityFunction slope = Gradient(
axis = y,
from_coordinate = -64,
to_coordinate = 192,
from_value = 1,
to_value = -1,
)
/** Below 0 inside a cave. The y scale of 2 makes caves wider than tall. */
DensityFunction caves = 0.45 - Noise(cave_noise, 1, 2)
DensityFunction carved = Min(slope, caves)
Lower 0.45 for more and bigger caves. Max does the opposite and joins two terrains, such as islands floating above a ground.
A few more helpers cover most shapes:
| Helper | Use it to |
|---|---|
Abs(x) |
turn a noise into sharp ridges, or into thin tunnels along the lines where it is 0 |
Square(x) |
flatten values near 0 |
Clamp(x, min, max) |
keep a value in a range, so that it cannot overpower the rest |
Lerp(alpha, a, b) |
blend from one terrain to another, by a third function |
IntervalSelect(x, [...], [...]) |
switch between terrains by a value, such as one terrain per region |
Hover a helper for what it does. The cave-siege example builds a whole cave world from these, with comments on each step.
Plains and cliffs with a spline
Adding noise gives hills that are equally bumpy everywhere. Real landscapes have flat places and steep ones. A Spline maps a value through a curve you draw, so the same noise can mean "flat" over most of its range and "cliff" in a narrow band:
Noise rolling {
base_octave = -7
amplitude_modifiers = [1, 0.5, 0.25]
}
DensityFunction slope = Gradient(
axis = y,
from_coordinate = -64,
to_coordinate = 192,
from_value = 1,
to_value = -1,
)
/** How far the ground is lifted: plains, a cliff, then a plateau. */
DensityFunction lift = Spline({
coordinate = Noise(rolling, 1, 0)
points = [
SplinePoint(-1, 0, -0.1),
SplinePoint(0.1, 0, 0),
SplinePoint(0.3, 0, 0.35),
SplinePoint(1, 0, 0.4),
]
})
DensityFunction plateaus = slope + lift
Each SplinePoint is a location on the coordinate, the slope of the curve there, and the value. Between noise values of 0.1 and 0.3 the lift jumps from 0 to 0.35, which is a cliff about 45 blocks high. Everywhere else it barely changes.
Typing the numbers is the hard way. With the cursor in the spline, the Preview tab shows the curve: drag a point to move it, drag its handle to change the slope, double-click to add a point and press Delete to remove one. Each drag rewrites the numbers in the source. In VS Code, the Edit spline lens above Spline( opens the same editor. Splines has the details, including splines inside splines, which is how vanilla mixes continents, erosion and ridges.
Keep the world closed
A world with caves needs a floor, or players fall out of it. Blend towards solid near the bottom:
DensityFunction terrain = Gradient(axis = y, from_coordinate = -64, to_coordinate = 192, from_value = 1, to_value = -1)
/** 0 at the bottom of the world, 1 from y -48 up. */
DensityFunction floor_fade = Gradient(
axis = y,
from_coordinate = -64,
to_coordinate = -48,
from_value = 0,
to_value = 1,
)
/** Solid at the bottom, the terrain from y -48 up. */
DensityFunction closed = Lerp(floor_fade, 1, terrain)
The fade starts at the bottom of the world, which is -64 here. Use your own dimension's min_y: in a world that starts at y = 0, this fade would be 1 everywhere and do nothing. The same trick at the top, blending towards -1, keeps terrain from being cut off flat at the height limit.
Use it in a world
The terrain is the final_density of a NoiseSettings, which a Dimension names as its generator with Noise(settings = hills, ...). The sample below uses the flat slope, to stay short. Put the name of your own formula in its place, and keep noise in step with the dimension type's min_y and height:
DensityFunction terrain = Gradient(axis = y, from_coordinate = -64, to_coordinate = 192, from_value = 1, to_value = -1)
NoiseSettings hills {
sea_level = 48
default_block = stone
default_fluid = water
disable_mob_generation = false
legacy_random_source = false
noise = { min_y = -64, height = 384 }
material_rule = Block(stone)
spawn_target = []
noise_router = {
final_density = Interpolated(terrain, 4, 8)
temperature = 0
vegetation = 0
continents = 0
erosion = 0
depth = 0
ridges = 0
chunk_surface_level = 0
}
}
Interpolated(terrain, 4, 8) works the formula out once per 4×8×4 cell and blends between, as vanilla does. It is much faster and smooths out single-block noise. The preview of the NoiseSettings shows the finished blocks, with water and the surface rule applied.
Give each step a name, as the samples here do. A named DensityFunction can be previewed on its own, which is how you find the step that went wrong, and its doc comment says what its numbers mean.
Try it in the game
mms build --world ~/.minecraft/saves/MyWorld --reset-dimensions
The game never generates a chunk twice, so a change to the terrain only shows in new chunks. --reset-dimensions deletes the saved chunks of the project's dimensions whose worldgen changed, so the same place generates again with the new shape. Close the world first. Seeing worldgen changes explains what is reset and how to get the old chunks back.
Borrow from vanilla
Vanilla's own density functions and noises can be used by id, with no declaration: overworld/continents, overworld/erosion, or the noise ridge. Go to definition on one opens the game's JSON for it. To study a whole vanilla dimension as MMS, run mms import on the vanilla data pack.
The sky-islands, badlands and cave-siege examples are complete terrains to take apart.