Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 45 — Noise, Density Functions, and Surface Rules

The hardest chapter in this book. It closes Part XI, and it is genuinely advanced: the deepest corner of world generation. If a section makes your head spin, that is normal. Read it once for the shape of the idea, build the example, and come back later. You do not need to memorize every field. You need to understand the pipeline and be able to copy-and-adjust the worked files. We will favor understanding over covering everything, and we’ll point you onward whenever a topic (like the raw noise math) is a deep specialty of its own.

What You’ll Build

In Chapter 44 you built a custom dimension (a separate world space) and pointed its minecraft:noise generator at a noise settings file. You treated that file as a black box: it decided what the land looked like, and you didn’t open it. This chapter opens it.

By the end you’ll have, inside the mypack data pack you started in Chapter 9:

  • a noise settings file that makes exaggerated terrain: taller mountains and deeper valleys than the normal Overworld;
  • a surface rule that paints the ground with red sand over terracotta, like a desert mesa;
  • a carver that digs big caves.

Along the way you’ll meet density functions (the math that decides where land is), the noise router (the bundle of density functions a dimension uses), surface rules (the decision tree that paints the surface blocks), and carvers (caves and ravines). We’ll test everything in a fresh world using the dimension you built in Chapter 44.

Figure (to be captured). a custom dimension showing exaggerated terrain — tall jagged mountains and deep valleys — with red-sand-and-terracotta ground

A word before we start

World generation is the most complex part of data packs, and density functions are the most complex part of world generation. Each field has a name and a job, but the deep math behind several of them (exactly how a noise turns into terrain, how splines bend the land) is a specialty in its own right. When we reach one of those, this chapter will tell you plainly that it’s a topic of its own and point you to where the full spec lives, rather than hand-wave a half-formula. The good news: you can build real, working terrain with just a handful of the pieces, and that’s exactly what we’ll do.

The big picture: how terrain gets decided

Before any single field, hold the whole pipeline in your head. When Minecraft generates a chunk in your dimension, four things happen in order:

  1. Density functions run. A density function is a little math expression that takes a position in the world (x, y, z) and returns a single number. It “makes up mathematical expressions to obtain a number from a position.”
  2. The noise router collects those density functions into named slots. One slot, final_density, decides the basic shape: where the number is positive, the spot becomes solid block; where it’s negative, it becomes air (or water). final_density “determines where there is an air or a default block.”
  3. The surface rule runs over that solid shape and decides which block goes on top: grass, sand, terracotta bands, deepslate, bedrock. Surface rules “determine the block for each solid position of the terrain.”
  4. Carvers dig caves and ravines out of the result.

All four live inside, or are pointed to by, the noise settings file. So the noise settings file is our home base for this chapter. Here is where it lives:

Noise settings are for generating the shape of the terrain and noise caves, and what blocks the terrain is generated with, stored as JSON files within a data pack in the path data/<namespace>/worldgen/noise_settings, and are used with the minecraft:noise generator in a dimension.”

That last clause is the bridge from Chapter 44: the dimension’s minecraft:noise generator names a settings, and that is one of these files. Vanilla ships several you’ve seen the names of: minecraft:overworld, minecraft:amplified, minecraft:nether, minecraft:caves, minecraft:end, and minecraft:floating_islands, and we’re about to write our own.

Density functions: a number from a position

A density function is a JSON file (or a piece of JSON nested inside another file) that describes a math expression. It lives at data/<namespace>/worldgen/density_function/, and it “can be a constant number or an object.”

The simplest density function is just a number. This is the constant shorthand:

0.5

That’s a complete, valid density function: every position gets the value 0.5. The longer way to write the same thing names the type:

data/mypack/worldgen/density_function/half.json

{
  "type": "minecraft:constant",
  "argument": 0.5
}

Every density-function object has a "type" field naming what kind of math it does, plus a few extra fields that depend on the type. The type is “the ID of the density function type,” and the “other additional fields depend on the value of type.” The constant type takes one field, argument.

You will rarely need a separate file for each tiny expression. You can nest one density function directly inside another wherever a density function is expected. We’ll do almost everything inline.

Under the Hood — there are a LOT of density-function types (skippable)

