Noise & Terrain
Noise, density functions and noise settings are the three layers that shape Minecraft terrain. They build on each other in that order:
- Noise definitions (
worldgen/noise) - raw Perlin noise, described by octaves and amplitudes. - Density functions (
worldgen/density_function) - composable math nodes that sample noises and combine them into a density value for any 3D position. - Noise settings (
worldgen/noise_settings) - the terrain configuration a dimension points at: world bounds, default blocks, the noise router wiring density functions to generation roles, and the material rules painting the top layers.
Minecraft decides whether a position is solid from the density value the router's finalDensity produces: positive density is a block, negative density is air (or fluid, below sea level).
References: Noise, Density function, Noise settings, misode's noise, density function and noise settings generators
Noise Definitions
A noise definition configures one Perlin noise instance. Octaves layer noise samples at different scales - low octaves make large features (continents), high octaves add fine detail (small bumps).
baseOctave = -7 starts at the 2⁷ = 128 block scale. Each following octave doubles in frequency, and amplitudeModifiers weights each one, usually decreasing so detail never overwhelms the base shape. octaveCount is the number of octaves, which the game requires to match the number of amplitudeModifiers: left unset, it's written as that number.
amplitudeModifiers(...) is the vararg form of the same list:
Only baseOctave is always written. Leaving amplitudeModifiers out gives every octave a modifier of 1.0, so noise("flat") { baseOctave = -3; octaveCount = 2 } writes {"base_octave": -3, "octave_count": 2}.
A file name holding slashes lands in subfolders, so noise("cave/entrance") writes data/<namespace>/worldgen/noise/cave/entrance.json.
| Field | Type | Description | Vanilla temperature |
|---|---|---|---|
amplitudeModifiers |
List<Double>? |
Amplitude multiplier per octave, from 0.0 to 1000000.0, 0.0 skipping one, at most 32. Absent means 1.0 for every octave. |
listOf(1.5, 0.0, 1.0, 0.0, 0.0, 0.0) |
baseAmplitude |
Double? |
Amplitude every octave is scaled by, from 0.0 to 1000000.0. Defaults to 1.0. |
1.2453007926713473 |
baseOctave |
Int |
Starting octave from -32 to 32, negative values are larger scale. Lower means larger features. |
-10 |
normalize |
Boolean? |
Whether the sampled value is normalized. Defaults to true. The deprecated "legacy" normalization isn't expressible. |
not written |
octaveCount |
Int? |
Number of octaves, from 1 to 32. Defaults to the number of amplitudeModifiers, or 1. |
6 |
The block state providers of features take the same fields inline through NoiseParameters, built with noiseParameters(baseOctave, vararg amplitudeModifiers).
Every call returns a NoiseArgument. Vanilla noises are available as Noises.AQUIFER_BARRIER, Noises.CONTINENTALNESS and so on, and can be passed anywhere a NoiseArgument is expected.
Octaves and Scale
Octave n has a wavelength of 2⁻ⁿ blocks, so -7 is 128 blocks and -3 is 8 blocks. Octave i of the list sits at baseOctave + i, double the frequency of the previous one. Before its modifier is applied, each octave weighs half the previous one: three octaves weigh 4/7, 2/7 and 1/7. The game adds two Perlin noises together, the second sampled at the coordinates multiplied by 1.0181268882175227.
| baseOctave | Wavelength | Vanilla noise | Read in vanilla at |
|---|---|---|---|
-16 |
65536 blocks | jagged |
xz_scale 1500 |
-10 |
1024 blocks | temperature |
xz_scale 0.25 |
-9 |
512 blocks | continentalness, erosion |
xz_scale 0.25 |
-8 |
256 blocks | vegetation |
xz_scale 0.25 |
-7 |
128 blocks | ridge, cave_entrance |
xz_scale 0.25 for ridge, 0.75 for cave_entrance |
-6 |
64 blocks | surface |
none, noise_threshold has no scale |
-3 |
8 blocks | offset |
coordinates divided by 4, through shift_a and shift_b |
Density functions scale the coordinates they sample with (xzScale), so the in-game size is the wavelength divided by that scale: temperature at xz_scale 0.25 repeats every 4096 blocks.
The explorer below samples a noise with the game's algorithm, from your octaves or a vanilla noise, as a map, a terrain slice or one curve per octave, with the matching Kotlin and JSON:
Density Functions
Density functions are math nodes evaluated per position. Each one lives in its own file and holds exactly one node type, so composing them means referencing other density function files. The game evaluates density functions and noises in single precision, so setups that depend on exact rounding can give slightly different terrain than the same values in double precision.
Declare them inside a densityFunctions { ... } block, one builder call per file:
Each call returns a DensityFunctionArgument usable as an input to another node, as a noise router field, or anywhere else a density function is expected. Vanilla nodes come from the generated DensityFunctions object, e.g. DensityFunctions.Overworld.BASE_3D_NOISE.
The block itself returns the builder scope, not the last node, so arguments you need later are captured in vals inside it. For a single node, or to return one node out of a group, dp.densityFunctionsBuilder exposes the same builders:
Most builders accept either a Double or a DensityFunctionArgument for their inputs, in every combination. constant takes a Double only, and noise, shift, shiftA and shiftB take either a NoiseArgument or inline noise parameters built with noiseParameters(...), since they sample a noise directly. Their noise property is a NoiseOrParameters, set with noiseOrParameters(...).
The JSON fields follow the game: single-input nodes (abs, cube, negate, reciprocal...) write input, two-input nodes (add, div, max, min, mul, sub) write left and right, constant writes value and the shift nodes write noise. The same names are the properties of the node classes.
Density Function Types
| Type | Builder | Description |
|---|---|---|
abs |
abs(...) |
Absolute value of the input. |
add |
add(...) |
Sums two inputs. |
beardifier |
beardifier(...) |
Blends nearby terrain into structures. No parameters. |
blend_alpha |
blendAlpha(...) |
Smooths transitions between chunk generation versions. No parameters. |
blend_density |
blendDensity(...) |
Blends the input across chunk generation version transitions. |
blend_offset |
blendOffset(...) |
Supports legacy chunk compatibility blending. No parameters. |
cache |
cache(...) |
Caches the input once per block position, even if referenced multiple times. |
ceil |
ceil(...) |
Rounds the input up to a multiple of multiple (1 by default). |
clamp |
clamp(...) |
Restricts the input between min and max. |
constant |
constant(...) |
A fixed value, ignoring the input position. |
cube |
cube(...) |
Raises the input to the power of 3 (x³). |
distance_to_point |
distanceToPoint(...) |
Distance from the sampled position to a block position, in one of 4 metrics. |
div |
div(...) |
Divides the first input by the second. |
end_outer_islands |
endOuterIslands(...) |
Samples the End's outer islands, without the central island. No parameters. |
find_top_surface |
findTopSurface(...) |
Scans a column for the topmost position where a density is above zero. |
floor |
floor(...) |
Rounds the input down to a multiple of multiple (1 by default). |
gradient |
gradient(...) |
Linear gradient between two values along any axis, with a tiling mode. |
half_negative |
halfNegative(...) |
Halves the input when negative, otherwise leaves it unchanged. |
interpolated |
interpolated(...) |
Interpolates the input across grid cells of cellSizeXz x cellSizeY blocks. |
interval_select |
intervalSelect(...) |
Picks one of several functions from where the input falls between thresholds. |
lerp |
lerp(...) |
Interpolates from first to second by alpha. |
log |
log(...) |
Takes the logarithm of the input. |
max |
max(...) |
Larger of two inputs. |
min |
min(...) |
Smaller of two inputs. |
mul |
mul(...) |
Multiplies two inputs. |
negate |
negate(...) |
Flips the sign of the input. |
noise |
noise(...) |
Samples a noise ID or inline parameters, scaled and optionally shifted. |
old_blended_noise |
oldBlendedNoise(...) |
Legacy blended noise used before the 1.18 terrain rewrite. |
pow |
pow(...) |
Raises base to the power of exponent. |
quarter_negative |
quarterNegative(...) |
Quarters the input when negative, otherwise leaves it unchanged. |
range_choice |
rangeChoice(...) |
Picks between two inputs based on whether a value falls within a range. |
reciprocal |
reciprocal(...) |
Reciprocal (1 / x) of the input. |
round |
round(...) |
Rounds the input to the nearest multiple of multiple (1 by default). |
shift |
shift(...) |
Samples a noise ID or inline parameters at (x/4, y/4, z/4), scaled up by 4. |
shift_a |
shiftA(...) |
Samples a noise ID or inline parameters at (x/4, 0, z/4), scaled up by 4. |
shift_b |
shiftB(...) |
Samples a noise ID or inline parameters at (z/4, x/4, 0), scaled up by 4. |
sign |
sign(...) |
Outputs the sign of the input. |
slice |
slice(...) |
Evaluates the input with one axis coordinate fixed, flattening 3D to 2D. |
spline |
spline(...) |
Cubic spline interpolating control points over a coordinate. |
sqrt |
sqrt(...) |
Takes the square root of the input. |
square |
square(...) |
Raises the input to the power of 2 (x²). |
squeeze |
squeeze(...) |
Clamps the input to [-1, 1], then applies x/2 - x³/24. |
sub |
sub(...) |
Subtracts the second input from the first. |
truncate |
truncate(...) |
Rounds the input towards zero to a multiple of multiple (1 by default). |
All builders live in io.github.ayfri.kore.features.worldgen.densityfunction.types and take the file's name as their first argument.
lerp takes the alpha density function, then first and second as a Double or a DensityFunctionArgument. floor, round, ceil and truncate snap input / multiple to an integer and multiply back, so multiple is the step. It is optional, defaults to 1 and isn't written when left out. round rounds halves up, and truncate rounds towards zero where floor rounds down:
blend_alpha, blend_offset and blend_density are internal to vanilla generation and are not meant to be referenced from a datapack, even though the builders exist. beardifier is added to the finalDensity by hand, see the noise router.
Multi-Parameter Nodes
Nodes with more than a couple of inputs take a builder block instead of a long positional list:
noise offsets the sampled coordinates with shiftX, shiftY and shiftZ, each a Double or a density function. A shift left out isn't written and counts as a constant 0. Vanilla passes the shift_x and shift_z density functions to bend its climate noises (overworld/temperature, overworld/vegetation, overworld/continents...).
interpolated has no default cell sizes: cellSizeXz and cellSizeY are required, at least 1. Vanilla's Overworld uses 4 and 8 and the End 8 and 4:
intervalSelect holds one more function than it has thresholds: the first function applies below the first threshold, each following one applies between two consecutive thresholds, and the last one applies at or above the last threshold. Thresholds must be sorted ascending.
gradient goes linearly from fromValue to toValue as the axis coordinate goes from fromCoordinate to toCoordinate, which have to differ. tiling sets the value past that range: CLAMP_TO_EDGE (the default, left out of the file) holds the edge value, REPEAT restarts the gradient and MIRRORED_REPEAT goes back and forth:
distanceToPoint measures the distance to a block position with a DistanceMetric (EUCLIDEAN by default, EUCLIDEAN_SQUARED, MANHATTAN or CHEBYSHEV), and slice evaluates a function with one axis fixed. Chained, they give a distance field that is the same at every Y, the base of a disc like the End's main island:
Splines
A spline interpolates control points over a coordinate density function. Points hold either a constant value or a nested spline, which is how vanilla layers continentalness, erosion and ridges into a single terrain offset:
Points are ordered by increasing location, and derivative sets the slope of the curve at that point.
Fields typed DensityFunctionOrDouble are set through their matching setter function (input(...), whenInRange(...), shiftX(...)), overloaded for both a Double and a DensityFunctionArgument. Assigning the field directly needs an explicit densityFunctionOrDouble(...) wrapper.
Noise Settings
Noise settings are the complete terrain configuration for a dimension. A dimension references one through its noise generator.
Properties
Defaults below are Kore's, which are not always vanilla's - the Overworld for instance ships with aquifers.
| Property | Type | Kore default | Description |
|---|---|---|---|
aquifers |
Aquifers? |
null |
Local water/lava pockets instead of a flat sea |
debugFunctions |
List<DebugFunctionEntry>? |
null |
Density functions shown in the generation debug entry |
defaultBlock |
BlockState |
stone |
Block placed where density is positive |
defaultFluid |
BlockState |
water[level=0] |
Fluid placed below seaLevel where density is negative |
disableMobGeneration |
Boolean |
false |
Skips mob spawning during chunk generation |
legacyRandomSource |
Boolean |
false |
Uses the pre-1.18 random source |
materialRule |
MaterialRule |
bandlands |
Rule picking the block of each position, inline or ID |
noise |
NoiseOptions |
(-64, 384) |
Vertical range of the terrain |
noiseRouter |
NoiseRouter |
all zeroes | Density functions wired to generation roles |
seaLevel |
Int |
63 |
Y level the default fluid fills up to |
spawnTarget |
List<Map<DensityFunctionArgument, DoubleOrDoublePair>> |
empty | Climate points the world spawn prefers |
Spawn Target
spawnTargetPoint adds one point of climate space: the density functions it lists, each set to a value or a min..max range. The world spawn prefers where all of them match, but it is a preference, not a filter: every candidate is scored by how far its climate is outside the closest point (lower is better), plus its distance from 0, 0, so the search still ends somewhere when nothing matches. Several points describe several preferred areas. Narrow a range to steer the spawn, widen it to -1.0..1.0 to ignore that function. Vanilla's overworld prefers land (continents from -0.11) away from the ridges values near 0, so it has one point per side (only the first is shown):
Each point is a JSON object keyed by density function ID, and a lone number, such as this[DensityFunctions.Overworld.DEPTH] = 0.5, replaces a range:
Noise Options
minY + height must stay within the dimension type's own vertical range. The sampling resolution isn't part of the noise options: each interpolated density function sets its own cellSizeXz and cellSizeY.
Debug Functions
debugFunction(function, label) adds a density function the game samples at the player's position and lists under label in the chunk_generation_stats debug entry. function is a DensityFunctionArgument or a Double, and without any debugFunction call the field isn't written. Vanilla's overworld lists nine, labeled with one letter for final density (N), temperature (T), vegetation (V), continents (C), erosion (E), depth (D) and ridges (W), plus PV for an inline peaks and valleys expression and PS for overworld/preliminary_surface_level:
Default Blocks
Both take an optional block-state property block, and both accept a plain Map<String, String> instead.
Noise Router
The noise router maps density functions to generation roles. Every field is a DensityFunctionOrDouble, so set it with the matching function, which is overloaded for both a Double and a DensityFunctionArgument:
| Field | Role |
|---|---|
chunkSurfaceLevel |
Estimated surface height of the chunk, used by generation |
continents |
Continentalness climate parameter, ocean vs inland |
depth |
Depth climate parameter, distance below the surface |
erosion |
Erosion climate parameter, flat vs mountainous |
finalDensity |
Final solid/air decision for every position |
ridges |
Weirdness climate parameter, driving ridged terrain |
temperature |
Temperature climate parameter for biome placement |
vegetation |
Humidity climate parameter for biome placement |
Unset fields serialize as 0.0, which means flat, featureless terrain - so a hand-written router usually starts from the vanilla density functions in the generated DensityFunctions object.
finalDensity no longer adds the minecraft:beardifier on its own, so terrain doesn't blend into structures unless the density function does it. A pack reusing vanilla terrain adds it, as the vanilla overworld does with an add whose right is { "type": "minecraft:beardifier" }:
Reference: Noise router
Aquifers
aquifers { } carves the local water and lava pockets. Without it, the terrain is only flooded up to seaLevel, like the Nether and the End in vanilla. Every field is a DensityFunctionOrDouble, set with the matching function, overloaded for a Double and a DensityFunctionArgument, and unset fields serialize as 0.0.
| Field | Role |
|---|---|
barrier |
Noise separating the fluid pockets from the stone |
exclusion |
Density below which no aquifer generates |
fluidLevelFloodedness |
How often the aquifers are filled |
fluidLevelSpread |
How much the fluid level of the aquifers varies |
lava |
Whether an aquifer holds lava instead of water |
surfaceLevel |
Surface height the aquifers are placed relative to |
Material Rules
Material rules decide which blocks replace the top layers of terrain. They are evaluated in order, and the first rule producing a block wins.
Every builder below lives on the materialRules scope, so nothing leaks into the global namespace and the IDE completes the whole rule set from inside the block.
| Rule | Builder | Description |
|---|---|---|
bandlands |
bandlands() |
Vanilla badlands terracotta banding. |
block |
block(block) { } |
Places a block state. |
condition |
condition(condition) { } |
Runs nested rules when the condition passes. |
ore_vein |
oreVein(...) { } |
Places a large vein of ore and raw ore. |
sequence |
sequence { } |
Groups rules, first match wins. |
A condition block holding a single rule serializes as that rule, several rules are wrapped in a sequence.
Ore Veins
oreVein(oreBlock, rawOreBlock, fillerBlock, density) places one large vein where the rule is evaluated, repeatable. density is required, a density function or a constant: a 0.0 density replaces nothing. rawOreChance is the chance, from 0.0 to 1.0, of a raw ore block instead of the ore (default 0.02, like vanilla). The block takes the other density functions of the vein, unset ones serializing as 0.0:
| Field | Role |
|---|---|
density |
Chance, from 0.0 to 1.0, that the vein replaces a block, 0.0 or lower replaces nothing |
fillerGap |
Overrides richness when positive: the filler block is always placed |
richness |
Chance of an ore block instead of a filler block in the vein |
Vanilla keeps its veins in the overworld/copper_ore_vein and overworld/iron_ore_vein material rule files, referenced from the overworld and overworld_caves rules. A pack declares its own the same way, in a materialRule file.
Conditions
Condition builders live on the materialRules scope too, so they resolve without extra imports inside the block.
| Condition | Builder | Description |
|---|---|---|
above_preliminary_surface |
AbovePreliminarySurface |
Position is above the router's preliminary surface. |
biome |
biomes(...) |
Position is in one of the listed biomes. |
hole |
Hole |
Column has a surface depth of 0. |
noise_threshold |
noiseThreshold(noise, minThreshold, maxThreshold) { is3d } |
Noise value falls within a range, in 2D or 3D. |
not |
not(condition) |
Inverts another condition. |
steep |
Steep |
Position is on a steep north or east facing slope. |
stone_depth |
stoneDepth(surfaceType, offset, ...) |
Depth below the floor or ceiling of the terrain. |
temperature |
Temperature |
Biome is cold enough for snowfall. |
vertical_gradient |
verticalGradient(name, trueAtAndBelow, falseAtAndAbove) |
Random blend between two Y anchors. |
water |
water(offset, surfaceDepthMultiplier, addStoneDepth) |
Position is above the local water level. |
y_above |
yAbove(anchor, surfaceDepthMultiplier, addStoneDepth) |
Position is above a Y anchor, exclusive. |
noiseThreshold evaluates its noise in 2D (X/Z) by default. Set is3d = true to evaluate it in 3D instead:
AbovePreliminarySurface, Hole, Steep and Temperature take no arguments, so they are passed directly as objects:
The vertical anchors used by yAbove and verticalGradient are built with absolute(y), aboveBottom(offset) or belowTop(offset), all scoped to the materialRules { } block:
Material Rule and Condition Files
Rules and conditions can live in their own files, worldgen/material_rule/<name>.json and worldgen/material_condition/<name>.json, and be used by ID wherever an inline one fits. materialRule holds the rules of its block, a single one as-is and several in a sequence, materialCondition the condition its block returns:
rule(id) appends a rule file, reference(id) turns a condition file into a condition, and materialRule(id) sets the noise settings' rule to a file, like vanilla's materialRule(MaterialRules.Overworld). The vanilla files are typed in the generated MaterialRules and MaterialConditions.
Reference: Material rule
Putting It Together
The worldgen overview walks through a noise, its density functions, the noise settings wiring them together, and the dimension pointing at the result.
See Also
- Biomes - the biomes a noise generator distributes over the terrain
- Carvers - the caves cut out of the terrain afterwards
- Dimensions - the dimension pointing at noise settings
- Providers - the vertical anchors used by material rule conditions
- World Generation - overview of the worldgen system
