Biomes, noise & density
The biome definition and the noise machinery beneath it: noise settings, density functions, surface rules, and custom noise β the math that actually shapes terrain.
The biome definition
A biome file (data/<ns>/worldgen/biome/<id>.json) controls climate (for grass/foliage tint and rain/snow), client-side effects (colors, fog, particles, sound/music), mob spawning, cave carvers, and the list of placed features generated in each of the 11 generation steps.
| Field | Type | Notes |
|---|---|---|
has_precipitation 1.19.4+ | bool | Whether weather (rain/snow) falls at all. Replaced the old precipitation enum. |
temperature | float | Grass/foliage color influence; decides rain vs. snow (below ~0.15 → snow) and gameplay (e.g. water freezing). |
temperature_modifier | string | "none" (default) or "frozen" β adds noisy warm patches (used by frozen oceans). |
downfall | float | Humidity for grass/foliage color; historically affected rain intensity. |
effects | object | Client rendering (see below). |
spawners | object | Per-category spawn lists (required; may be empty). |
spawn_costs | object | Per-entity spawn density budget (required; may be empty). |
carvers | array/tag | Cave/ravine carvers (#tag or list of carver IDs). |
features | array of 11 arrays | Placed features per generation step (indices 0–10; see below). |
creature_spawn_probability | float (optional) | Chunk creature-spawn chance (0–0.9999). |
precipitationenum →has_precipitationboolean (1.19.4 /pack_format12). Old:"precipitation": "none" | "rain" | "snow". New:"has_precipitation": true|false, and rain-vs-snow is decided purely bytemperature. A 1.21 file must usehas_precipitation.dry_foliage_coloradded in 1.21.5 (leaf litter) β omit for ≤1.21.4.- Older packs used
sky_color/grass_color_modifieridentically; theeffectsobject is otherwise stable across 1.19–1.21.
Biome effects
The effects object drives all client-side rendering.
| Field | Type | Notes |
|---|---|---|
fog_color | int (decimal RGB) | Required. |
water_color | int | Required (default 4159204). Water + cauldron color. |
water_fog_color | int | Required. Underwater fog. |
sky_color | int | Required. |
foliage_color | int (optional) | Overrides computed leaf/vine tint. |
dry_foliage_color 1.21.5+ | int (optional) | Leaf-litter tint. |
grass_color | int (optional) | Overrides computed grass tint. |
grass_color_modifier | string | "none" / "dark_forest" / "swamp". |
particle | object (optional) | { options: {type}, probability } ambient particle. |
ambient_sound | string/object (optional) | Looping ambience. |
mood_sound | object (optional) | Cave "mood" sound (sound, tick_delay, block_search_extent, offset). |
additions_sound | object (optional) | Random additional sounds (sound, tick_chance). |
music | object/list (optional) | Background music (sound, min_delay, max_delay, replace_current_music). |
Spawners & features
spawners β keys are spawn categories: monster, creature, ambient, water_creature, underground_water_creature, water_ambient, misc, axolotls. Each is a list of { "type": <entity>, "weight": int, "minCount": int>0, "maxCount": int≥minCount }.
spawn_costs β per entity { "energy_budget": double, "charge": double } (density-based spawn limiting, used by soul-sand-valley-style crowding control).
features β an array of 11 sub-arrays, one per generation step (indices 0–10 inclusive), each holding placed-feature IDs (or #tags) run in that step:
| # | Step | # | Step |
|---|---|---|---|
| 0 | RAW_GENERATION | 6 | UNDERGROUND_ORES |
| 1 | LAKES | 7 | UNDERGROUND_DECORATION |
| 2 | LOCAL_MODIFICATIONS | 8 | FLUID_SPRINGS |
| 3 | UNDERGROUND_STRUCTURES | 9 | VEGETAL_DECORATION |
| 4 | SURFACE_STRUCTURES | 10 | TOP_LAYER_MODIFICATION |
| 5 | STRONGHOLDS |
Always include spawners, spawn_costs, carvers, and features even when empty β they are required keys. Ensure any placed features you list actually exist, or worldgen throws and the chunk fails to load. To reproduce overworld caves, reference the carver tag rather than leaving carvers empty (empty does not mean "vanilla").
Biome example
data/example/worldgen/biome/my_plains.json
{
"has_precipitation": true,
"temperature": 0.8,
"temperature_modifier": "none",
"downfall": 0.4,
"effects": {
"fog_color": 12638463,
"sky_color": 7907327,
"water_color": 4159204,
"water_fog_color": 329011,
"foliage_color": 9215022,
"grass_color": 9287662,
"grass_color_modifier": "none",
"mood_sound": {
"sound": "minecraft:ambient.cave",
"tick_delay": 6000,
"block_search_extent": 8,
"offset": 2.0
}
},
"spawners": {
"creature": [
{ "type": "minecraft:sheep", "weight": 12, "minCount": 4, "maxCount": 4 },
{ "type": "minecraft:pig", "weight": 10, "minCount": 4, "maxCount": 4 }
],
"monster": [
{ "type": "minecraft:zombie", "weight": 100, "minCount": 4, "maxCount": 4 }
]
},
"spawn_costs": {},
"carvers": [],
"features": [
[], [], [], [],
[], [],
["minecraft:ore_dirt", "minecraft:ore_gravel"],
[], [],
["minecraft:trees_plains", "minecraft:patch_grass_plain"],
[]
]
}
- Custom colored world: author one biome with vivid
grass_color/foliage_color/sky_color/fog_color, then use it via afixedbiome source. Set bothgrass_colorandfoliage_colorexplicitly to force a flat tint regardless of temperature/downfall. - Silent/empty biome: empty
spawnerscategories + emptyfeaturessteps = a sterile canvas biome. - Perpetual-snow biome:
temperature: -0.5,has_precipitation: true.
Noise settings
Noise settings (data/<ns>/worldgen/noise_settings/<id>.json) drive noise-based terrain: the default block/fluid, the world's vertical envelope, aquifers, ore veins, the noise router (which wires density functions into named generation slots), spawn targeting, and the surface rule decision tree. This is the most complex worldgen file; the heavy math lives in density functions.
| Field | Type | Notes |
|---|---|---|
sea_level | int | Fluid level used by generation (independent of the vanilla Y=63 mob rule). |
disable_mob_generation | bool | Suppresses creature spawning during chunk gen. |
aquifers_enabled | bool | Local water tables in caves; if false, caves below sea_level flood with default_fluid. |
ore_veins_enabled | bool | Large copper/iron ore veins. |
legacy_random_source | bool | Use the pre-1.18 RNG (also constrains custom noise firstOctave ≤ 1). |
default_block | blockstate {Name, Properties?} | Terrain fill (e.g. minecraft:stone). |
default_fluid | blockstate | Sea/lake fill (minecraft:water OW / minecraft:lava Nether). |
noise | object | Vertical envelope + cell size (below). |
noise_router | object | Density-function slots (below). |
spawn_target | array | Climate parameter targets around which player spawn is chosen. |
surface_rule | object | Top-of-terrain block decisions (see below). |
The noise object sets the vertical envelope and cell size:
| Field | Range | Notes |
|---|---|---|
min_y | −2032…2031, ÷16 | Terrain floor β must match dimension_type.min_y. |
height | 0…4064, ÷16 | Terrain height β must match dimension_type.height; min_y + height ≤ 2032. |
size_horizontal | 0–4 | Horizontal cell size; larger = smoother but coarser (1 = 8-block cells, 3 = 12-block). |
size_vertical | 0–4 | Vertical cell size. |
The noise router
The noise_router maps density functions (inline object, referenced ID, or a constant) into named generation slots. Grouped by purpose:
| Slot | Role |
|---|---|
final_density | The terrain shape. >0 → place default_block; ≤0 → air / aquifer fluid. Constant 0 = void world; constant 1 = solid-stone world. |
initial_density_without_jaggedness | Preliminary density used before jaggedness, feeding surface/aquifer estimation. |
barrier | Aquifer separation vs. open caves. |
fluid_level_floodedness | Probability a cave aquifer holds liquid (clamped −1…1). |
fluid_level_spread | Vertical spread / surface height of aquifer liquid. |
lava | Whether an aquifer is lava instead of water (threshold ~0.3). |
vein_toggle | Ore-vein type/region (copper Y 0–50 positive; iron Y −60…−8 non-positive). |
vein_ridged | Which blocks make up a vein. |
vein_gap | Gaps/placement of ore within veins. |
temperature | Climate param → biome placement only (not terrain). |
vegetation | (a.k.a. humidity) climate → biome placement. |
continents | (continentalness) climate → biome placement. |
erosion | Climate → biome placement. |
depth | Climate → biome placement (surface vs. cave). |
ridges | (weirdness) climate → biome placement. |
Key insight: temperature/vegetation/continents/erosion/depth/ridges do NOT shape terrain β they only feed the multi_noise biome source. Terrain height is decided solely by final_density.
data/example/worldgen/noise_settings/my_settings.json β a void world (final_density: 0)
{
"sea_level": 63,
"disable_mob_generation": false,
"aquifers_enabled": true,
"ore_veins_enabled": true,
"legacy_random_source": false,
"default_block": { "Name": "minecraft:stone" },
"default_fluid": { "Name": "minecraft:water", "Properties": { "level": "0" } },
"noise": {
"min_y": -64,
"height": 384,
"size_horizontal": 1,
"size_vertical": 2
},
"noise_router": {
"barrier": 0,
"fluid_level_floodedness": 0,
"fluid_level_spread": 0,
"lava": 0,
"temperature": 0,
"vegetation": 0,
"continents": 0,
"erosion": 0,
"depth": 0,
"ridges": 0,
"initial_density_without_jaggedness": 0,
"final_density": { "type": "minecraft:constant", "argument": 0 },
"vein_toggle": 0,
"vein_ridged": 0,
"vein_gap": 0
},
"spawn_target": [],
"surface_rule": { "type": "minecraft:block", "result_state": { "Name": "minecraft:stone" } }
}
For real terrain, copy data/minecraft/worldgen/noise_settings/overworld.json from the vanilla data and edit incrementally β hand-writing a full router is impractical.
initial_density_without_jaggedness→preliminary_surface_level(1.21.9 / 25w31a). Useinitial_density_without_jaggednessfor 1.21–1.21.8; the newer slot is a 2-D function sampled at Y=0.- 26.3-snap1:
surface_rulerenamed tomaterial_rule. 26.3-snap2:spawn_targetaccepts any density function. Neither applies to 1.21. - The noise-based system itself stabilized in 1.18 (Caves & Cliffs pt. 2).
Density functions
Density functions (data/<ns>/worldgen/density_function/<id>.json) are JSON-encoded math expressions of position (x,y,z) returning a number. They compose into the noise-router slots. A function is either referenced (a string ID, e.g. "minecraft:overworld/sloped_cheese") or inline (a { "type": ... } object). A bare number is shorthand for a constant. Exception: clamp's input accepts only an inline definition, not a string ID.
Unary transforms & constants
Unary transforms take argument = a density function:
| Type | Effect |
|---|---|
abs | |x| |
square | x² |
cube | x³ |
half_negative | x/2 if x<0 else x |
quarter_negative | x/4 if x<0 else x |
squeeze | clamp to [−1,1] then x/2 − x³/24 |
constant | {argument: double} (−1e6…1e6). A raw number is equivalent. |
Binary & caching
Binary: add, mul, min, max β each {argument1, argument2}.
Caching / performance (take argument): interpolated (interpolate across cell corners), flat_cache (compute once per 4×4 column at Y=0), cache_2d (once per XZ column), cache_once (compute once per position even if referenced twice), cache_all_in_cell (engine-internal β avoid in datapacks).
Noise-based
| Type | Fields |
|---|---|
noise | noise (custom-noise ID), xz_scale, y_scale |
shifted_noise | noise, xz_scale, y_scale, shift_x, shift_y, shift_z |
weird_scaled_sampler | input, noise, rarity_value_mapper (type_1/type_2) β samples noise at a rarity that scales with input. |
end_islands | none (End-island noise, range −0.84375…0.5625) |
old_blended_noise | xz_scale, y_scale, xz_factor, y_factor, smear_scale_multiplier (legacy terrain noise) |
Shift helpers (argument = a noise ID, sample downscaled by 4): shift_a (samples at x/4,0,z/4), shift_b (z/4,x/4,0), shift (x/4,y/4,z/4).
Range, blending, splines
| Type | Fields |
|---|---|
range_choice | input, min_inclusive, max_exclusive, when_in_range, when_out_of_range β an if/else on the input's value. |
clamp | input (inline only), min, max. |
blend_alpha | none (old-chunk transition blending) |
blend_offset | none |
blend_density | argument |
beardifier | none β engine adapts terrain under structures. |
spline | spline: { coordinate: <density fn>, points: [ { location, value(number or nested spline), derivative } ] } β cubic-spline mapping used for continentalness/erosion/ridges → offset/factor/jaggedness. |
y_clamped_gradient | from_y, to_y (int −4064…4062), from_value, to_value β linearly maps clamped Y into a value range. |
- Density functions were introduced 1.18.2-pre1;
splineadded 1.18.2-pre2. - 1.19: removed
terrain_shaper_splineand the legacy spline format (22w11a); removedslide(22w12a β recreate viaadd/mul/y_clamped_gradient);old_blended_noisegained its scale/factor params. - 1.21.9 (25w31a): added
find_top_surfaceandinvert. - 26.2-snap5: added
interval_select; removedweird_scaled_sampler(still valid in 1.21).
Inline example β unary + constant
{ "type": "minecraft:abs", "argument": { "type": "minecraft:constant", "argument": -5.0 } }
A y_clamped_gradient β the simplest "flat surface at a Y level" terrain
{
"type": "minecraft:y_clamped_gradient",
"from_y": -64,
"to_y": 320,
"from_value": 1.0,
"to_value": -1.0
}
Used as final_density, this alone makes solid ground low and air high.
Wrap expensive noise in cache_2d/flat_cache when the value is constant per column; use cache_once when the same function feeds two slots. Break complex expressions into named files under worldgen/density_function/ and reference them by ID β mirroring how vanilla organizes overworld/*. Note that clamp can't reference by ID β inline its input, or wrap a reference in an op that does accept IDs.
Surface rules
Surface rules (the surface_rule field inside noise_settings) decide which block goes at each solid surface position β grass over dirt over stone, sand in deserts, deepslate below a Y band, bedrock at the floor. It is a decision tree of three node types evaluated top-down.
| Type | Field(s) | Meaning |
|---|---|---|
minecraft:block | result_state (blockstate) | Place this block; a terminal/leaf. |
minecraft:sequence | sequence (array of rules) | Try each in order; first match wins. |
minecraft:condition | if_true (a condition), then_run (a rule) | If the condition holds, run the nested rule. |
Condition types (if_true):
type | Fields | Purpose |
|---|---|---|
biome | biome_is (array of biome IDs) | Match specific biomes. |
noise_threshold | noise (noise ID), min_threshold, max_threshold | Sampled noise within a band (e.g. gravel/surface variation). |
vertical_gradient | random_name, true_at_and_below (Y anchor), false_at_and_above (Y anchor) | Fuzzy Y transition (deepslate blend, bedrock roof/floor roughness). |
y_above | anchor (Y anchor), surface_depth_multiplier (int), add_stone_depth (bool) | True above a height. |
water | offset (int), surface_depth_multiplier (int), add_stone_depth (bool) | Relative to the local water level. |
temperature | — | True where it's cold enough to snow. |
steep | — | On steep (mountain-face) slopes. |
not | invert (a condition) | Negation. |
hole | — | Where surface depth = 0 (noise-cave openings). |
above_preliminary_surface | — | At/above the preliminary surface (keeps grass out of noise caves). |
stone_depth | surface_type (floor/ceiling), offset (int), add_surface_depth (bool), secondary_depth_range (int) | Distance into the surface from floor or ceiling β the workhorse for layering grass/dirt/stone. |
Y anchors (anchor, true_at_and_below, …) are objects like { "absolute": 63 }, { "above_bottom": 0 }, or { "below_top": 0 }.
surface_rule inside noise_settings β desert sand, else grass→dirt cap, else stone
{
"type": "minecraft:sequence",
"sequence": [
{
"type": "minecraft:condition",
"if_true": { "type": "minecraft:biome", "biome_is": ["minecraft:desert"] },
"then_run": { "type": "minecraft:block", "result_state": { "Name": "minecraft:sand" } }
},
{
"type": "minecraft:condition",
"if_true": {
"type": "minecraft:stone_depth",
"surface_type": "floor",
"offset": 0,
"add_surface_depth": true,
"secondary_depth_range": 0
},
"then_run": { "type": "minecraft:block", "result_state": { "Name": "minecraft:grass_block", "Properties": { "snowy": "false" } } }
},
{ "type": "minecraft:block", "result_state": { "Name": "minecraft:dirt" } }
]
}
How badlands gets its terracotta banding. The colored bands are one built-in rule source β { "type": "minecraft:bandlands" } β gated by a minecraft:biome condition on badlands/eroded_badlands/wooded_badlands. It's hardcoded (SurfaceRules.Bandlands): effectively a ~192-entry, Y-indexed table of stained terracotta (orange base; bands of plain/yellow/brown/red/light-gray terracotta; white_terracotta bands driven by a separate noise), with red_sand capping the top so the bands only show on cut walls. The palette and spacing are baked into Java and can't be reconfigured β to make custom bands, stack minecraft:y_above (or noise_threshold) conditions each selecting a different terracotta result_state.
- 26.2-snap6:
noise_gradientsurface rule removed;noise_thresholdchanged. 26.3-snap1: the wholesurface_rulefield renamed tomaterial_rule. Neither applies to 1.21. - The condition/rule system as shown has been stable across 1.18–1.21.
Put the most specific conditions first (biome-specific), general fill (stone) last as the sequence's final leaf. Reuse stone_depth floor for the grass/dirt cap β it automatically respects the biome's surface depth.
- Badlands-style banding: emulate with
noise_thresholdselecting between terracotta colors. - Bedrock floor roughness:
vertical_gradientwith arandom_namefromtrue_at_and_below above_bottom 5tofalse_at_and_above above_bottom 0. - Single-material world: a lone
{ "type":"block", "result_state": {...} }(no sequence) paints everything.
Custom noise
A custom noise definition (data/<ns>/worldgen/noise/<id>.json) supplies a Perlin/octave noise referenced by density functions (noise, shifted_noise, shift*). Two fields only.
| Field | Type | Notes |
|---|---|---|
firstOctave | int | The lowest (largest-scale) octave. More negative → larger features. If legacy_random_source is true in the consuming noise settings, firstOctave must be ≤ 1; otherwise unbounded. |
amplitudes | array of doubles | Weight of each successive octave. Octave i has frequency 2^(firstOctave + i); 0.0 entries disable that octave. |
Each amplitude's normalized contribution follows roughly 1.04 · value · 2^(n−i−1) / (2^n − 1) where n = amplitudes.length. Lower octaves = broad shape, higher octaves = fine detail.
data/example/worldgen/noise/rolling.json
{
"firstOctave": -3,
"amplitudes": [1.0, 1.0, 0.5, 0.25]
}
Reuse vanilla noises (minecraft:temperature, minecraft:vegetation, minecraft:continentalness, minecraft:erosion, minecraft:ridge, minecraft:offset, minecraft:cave_*) via ID rather than redefining β they're tuned to the vanilla router. For bigger biomes/terrain, lower firstOctave (more negative) and/or use fewer high-octave amplitudes; for spikier detail, append small trailing amplitudes (e.g. ..., 0.1, 0.05).