There are well over thirty density-function types. Most of them are for the game’s internal use: types like cache_2d, flat_cache, cache_once, blend_density, beardifier, old_blended_noise, and end_islands exist to make vanilla generation fast or to blend with chunks from older versions, and several of them “should not be referenced in data packs.” You do not need them. This chapter teaches the small set you actually combine by hand: the arithmetic ones, noise, and y_clamped_gradient. If you ever go spelunking in the vanilla files and see an unfamiliar type, that’s fine: leave it alone.

The arithmetic types: combining numbers

Most hand-built terrain is just a few simple types glued together. These take other density functions as inputs and combine them. Each name below is the exact type string, with its one-line description:

typeWhat it doesInputs
constant“A constant value.”argument (a number)
add“Adds two density functions together.”argument1, argument2
mul“Multiplies two inputs.”argument1, argument2
min“Returns the minimum of two inputs.”argument1, argument2
max“Returns the maximum of two inputs.”argument1, argument2
abs“Calculates the absolute value of the input.”argument
clamp“Clamps the input between two values.”input, min, max

So add of two functions gives their sum at every position, mul gives their product, min/max pick the smaller/larger of the two, abs strips the minus sign, and clamp forces the result to stay between a floor and a ceiling. That’s ordinary arithmetic. The only twist is that the “numbers” are themselves functions of position.

One detail worth flagging for clamp: its input must be a direct density function written out in place, not a reference to a density-function ID. There’s a known bug here: “Clamp density function takes a direct input and doesn’t allow a reference.” Good to know if you ever get a mysterious error on a clamp.

Getting variety: the noise type

Pure arithmetic gives you smooth, boring shapes. Real terrain wiggles, and the wiggle comes from noise. The noise density-function type samples a noise pattern. Here’s the type:

noise — Samples a noise.” Fields: type (= noise), noise (the noise to sample), xz_scale (“Scales the X and Z before sampling”), y_scale (“Scales the Y before sampling”).

A few cousins exist for special cases: shifted_noise (“Similar to noise, but first shifts the input coordinates”) and interpolated (“Interpolates at each block in one cell based on the input density function value of some cells around”). We’ll use plain noise.

What does noise point at? A separate noise file:

“A noise is a technical JSON file that can be referenced by a density function and surface rule. They are stored within a data pack in the folder data/<namespace>/worldgen/noise.”

A noise file has two fields: firstOctave and amplitudes, the “First octave” and a list of “Amplitudes of sub-noise.” Here is a small one we’ll use:

data/mypack/worldgen/noise/rolling.json

{
  "firstOctave": -7,
  "amplitudes": [1.0, 1.0, 1.0, 1.0]
}

Designing noise from scratch is a topic of its own. Exactly how firstOctave and the amplitudes list translate into the size and roughness of the bumps is a dense formula involving octaves, Perlin noise, and a normalizing factor. There’s no simple “use these numbers for hills this big” dial. When you want to design noise precisely, the wiki’s Noise page is the place to go for the full math. For now, treat the values above as a knob to experiment with: they’re a reasonable starting point in the spirit of vanilla noises. Smaller (more negative) firstOctave and more list entries generally mean a more detailed pattern.

One thing you can rely on: there are hard-coded noises with fixed jobs. For example, minecraft:surface “affects the surface layer thickness in surface rules” and minecraft:clay_bands_offset is “used to generate badland terracotta bands.” You don’t write those; the game already has them.

Shaping height: y_clamped_gradient

The one density function that turns “a number per position” into “a world with a sky and a floor” is y_clamped_gradient:

y_clamped_gradient — Clamps the Y coordinate between from_y and to_y and then linearly maps it to a range.” Fields: from_y, to_y, from_value, to_value.

In plain terms: it makes the density depend on height. Here is the exact example of a flat world built this way:

“Using the y_clamped_gradient density function, a flat world can be created. In the following example positions at Y=-64 get a density of 1 and positions at Y=320 get a density of -1.”

{
  "type": "minecraft:y_clamped_gradient",
  "from_y": -64,
  "to_y": 320,
  "from_value": 1,
  "to_value": -1
}

Read that as: at the bottom (from_y = -64) the density is +1 (solid), and at the top (to_y = 320) it’s -1 (air), with a smooth slope in between. Because solid means “positive,” this fills the bottom of the world and leaves the top empty: flat ground with a flat sky. The number crosses zero somewhere in the middle, and that height is your terrain surface.

To turn that flat slab into hills, you add a noise to it:

