Chapter 42 — Biome Customization
What You’ll Build
A biome is a region of a generated world with its own distinct geography, plants, mobs, temperature, humidity, and colors: forests, deserts, oceans, the Nether wastes. The biome of a location is determined during world generation rather than by the current environment. That last part is the key to this whole chapter, and we’ll come back to it.
Until now you’ve added files to registries that take effect the moment you /reload: recipes, loot
tables, advancements, tags. World generation is different. In this chapter you’ll write a biome
definition (a JSON file under data/<namespace>/worldgen/biome/) that defines a brand-new biome
called the Frozen Wasteland: bone-cold temperature, falling snow, pale washed-out water and grass,
and a spawners block that fills it with hostile mobs. You’ll learn the full Java biome-definition
field shape, why a biome needs a world reboot rather than a /reload, how
biome tags group biomes and what they’re used for, and finally what those cat/variant and
wolf/variant item components from Chapter 24 actually point at: the mob-variant definition registries.
By the end you’ll have a registered custom biome (whether or not it shows up in your world yet, that comes in Chapter 44), and you’ll understand exactly how far the standard fields take you and where you’d reach for the live wiki to go further.
Figure (to be captured). a player standing in a custom pale-blue snowy biome with washed-out grass, a skeleton and a husk visible nearby
Modern Minecraft. Worldgen files used to live behind the dreaded “experimental” warning. In the registry map you saw in Chapter 38, the
worldgen/biomefolder (like every worldgen folder) carries a small red asterisk. That asterisk means a pack using that folder is flagged as using experimental settings (you’ll get a warning screen when you create the world, and Realms won’t take it). The feature itself is real and documented; it’s the folder that trips the flag, exactly like thedialogfolder back in Chapter 39.
What a Biome Is (and Real Biome Names, At Last)
Twice before, this book has used a biome ID and then quietly admitted it couldn’t fully prove it. In
Chapter 18 we wrote a predicate that fired in the dark forest and hedged the ID minecraft:dark_forest.
In Chapter 31 we avoided biome literals entirely. This chapter closes that gap, naming the real biomes
directly.
In Java Edition there are 66 biome types: 55 for the Overworld, 5 for the
Nether, and 5 for the End, plus one used only for a superflat preset. Each biome has its own
resource location (its ID) of the familiar minecraft:<name> shape. Dozens of these biomes have
names you’ll recognize: the plains, the dark forest, snowy plains, ice spikes, deserts,
swamps, jungles, cherry groves, the pale garden, and so on, all real biomes you’ll
see in-game. There’s an important difference between knowing a biome’s name and knowing its
exact ID. The wiki’s Biome/ID page lists every Java biome beside its
exact resource location (more on that in a moment). Two of those IDs you can confirm a second
way, which makes them worth calling out:
minecraft:dark_forest— the dark forest biome is mainly composed of dark oak trees (woodland mansions can generate here), anddark_forestis one of the allowed values of thegrass_color_modifierfield you’ll meet below. The registry namedark_forestshows up verbatim as a field value, so this one is solidly grounded, and that lays the Chapter 18 hedge to rest.minecraft:plains— appears as a full ID literal in the game’s feature-ordering rules (in the UNDERGROUND_ORES step ofminecraft:plains, ore_dirt is placed before ore_gravel). Soplainsis a confirmed ID too.
Resolving the gap (for real this time). The wiki’s Biome/ID page carries the complete Java Edition table: every biome paired with its exact resource location, from
minecraft:the_voidandminecraft:plainsthroughminecraft:dark_forest,minecraft:cherry_grove,minecraft:deep_dark, the Nether five (nether_wastes,warped_forest,crimson_forest,soul_sand_valley,basalt_deltas), and the End set (the_end,end_highlands,end_midlands,small_end_islands,end_barrens). So the Chapter 18 hedge is fully closed:minecraft:dark_forestis real, and so is every other ID you’ll reach for. Two of them you can double-confirm a second way:dark_forestis also an allowedgrass_color_modifiervalue, andplainsappears as a literal ID in the feature-ordering example above. (One genuine caution worth keeping: the stringsminecraft:swamp,minecraft:frozen_ocean, andminecraft:the_endalso appear as Bedrocksurface_buildertype values, so a name matching a biome isn’t proof of the biome’s ID. The Biome/ID table is the place to confirm the Java resource location.)
What actually makes a biome feel like a biome? A handful of properties do, and these are exactly the fields you’re about to write:
- Temperature — a number that drives grass and foliage color, and (height-adjusted) whether it rains or snows.
- Downfall — a humidity number between 0.0 and 1.0, mainly used for block colors; above 0.85 the biome counts as “humid.”
- Precipitation — in Java Edition, simply on or off (
true/false), separate from downfall. - Effects — the colors of water, grass, and foliage.
- Spawns — which mobs appear, and how often.
- Features and carvers — the trees, ores, lakes, and caves carved into the terrain (Chapter 43).
The Biome Definition File
Biome definitions are stored as JSON files within a data pack in the path
data/<namespace>/worldgen/biome. A file at data/mypack/worldgen/biome/frozen_wasteland.json
therefore becomes the biome mypack:frozen_wasteland: the same registry-folder rule you’ve used since
Chapter 7, just a deeper folder.
Here is every Java field, top to bottom. Read it once now; the next section builds a real file using these.
Root fields:
has_precipitation— a true/false boolean: whether the biome has precipitation at all.temperature— a float (decimal number) that “controls gameplay features like grass and foliage color, and a height adjusted temperature (which controls whether raining or snowing occurs ifhas_precipitationis true).”temperature_modifier— optional, defaults tonone. Eithernoneorfrozen. Whenfrozen, it “makes some places’ temperature high enough to rain (0.2)”. This is the trick the frozen ocean uses so a few patches don’t freeze.downfall— a float that “controls grass and foliage color.”effects— a compound (an object) holding the biome’s ambient colors. Its fields:water_color— required, “the normal value is 4159204.” A decimal number converted from a hex color, used for water blocks and cauldrons.foliage_color— optional. Decimal color for tree leaves and vines. If absent, it’s derived from downfall and temperature.dry_foliage_color— optional. Decimal color for leaf litter.grass_color— optional. Decimal color for grass blocks, grass, ferns, and sugar cane. If absent, derived from downfall and temperature.grass_color_modifier— optional, defaults tonone. One ofnone,dark_forest, orswamp(these apply the special grass tints those biomes use).
carvers— required, but can be empty. The cave/ravine carvers for this biome. We’ll write{}and leave carvers for Chapter 43.features— a list of generation steps (can be empty). Each step is itself a list of placed features to run during that step. There are eleven step names, in order:RAW_GENERATION,LAKES,LOCAL_MODIFICATIONS,UNDERGROUND_STRUCTURES,SURFACE_STRUCTURES,STRONGHOLDS,UNDERGROUND_ORES,UNDERGROUND_DECORATION,FLUID_SPRINGS,VEGETAL_DECORATION, andTOP_LAYER_MODIFICATION. (You build the things that go inside these steps in Chapter 43.)creature_spawn_probability— optional float between 0.0 and 0.9999999; higher means more creatures spawned during world generation.spawners— required, but can be empty. This is the heart of “what spawns here.” It’s a compound whose keys are mob categories and whose values are lists of spawn entries.spawn_costs— required, but can be empty. Only mobs listed here use the “spawn cost” mechanism (a way to limit dense spawning); each entry has anenergy_budgetand acharge.
Inside spawners — the mob categories. Each key must be one of monster,
creature, ambient, water_creature, underground_water_creature, water_ambient, misc, or
axolotls. If a category is missing or its list is empty, mobs in that category simply don’t spawn.
Each entry in a category’s list is the spawner data for a single mob:
type— the namespaced entity ID of the mob (e.g.minecraft:skeleton).weight— an int: how often this mob spawns; higher values produce more spawns.minCount— an int greater than 0: the minimum size of a spawned pack.maxCount— an int not less thanminCount: the maximum pack size.
Under the Hood (skippable). Notice
minCountandmaxCountuse capital letters in the middle. That’s “camelCase,” and it’s unusual for data-pack JSON, which almost always uses lowercasesnake_case. Worldgen inherited a few old names like these. Type them exactly as shown; JSON cares about capitalization.
Those are the effects fields we’ll use here. Real biomes also carry a few more effect fields: a
sky_color, a fog_color and water_fog_color, a mood_sound and additions_sound, music, and
ambient particles. They drive the daytime sky tint (computed from temperature), the fog color, the
cave ambience, and the pale garden’s silence. We’ll build a vivid biome with the color fields alone,
which is plenty to see the system working; when you want to add the rest, open the wiki’s Biome page or
copy a vanilla biome and write the extra fields you find there.
Walkthrough: The Frozen Wasteland
Let’s build it. We need a single file. Create the folders worldgen/biome inside your mypack data
folder and add this file.
data/mypack/worldgen/biome/frozen_wasteland.json
{
"has_precipitation": true,
"temperature": -0.7,
"temperature_modifier": "none",
"downfall": 0.4,
"effects": {
"water_color": 3750201,
"foliage_color": 11445290,
"grass_color": 8434339
},
"carvers": {},
"features": [],
"creature_spawn_probability": 0.05,
"spawners": {
"monster": [
{
"type": "minecraft:skeleton",
"weight": 100,
"minCount": 1,
"maxCount": 4
},
{
"type": "minecraft:husk",
"weight": 80,
"minCount": 1,
"maxCount": 3
}
],
"creature": []
},
"spawn_costs": {}
}
Walk through it against the field list:
"has_precipitation": truewith a very low"temperature": -0.7means precipitation falls as snow: when the base temperature is below 0.15, a biome is snowable at any height. That’s our frozen wasteland’s weather.temperature_modifierisnonehere. (Set it tofrozenif you wanted scattered unfrozen patches like the frozen ocean; we want it uniformly icy.)- The three
effectscolors are decimal numbers.3750201is a deep cold-blue water;11445290and8434339are pale, washed-out foliage and grass. These are ordinary decimal-from-hex color numbers, exactly the kind you’ve converted since Chapter 21’scustom_model_datacolors.water_coloris the one effect field that’s required, so it must be present. "carvers": {}and"features": []are the empty-but-required containers. Our biome generates with no special caves and no trees or ores of its own: a true wasteland. (Chapter 43 fills these.)creature_spawn_probabilityis low (0.05) because a wasteland should feel barren of passive life.spawnersis where the danger lives. Themonstercategory lists skeletons (weight 100) and husks (weight 80), each spawning in small packs.weightis relative: skeletons appear a bit more often than husks. Thecreaturelist is present but empty, so no passive animals spawn here. We left the other six categories out entirely, which means those categories don’t spawn either.spawn_costsis the required-but-empty{}; we’re not using the spawn-cost limiter.
Every field above is a real biome-definition field. Save the file.
Try It! Want a hint of life among the bones? Add a third category to
spawners:"ambient": [ { "type": "minecraft:bat", "weight": 10, "minCount": 1, "maxCount": 2 } ]. Bats are in theambientcategory, so they belong there, not increature.
Why a /reload Isn’t Enough
Now the catch we flagged at the top. A location’s biome is determined during world generation rather than by the current environment, even if every block in a large area is altered to imitate the terrain of another biome. A biome is baked into the world as it generates.
This connects to the dynamic-registry idea from Chapter 9. Most registries you’ve written to (recipes,
loot tables, functions) are re-read every time you run /reload. Worldgen registries (biomes,
dimensions, and the enchantments you saw in Chapter 35) are dynamic registries that are loaded when a
world is created or opened, not on /reload. Editing frozen_wasteland.json and typing /reload
will not make the biome appear or update.
To get a biome definition into the registry:
- Make sure your pack (with the
worldgen/biomefile) is installed in the world’sdatapacksfolder before you open the world. The cleanest way: put the pack in a fresh world’sdatapacksfolder and create the world. - If you edit the file later, you must close and reopen the world (or remake it) for the change
to load, not
/reload.
What Went Wrong? “I made the biome, ran
/reload, and/locate biome mypack:frozen_wastelandsays it doesn’t exist.” A/reloaddoesn’t reload worldgen. Quit to the title screen and reopen the world. If it still isn’t found, the file has a JSON typo and failed to load. Check the game log (Chapter 10) for a worldgen error naming your file.
And one honest limitation: registering a biome is not the same as making it appear in your Overworld. The Overworld decides which biomes go where using a biome source that’s part of its dimension. A freshly registered custom biome sits in the registry, ready, yet won’t show up until a dimension is told to place it. That’s Chapter 44’s job. For now you can still prove the biome loaded:
data/mypack/function/find_frozen_wasteland.mcfunction
locate biome mypack:frozen_wasteland
In a vanilla Overworld this will report the biome isn’t found nearby (because nothing places it yet), but if it loaded into the registry the command will be recognized rather than erroring on an unknown biome, your first sign the file is valid. (You’ll make it actually generate in Chapter 44.)
Biome Tags and Their Uses
You’ve grouped registry entries with tags since Chapter 14. Biomes get tags too. A biome tag is simply a group of biomes, and it has three concrete uses:
- Controlling where structures generate.
- Setting the spawn conditions of various entities, including, as you’ll see in a moment, which variant of an animal spawns.
- Testing biomes in commands. A biome tag can be used when testing for biome
arguments in commands with
#<resource location>, which succeeds if the biome matches any of the biomes specified in the tag. So#minecraft:is_forestmatches every forest biome.
Real biome tags include is_overworld, is_forest, is_badlands, is_ocean,
is_taiga, is_mountain, is_river, is_nether, is_end, is_jungle, and is_beach. A biome-tag
file looks like any other tag file (Chapter 14), a list of biome IDs, and lives at
data/<namespace>/tags/worldgen/biome/<name>.json.
Try It! Make your own biome group and put the wasteland in it:
data/mypack/tags/worldgen/biome/spooky.json{ "replace": false, "values": [ "mypack:frozen_wasteland" ] }Now
#mypack:spookyis a valid biome test in any command that takes a biome argument. (Like the biome itself, this is worldgen-adjacent data, so reopen the world after adding it.)
Mob-Variant Definition Registries
Back in Chapter 24 you met a quietly important kind of item component: the minecraft:<mob>/variant
strings. A cat carries minecraft:cat/variant; a wolf carries minecraft:wolf/variant and a separate
minecraft:wolf/sound_variant; cows, chickens, frogs, and pigs each have their own. We told you those
strings name a variant but deferred where the variant is defined to this chapter. Here it is.
In modern Minecraft, a mob’s appearance variants are their own dynamic registries, each with its
own data-pack folder, right alongside worldgen/biome in the registry map you read in Chapter 38.
Here they are, every one marked with the experimental red asterisk:
data/<namespace>/
cat_variant * Textures and spawn conditions of cat variants
chicken_variant * Textures and spawn conditions of chicken variants
cow_variant * Textures and spawn conditions of cow variants
frog_variant * Textures and spawn conditions of frog variants
pig_variant * Textures and spawn conditions of pig variants
wolf_variant * Textures and spawn conditions of wolf variants
wolf_sound_variant * Sound variants of wolves
zombie_nautilus_variant * Textures and spawn conditions of zombie nautilus variants
So the wiring is: a file at data/mypack/wolf_variant/glacier.json would register the wolf variant
mypack:glacier, and the item component minecraft:wolf/variant on a wolf is a string that points
at that ID. The component side matches exactly: for the wolf, minecraft:wolf/variant is a wolf
variant definition (the variant of the wolf), and likewise for
cat/variant, chicken/variant, cow/variant, frog/variant, and pig/variant. The phrase
“spawn conditions” in the table above is the link back to biomes: the game uses biome tags to
decide which variant spawns where. The deciding tags are right there in the biome-tag list:
spawns_cold_variant_farm_animals, spawns_warm_variant_farm_animals, spawns_cold_variant_frogs,
spawns_warm_variant_frogs, and spawns_coral_variant_zombie_nautilus. That’s how a cow “knows” to be
the warm variant in a jungle and the cold variant in a taiga: the biome’s tags steer it.
You can set a variant directly on a spawned mob, using the very component from Chapter 24. Here’s a test function that gives you a wolf already locked to a specific variant via its component:
data/mypack/function/give_test_wolf.mcfunction
give @s minecraft:wolf_spawn_egg[minecraft:wolf/variant="minecraft:pale"]
The minecraft:wolf/variant component holds a string, a variant ID. Here it’s a vanilla one. If you
had authored mypack:glacier in wolf_variant/, you’d write
minecraft:wolf/variant="mypack:glacier" instead, and a wolf spawned from that egg would wear your
variant.
So you’ve seen the folders, the item components that reference them,
and the biome tags that steer them. The one piece left is the inside of a
variant definition file: which textures it points at, which spawn-condition keys it uses, its asset
model fields. Those internal fields are the variant system’s own deep end: for cat, chicken, cow, frog,
pig, wolf, wolf-sound, and zombie-nautilus variants, when you want to author a full custom variant,
open the live wiki’s “Mob variant definitions” page or copy a vanilla variant file and write the fields
you find there. (One handy exception: minecraft:horse/variant isn’t a definition file at all but a
fixed list of values: white, creamy, chestnut, brown, black, gray, or dark_brown.)
Modern Minecraft. If you followed older tutorials, you may have seen cat or wolf variants set with raw NBT, or grouped with a
cat_varianttag. Those tags still exist for compatibility, but the olddefault_spawns/full_moon_spawnscat-variant tags were replaced by spawn condition: the modern variant system uses spawn conditions (and biome tags) for this grouping now.
Practice
1. A second biome — Scorched Flats. Build the opposite of the wasteland: blistering, dry, no precipitation. Reuse the exact field shape from the walkthrough.
data/mypack/worldgen/biome/scorched_flats.json
{
"has_precipitation": false,
"temperature": 2.0,
"downfall": 0.0,
"effects": {
"water_color": 4566514,
"foliage_color": 10387789,
"grass_color": 12431967,
"grass_color_modifier": "none"
},
"carvers": {},
"features": [],
"creature_spawn_probability": 0.0,
"spawners": {
"monster": [
{
"type": "minecraft:husk",
"weight": 100,
"minCount": 2,
"maxCount": 4
}
]
},
"spawn_costs": {}
}
Note "has_precipitation": false and "downfall": 0.0 (no rain or snow at all) and a temperature
of 2.0, the kind of value deserts use. We dropped temperature_modifier entirely (it’s optional and
defaults to none), creature_spawn_probability is 0.0, and only husks spawn. Confirm it loads with
locate biome mypack:scorched_flats after reopening the world.
2. Tag both your biomes. Extend data/mypack/tags/worldgen/biome/spooky.json (or make a new
harsh.json) to list both mypack:frozen_wasteland and mypack:scorched_flats, then use
#mypack:harsh as a biome test in a command of your choice.
3. Read a variant’s wiring. Without authoring a variant definition (the book can’t), write a give
function that hands you a cat spawn egg whose minecraft:cat/variant component is set to a vanilla
variant ID of your choice. You’re practicing the component → variant-ID link from Chapter 24, now that
you know what the ID refers to.
What Can Go Wrong
- You typed
/reloadand nothing happened. Worldgen is a dynamic registry. Biome (and variant) files load when the world is created or opened, never on/reload. Quit to the title and reopen the world. - You used a hex color string for
water_color. The color fields are decimal integers, not"#aabbcc"strings. Convert your hex to a decimal number first (the same conversion you’ve done for item colors since Chapter 21).water_coloris also required; leaving it out fails the file. - You put a passive animal in the
monstercategory (or invented a category). The category key must be exactly one of the eight valid keys (monster,creature,ambient,water_creature,underground_water_creature,water_ambient,misc,axolotls). A typo’d category name silently means “nothing spawns there.” - You expected the biome to appear in your Overworld. Registering a biome doesn’t place it. A
dimension’s biome source decides where biomes generate: that’s Chapter 44. Until then,
locaterecognizing the ID is your proof of a valid file. - You tried to write the inside of a
wolf_variantfile from this book. This chapter stops at the folder boundary for those internal fields. Name and reference variants with confidence; author their definition bodies from the live wiki.