“By adding the previous y_clamped_gradient to a noise, the height of the terrain is based on a noise that varies along the X and Z coordinates.”

{
  "type": "minecraft:add",
  "argument1": {
    "type": "minecraft:y_clamped_gradient",
    "from_y": -64,
    "to_y": 320,
    "from_value": 1,
    "to_value": -1
  },
  "argument2": {
    "type": "minecraft:noise",
    "noise": "minecraft:gravel",
    "xz_scale": 2,
    "y_scale": 0
  }
}

Now the surface “wobbles”: where the noise is positive it pushes the zero-crossing higher (a hill), where it’s negative it pushes it lower (a valley). Two tuning notes you can lean on: “xz_scale: 0.5 makes the terrain smoother,” and to get overhangs “the noise also needs to vary along the Y coordinate. This can be done with xz_scale: 1 and y_scale: 1” (because y_scale: 0 means the noise ignores height, so the wobble is the same all the way up a column).

That little pattern, a height gradient plus a noise, is the heart of nearly all custom terrain. Everything fancier is variations on it.

The noise router: where the density functions plug in

A single final_density function is the star, but a dimension needs a whole bundle of density functions for different jobs. That bundle is the noise router:

“The noise router is a collection of density functions… used for terrain generation, biome layout, aquifers, ore veins, and more. A noise router is a part of a dimension’s noise settings.”

Here are the router’s fields. You will set only a couple of them by hand; the rest you can leave at 0 for a simple custom dimension.

Router fieldWhat it controls
final_density“Determines where there is an air or a default block. If positive, returns a default block… Otherwise, an air block.” The terrain shape.
preliminary_surface_level“A 2D density function… determining the Y-level of the preliminary surface… Used by the generation of aquifers and surface rules.”
temperature“The temperature values only for biome placement.”
vegetation“The humidity values only for biome placement.”
continents“The continentalness values only for biome placement.”
erosion“The erosion values only for biome placement and aquifer generation.”
depth“The depth values only for biome placement and aquifer generation.”
ridges“The weirdness values only for biome placement.”
barrierAquifer separation in caves.
fluid_level_floodednessProbability of liquid in a cave aquifer.
fluid_level_spreadHeight of the liquid surface in aquifers.
lava“Affects whether an aquifer here uses lava instead of water.”
vein_toggle“Affects ore vein type, vertical range and richness.”
vein_ridged“Controls which blocks are part of a vein.”
vein_gap“Affects which blocks in a vein are ore blocks.”

One thing is crystal clear, and it’s worth stating plainly because it’s the key to not getting lost: the biome-parameter fields (temperature, vegetation, continents, erosion, depth, ridges) “do not affect terrain shape, as terrain generation is defined in final_density.” In other words, for a simple dimension where you just want a shape, you only have to fill in final_density. The rest can be 0.

Modern Minecraft — continents and ridges, not “continentalness” and “weirdness”

If you read about terrain online you’ll hear the words continentalness and weirdness: the hidden parameters that the vanilla Overworld uses to lay out biomes. In the noise-router file, though, those fields are named continents (“the continentalness values”) and ridges (“the weirdness values”). So the field you’d type is continents, even though everyone talks about “continentalness.” Type the real field names: continents / ridges.

Two values worth memorizing. “Setting the final density… to 0 results in a void dimension, similarly setting it to 1 would completely fill the world with stone.” That’s your sanity check: final_density: 0 → empty world; final_density: 1 → solid stone world.

The final_density is also where the vein fields plug into the bigger machine. The three vein_* fields control ore vein behavior (which ore at which depth, and the exact thresholds), and the published rules describe how the vanilla veins are wired rather than a step-by-step recipe for designing your own. Custom ore veins are their own deep specialty, well beyond what one terrain chapter needs. For this chapter we leave the three fields at 0 (and set ore_veins_enabled: false) and treat custom veins as out of scope; when you want to build veins, start from a vanilla noise settings file and adapt its vein_* functions.

Under the Hood — splines (skippable)

The vanilla Overworld goes well beyond adding a single noise to a single gradient: it bends the terrain through splines, smooth curves that map one value (say, how far inland you are) to another (say, how high the land sits). There’s a spline density-function type for this: it “Computes a cubic spline,” taking a coordinate (the input density function) and a list of points, each with a location, a value, and a derivative (“The slope at this point”). You can build custom terrain profiles this way. Designing good splines (choosing the points and slopes so the land flows naturally) is a craft of its own; when you want full control, the wiki’s density-function page is where the spline grammar lives. For this chapter, just know splines exist for fine control: our exaggerated terrain reaches its drama with mul, which is plenty.

Walkthrough: a noise settings for exaggerated terrain

Time to assemble a real file. We’ll make a final_density that’s the usual gradient-plus-noise, but we’ll multiply the noise by a constant to exaggerate it: bigger bumps mean taller mountains and deeper valleys.

First, the noise file from earlier (if you haven’t made it yet):

data/mypack/worldgen/noise/rolling.json

{
  "firstOctave": -7,
  "amplitudes": [1.0, 1.0, 1.0, 1.0]
}

Now the noise settings. This is a complete file, nothing elided. Every field name and the default-block shape follow the standard noise-settings skeleton; we fill in a dramatic final_density.

data/mypack/worldgen/noise_settings/exaggerated.json

{
  "sea_level": 63,
  "disable_mob_generation": false,
  "aquifers_enabled": false,
  "ore_veins_enabled": false,
  "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": 2,
    "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,
    "preliminary_surface_level": 0,
    "initial_density_without_jaggedness": 0,
    "final_density": {
      "type": "minecraft:add",
      "argument1": {
        "type": "minecraft:y_clamped_gradient",
        "from_y": -64,
        "to_y": 320,
        "from_value": 1,
        "to_value": -1
      },
      "argument2": {
        "type": "minecraft:mul",
        "argument1": 3.0,
        "argument2": {
          "type": "minecraft:noise",
          "noise": "mypack:rolling",
          "xz_scale": 1,
          "y_scale": 0
        }
      }
    },
    "vein_toggle": 0,
    "vein_ridged": 0,
    "vein_gap": 0
  },
  "spawn_target": [],
  "surface_rule": {
    "type": "minecraft:block",
    "result_state": {
      "Name": "minecraft:stone"
    }
  }
}

Walk the important parts:

  • noise block. Here min_y is “The minimum Y coordinate where terrain starts generating… Must be divisible by 16,” height is “The total height where terrain generates… Must be divisible by 16,” and size_horizontal / size_vertical are each a “Value between 0 and 4.” Our -64 and 384 are the vanilla Overworld values, both divisible by 16.
  • final_density. This is the whole point. Inside the add, argument1 is the height gradient (solid at the bottom, air at the top); argument2 is our rolling noise multiplied by 3.0 using mul. Tripling the noise triples how far the surface swings up and down: that’s the exaggeration. Turn the 3.0 up for even crazier terrain, down toward 1.0 for gentle hills.
  • default_block / default_fluid. These are the block “used for the terrain” and the one “used for seas and lakes.” Wherever final_density is positive you get stone; below sea_level, the air gets filled with water.
  • surface_rule. For now it’s the simplest possible rule: a single block rule painting everything minecraft:stone. We replace this next.
  • spawn_target: []. This is “A list of climate parameters” for choosing the spawn point; an empty list is allowed (“Required, but can be empty”).

To use this, point your Chapter 44 dimension at it. The minecraft:noise generator’s settings becomes mypack:exaggerated:

data/mypack/dimension/exaggerated_world.json (from Chapter 44, shown for the cross-reference, not new)

{
  "type": "minecraft:overworld",
  "generator": {
    "type": "minecraft:noise",
    "settings": "mypack:exaggerated",
    "biome_source": {
      "type": "minecraft:fixed",
      "biome": "minecraft:plains"
    }
  }
}

Create a fresh world with the pack, run execute in mypack:exaggerated run tp @s ~ ~ ~ from a function (Chapter 44’s technique), and you should drop into wildly tall, jagged land.

Figure (to be captured). the exaggerated dimension — towering stone spikes and deep gorges next to normal-scale terrain for comparison

Surface rules: painting the surface

Right now everything is bare stone. The job of deciding which block shows on the surface belongs to the surface rule:

Surface rules are used to determine the block for each solid position of the terrain. They are responsible for grass and dirt layers, creating different bands of terracotta in badlands, for deepslate, bedrock, and more.”

A surface rule is a decision tree: “using a combination of sequences and conditions, it can implement checks to place the right blocks in the right places.” Like every other JSON object here, each rule has a type. There are four rule types:

Rule typeWhat it doesFields
block“Places a specified block.”result_state (the block state)
sequence“Tries surface rules in order, only the first that matches is applied.”sequence (a list of rules)
condition“Checks a condition.”if_true (a condition), then_run (a rule)
badlands“Used in badlands to place terracotta. This rule has no extra fields.”

(Watch out for a typo on the wiki here: the type is written as bandlands with a {{sic}} marker flagging it as a known misspelling. The real type name is almost certainly badlands; confirm it in-game before relying on it. We don’t use it in this chapter anyway.)

Read those four together and the pattern clicks: a sequence is a list it tries top to bottom, stopping at the first match; a condition says “if this is true, run that rule”; and a block is the leaf that actually places something. So a typical surface rule reads like: “In order, if you’re near the surface, place sand; otherwise, place stone.”

The conditions are the interesting part, because they’re how you ask where am I? Every condition also has a type. Here are the available ones, with their exact names:

Condition typeWhat it checks
biome“Checks the biome at the current position.” Field: biome_is (a list of biome IDs).
noise_threshold“Computes the noise value… checks if it is between the min and max threshold.” Fields: noise, min_threshold, max_threshold.
y_above“Checks if the current position is above a specified height (exclusive).” Field: anchor.
water“Checks if the current position is above water, based on terrain depth.” Fields: offset, surface_depth_multiplier, add_stone_depth.
stone_depth“Checks if the current position is within a specified distance from the surface.” Fields: surface_type (floor/ceiling), offset, add_surface_depth.
steep“Checks if the current position is a steep face on the north or east sides of a mountain.” (no extra fields)
vertical_gradient“Compares the current Y position, with a messy transition” — like the deepslate/bedrock fade.
above_preliminary_surface“Checks if the current position is above the preliminary surface level.” (no extra fields)
hole“Passes for columns where the surface depth is 0.” (no extra fields)
temperature“Checks if the current block is in a biome that is cold enough for snowfall.” (no extra fields)
not“Inverts a surface condition.” Field: invert (the condition to flip).

The two you reach for most when painting a surface band are stone_depth and y_above:

  • stone_depth is “how far am I from the surface?” With surface_type: floor, “the blocks will be placed based on the distance to the surface above.” Its offset is how thick a layer you want.
  • y_above is “am I above this height?” Its anchor is a vertical anchor (the same Y-anchor format you’d have met building dimensions). There’s also a water condition for beaches and an above_preliminary_surface condition that vanilla uses “to prevent grass blocks from being placed in noise caves.”

A note on the deeper machinery. Several conditions lean on internal quantities that live in formula form. stone_depth and water use a “terrain depth” / “surface depth” computed with expressions like floor(surface(X,0,Z) × 2.75 + 3.0 + …), and the anchor / vertical_anchor sub-formats are their own small specs. Tuning those precisely is a topic of its own; when you need the full surface-depth math or the exact vertical-anchor grammar, the wiki’s surface-rule and vertical-anchor pages are where they live. We’ll stick to the simplest forms shown in the worked examples below, and call out where a value is a knob to experiment with.

Walkthrough: red sand over terracotta

Let’s paint a desert-mesa surface: a thin cap of red sand on top, terracotta underneath. We’ll replace the surface_rule in exaggerated.json with a sequence that, in order:

  1. If we’re within a few blocks of the surface (a stone_depth floor check), place red_sand.
  2. Otherwise, place terracotta.

Because a sequence stops at the first match, putting the thin red-sand rule first and the catch-all terracotta second gives exactly “sand on top, terracotta below.” Replace the surface_rule value with this complete rule:

data/mypack/worldgen/noise_settings/exaggerated.json (the surface_rule field, complete value)

{
  "type": "minecraft:sequence",
  "sequence": [
    {
      "type": "minecraft:condition",
      "if_true": {
        "type": "minecraft:stone_depth",
        "surface_type": "floor",
        "offset": 0,
        "add_surface_depth": false,
        "secondary_depth_range": 0
      },
      "then_run": {
        "type": "minecraft:block",
        "result_state": {
          "Name": "minecraft:red_sand"
        }
      }
    },
    {
      "type": "minecraft:block",
      "result_state": {
        "Name": "minecraft:terracotta"
      }
    }
  ]
}

How to read it:

  • The outer rule is a sequence: a list tried top to bottom.
  • The first list entry is a condition: its if_true is a stone_depth check with surface_type: floor (distance to the surface above), and its then_run is a block rule placing minecraft:red_sand. The add_surface_depth and secondary_depth_range fields belong to that condition; with offset: 0 and add_surface_depth: false this matches the topmost surface layer.
  • The second list entry is a bare block rule placing minecraft:terracotta. It has no condition, so it always matches, which is why it must come last: it’s the catch-all that paints everything the red-sand rule didn’t.

Try It! — bands of color. Real badlands stack several terracotta colors. You could add more condition entries before the catch-all, each using a y_above check at a different height to place orange_terracotta, yellow_terracotta, and so on, higher bands first. (The exact band pattern vanilla uses comes from the hard-coded minecraft:clay_bands_offset noise and the badlands rule type; you can’t perfectly reproduce it by hand, but you can fake stripes with y_above.)

Drop into the dimension again and the stone is now capped with red sand over terracotta: an exaggerated mesa.

Figure (to be captured). a tall mesa spire with a red-sand cap and terracotta body, generated by the surface rule

Carvers: caves and ravines

Terrain so far is solid where final_density is positive. Carvers dig back into that solid rock to make caves and canyons:

Configured carvers are used to add caves and canyons. They are referenced in biomes.”

That last sentence matters: a carver is not placed inside noise settings. It’s a separate file at data/<namespace>/worldgen/configured_carver/, and a biome points at it (you met biomes in Chapter 42). So the wiring is biome → carver, the same way a biome points at features.

A carver has a type and a config. There are three carver types:

Carver typeWhat it carves
cave“Carves a cave. A cave is a long tunnel that sometimes branches.”
nether_caveLike cave but “with a less frequency and wider tunnels,” and lava-filled below a level.
canyon“Carves a canyon.” (a ravine)

The shared config fields are: probability (“The probability that each chunk attempts to generate carvers,” 0 to 1), y (“The height at which this carver attempts to generate”), lava_level (the Y at/below which carved areas fill with lava), and replaceable (“Blocks that can be carved… a block ID, a block tag, or a list of block IDs”). A cave adds shape knobs: yScale, horizontal_radius_multiplier, vertical_radius_multiplier, and floor_level (“Change the shape of the cave’s horizontal floor”).

Here’s a big-cave carver:

data/mypack/worldgen/configured_carver/big_caves.json

{
  "type": "minecraft:cave",
  "config": {
    "probability": 0.15,
    "y": {
      "type": "minecraft:uniform",
      "min_inclusive": {
        "above_bottom": 8
      },
      "max_inclusive": {
        "absolute": 180
      }
    },
    "lava_level": {
      "above_bottom": 10
    },
    "replaceable": "#minecraft:overworld_carver_replaceables",
    "yScale": {
      "type": "minecraft:uniform",
      "value": {
        "min_inclusive": 0.7,
        "max_inclusive": 1.4
      }
    },
    "horizontal_radius_multiplier": {
      "type": "minecraft:uniform",
      "value": {
        "min_inclusive": 1.0,
        "max_inclusive": 2.0
      }
    },
    "vertical_radius_multiplier": {
      "type": "minecraft:uniform",
      "value": {
        "min_inclusive": 0.8,
        "max_inclusive": 1.3
      }
    },
    "floor_level": -0.4
  }
}

Two of those fields lean on small sub-formats worth knowing about:

  • The y and yScale/radius values use the height_provider, vertical_anchor, and float_provider sub-formats. Each is a little reusable JSON shape with its own dedicated wiki page (Height provider, Vertical anchor, Float provider). The shapes shown here (uniform with min_inclusive/max_inclusive, and above_bottom/absolute anchors) follow the standard vanilla convention; if a value is rejected, the height-provider page has the exact grammar.
  • #minecraft:overworld_carver_replaceables is the vanilla block tag for carve-able blocks. replaceable accepts “a block tag,” and that tag is the standard one the Overworld uses; confirm it exists in your version (or list block IDs directly, e.g. ["minecraft:stone", "minecraft:terracotta"]).

To make the cave actually appear, the biome your dimension uses must list it under carvers (Chapter 42’s biome file). For a custom biome that would be:

"carvers": ["mypack:big_caves"]

Modern Minecraft — nether_cave and canyon. Reuse the same file shape: switch type to minecraft:canyon for a ravine (its config swaps the cave’s radius knobs for a shape compound: distance_factor, thickness, horizontal_radius_factor, and so on), or minecraft:nether_cave for the wider, lava-floored Nether style.

Practice

These extend the files you just built, so keep working in mypack.

  1. Crank the exaggeration. In exaggerated.json, change the mul constant from 3.0 to 6.0 and reload into a fresh world. Then try 1.5. Notice how the same gradient-plus-noise produces gentle hills or absurd spikes depending on that one multiplier. (You’re tuning a mul density function, the one that “Multiplies two inputs.”)

  2. Add a third surface band. Insert a new condition entry into the surface-rule sequence, before the terracotta catch-all, that uses a y_above condition to place orange_terracotta above some height. Higher, more specific rules go first; the catch-all stays last. (You’re using the y_above condition and its anchor field from the conditions table.)

  3. Make a canyon carver. Copy big_caves.json to ravines.json, change type to minecraft:canyon, and replace the cave-only radius fields with the canyon’s shape compound (its fields: distance_factor, thickness, horizontal_radius_factor, vertical_radius_default_factor, vertical_radius_center_factor, width_smoothness). Add mypack:ravines to your biome’s carvers list. (If a vertical_anchor/height_provider value is rejected, check the height-provider wiki page for the exact shape.)

What Can Go Wrong

Your world is completely empty (void). Almost always final_density is evaluating to 0 or negative everywhere. Remember the rule: final_density: 0 → void; final_density: 1 → solid stone. Check that your y_clamped_gradient actually goes positive at low Y (from_value should be positive at from_y) and that you didn’t multiply the whole thing to nothing.

The pack won’t load / the noise block is rejected. Minecraft requires min_y and height to be divisible by 16, with min_y + height not exceeding 2032, and size_horizontal/size_vertical between 0 and 4. A height of 385 (not divisible by 16) will fail. Stick to multiples of 16.

A type doesn’t exist / silently wrong terrain. Density-function, surface-rule, and condition types are exact strings. minecraft:y_clamp_gradient (missing the ed) or minecraft:sequnce won’t match anything. When something generates as flat stone or refuses to load, re-check every type against the tables in this chapter: they’re the exact strings Minecraft expects.

The surface rule paints the wrong order. A sequence stops at the first match. If your catch-all block rule (no condition) is at the top, it wins every time and the conditional bands below it never run. Put the most specific conditions first and the bare catch-all last.

The cave never appears. A carver is referenced from a biome, not from noise settings. If you created the configured_carver file but never added it to a biome’s carvers list, nothing carves. Also check probability isn’t 0.


What You Know Now — Part XI complete

That’s the bottom of the rabbit hole, and the end of Part XI. Across these chapters you went from placing structures (Chapter 40) to defining them (41), from using biomes to customizing them (42), to scattering features (43), to building whole dimensions (44), and now, in this chapter, to sculpting the raw land itself.

You can now:

  • read a noise settings file and name its parts: sea_level, default_block/default_fluid, the noise block, the noise_router, and the surface_rule;
  • write a final_density from a y_clamped_gradient height gradient plus a noise, combined with add/mul, and exaggerate or calm the terrain with a single multiplier;
  • recognize that the biome-parameter router fields (temperature, vegetation, continents, erosion, depth, ridges) shape biome placement, not terrain, and can stay 0 for a simple dimension;
  • build a surface rule as a sequence of conditionblock rules, using stone_depth and y_above to paint layered blocks like red sand over terracotta;
  • define a carver (cave, nether_cave, or canyon) and wire it to a biome’s carvers list.

You’ve also learned the most advanced data-pack skill of all: knowing where a topic gets deep enough to deserve its own study. Several times this chapter we reached fields that open onto specialties of their own (the exact noise math, splines, vein design, the depth formulas, the height-provider sub-formats), and the right move was to name them, use the simplest working form, and point you to where the full spec lives, rather than fake a half-answer. That instinct will serve you in every corner of data packs.

Where to go next. This is the deepest the book goes. If terrain generation grabbed you, the vanilla data pack’s own worldgen/noise_settings/overworld file is the master class: open it and you’ll now recognize the final_density, the router fields, and the surface-rule sequence, even if its splines are beyond what we built. Part XII moves to finishing and sharing the packs you’ve made.