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 44 — Custom Dimensions

What You’ll Build

Way back in Chapter 7, when we listed everything a data pack can configure, one item came with a promise attached: dimensions. You already know dimensions from playing: the Overworld you spawn in, the Nether you reach through a portal, the End where the dragon waits. Each one is its own separate space with its own sky, its own height, its own rules. Chapter 27 even let you reach across them with execute in. What Chapter 7 promised, and what this chapter delivers, is that a data pack can add a brand-new dimension of your own: a separate world that didn’t exist before you wrote a couple of JSON files.

By the end of this chapter you’ll have built a flat void arena in the mypack pack you started back in Chapter 9: a private, empty, perfectly flat dimension that’s good for a minigame. Players teleport in, the match happens on a clean slate, and nothing from the Overworld is in the way. You’ll do it with exactly two files: a dimension type (the physical rules of the space) and a dimension definition (which links that type to a generator that decides what blocks fill the world). Then you’ll travel into it with execute in, and as practice you’ll build a second dimension made of thin floating slabs: a sky world.

Figure (to be captured). a player standing in an empty flat void dimension, flat ground stretching out under a plain sky

Heads up before you start — this dimension needs a reboot, not /reload. Dimensions live in a dynamic registry (Chapter 7’s word for a registry data packs are allowed to add to). You learned the rule for these back with biomes in Chapter 42: the game only reads dynamic-registry files when a world loads. So after you write these files, /reload will not make the new dimension appear. You have to leave the world and open it again. We’ll repeat this at the moment it matters, but keep it in mind so you’re not confused when /reload seems to do nothing.

What a dimension really is

Here’s the plain definition:

Dimensions are parallel worlds within a Minecraft world characterized by a way of generation, biomes and structures, and other things unique to one dimension. Each dimension is its own separate 3D space, not affecting other dimensions.”

That last sentence is the key idea for this chapter. A dimension is a separate 3D space, a whole independent block world. Blocks you place in the Nether don’t show up in the Overworld; they’re different spaces that happen to belong to the same saved world. The Overworld, the Nether, and the End are just the three dimensions the game ships with.

And here’s the promise being kept. As the wiki puts it, under “Custom dimensions”:

“Using data packs, it is possible to create custom dimensions and dimension types that have different biomes, world generation and properties from the regular dimensions. (Java Edition only)”

So a data pack can hand the game a new separate space, with its own biomes, its own way of generating terrain, and its own properties like height and lighting. That “(Java Edition only)” note matters: everything in this chapter is a Java Edition feature.

The two files: type and definition

Making a dimension takes two JSON files, and it’s worth being clear from the start about which does what, because the names are similar:

  • A dimension type describes the physical rules of a space: how tall it is, whether it has a sky, how bright the ambient light is, whether time is frozen. Dimension types “define properties of a dimension such as world height build limits, the ambient light, and more.” These files live in the folder data/<namespace>/dimension_type (note the singular folder name), the same rule you’ve followed since Chapter 9.

  • A dimension definition (or just “dimension file”) is the file that creates an actual dimension by pointing at a type and attaching a generator. Dimensions are stored as JSON files within a data pack, at the path data/<namespace>/dimension/<name>.json. Singular folder again: dimension.

Think of it like a recipe and a finished dish. The dimension type is a reusable description of what kind of space this is; the dimension definition is the one specific world that uses it and decides what’s in it. You can have one type and reuse it for several dimensions, the same way the game’s overworld type is shared.

We’ll write the type first, because the definition refers to it.

The dimension type file

A dimension type is a single JSON object. Here is the whole list of fields it can hold, so you know what each one means before we write the file:

  • coordinate_scale (number) — “The multiplier applied to coordinates when leaving the dimension.” The Nether’s is 8, which is why one Nether block equals eight Overworld blocks (you met that 8:1 ratio in Chapter 27). Must be between 0.00001 and 30000000.0.
  • has_skylight (true/false) — “Whether the dimension has skylight or not. If set to false, weather is additionally disabled.”
  • has_ceiling (true/false) — “Whether the dimension has a bedrock ceiling,” the way the Nether does. This changes how respawn and mob spawning are calculated.
  • has_ender_dragon_fight (true/false) — “Whether this dimension can have an ender dragon fight.” Leave this false unless you want End-style dragon machinery.
  • ambient_light (number) — “How much light the dimension has. When set to 0, it completely follows the light level; when set to 1, there is no ambient lighting.” In plain terms: 0.0 means it gets properly dark at night like the Overworld; higher values keep a baseline glow everywhere.
  • has_fixed_time (true/false, optional, defaults to false) — “Whether this dimension has fixed time.” With this on, the sun never moves.
  • monster_spawn_block_light_limit (whole number 0–15) — “Block light level must be less than or equal to this value for monsters to spawn.”
  • monster_spawn_light_level (0–15, or an int provider) — sets the spawn-light formula; a plain whole number is the simple form.
  • logical_height (whole number) — “The maximum height to which chorus fruits and Nether portals can bring players within this dimension.” Can’t be greater than height.
  • min_y (whole number) — “The minimum height in which blocks can exist within this dimension. Must be between -2032 and 2031 and be a multiple of 16.”
  • height (whole number) — “The total height in which blocks can exist within this dimension. Must be between 16 and 4064 and be a multiple of 16.” (So the highest place you can put a block is min_y + height - 1.)
  • infiniburn (string) — “A block tag with #. Fires on these blocks burns infinitely.” The Overworld uses the block tag #minecraft:infiniburn_overworld.
  • skybox (string, optional, defaults to overworld) — “The skybox to use. Can be none, overworld, or end.”
  • cardinal_light (string, optional, defaults to default) — “Direction of cardinal light affecting blocks. Can be default or nether.”
  • default_clock (string, optional) — which world clock the /time command uses here.

Under the Hood — min_y and height are about block space, not difficulty (skippable). These two are the most important fields to get right, and they trip people up because they interact. min_y is the floor (the lowest Y a block can exist at), and height is how many blocks of vertical room there are above that floor. The Overworld uses min_y of -64 and height of 384, so blocks can exist from Y=-64 up to Y=319. Both numbers must be multiples of 16: pick min_y and height from the 16-times-table (…-64, -48, -32, -16, 0, 16, 32…) or the game will reject the file.

For the arena we want a small, well-behaved space: a normal sky so players can see, light that gets dark normally, no ceiling, and a modest height range. Here’s the file. Every field name is copied from the list above.

data/mypack/dimension_type/arena.json

{
  "ultrawarm": false,
  "natural": true,
  "coordinate_scale": 1.0,
  "has_skylight": true,
  "has_ceiling": false,
  "has_ender_dragon_fight": false,
  "ambient_light": 0.0,
  "piglin_safe": false,
  "bed_works": true,
  "respawn_anchor_works": false,
  "has_raids": true,
  "logical_height": 256,
  "infiniburn": "#minecraft:infiniburn_overworld",
  "effects": "minecraft:overworld",
  "min_y": -64,
  "height": 320,
  "monster_spawn_block_light_limit": 0,
  "monster_spawn_light_level": 7
}

Where these values come from. The field list above covers the definitions (coordinate_scale, has_skylight, has_ceiling, has_ender_dragon_fight, ambient_light, has_fixed_time, the two monster_spawn_* fields, logical_height, min_y, height, infiniburn, skybox, cardinal_light, and default_clock). On top of those, each of the three vanilla dimensions (and the Overworld Caves preset) ships with a concrete set of values, and that’s where the extra fields above come from: ultrawarm, natural, piglin_safe, respawn_anchor_works, bed_works, and has_raids, each true or false per dimension, plus the Overworld’s infiniburn tag (infiniburn_overworld) and its effects (minecraft:overworld). Every value in the file above is simply the Overworld’s own settings, written out as JSON: its ambient_light is 0.0, its logical_height is 256, and its coordinate_scale is 1.0. Tweak height/min_y to taste within the limits below.

Read down the values: a 1:1 coordinate scale (no Nether-style scaling), a real sky (has_skylight true), no ceiling, light that fully follows the normal day/night cycle (ambient_light 0.0), a build range from Y=-64 up to Y=255 (min_y -64 plus height 320, both multiples of 16), and an infiniburn block tag (the value shown is the Overworld’s). The two monster_spawn fields are set low so the arena stays calm. That’s a complete, sky-lit, flat-friendly space.

The dimension definition file

Now the file that actually makes the dimension exist. The root object has just two parts:

type: dimension type. Can be preset overworld, the_nether, the_end, overworld_caves, or a custom dimension type”, and “generator: Generation settings used for that dimension.”

So type is the identifier of a dimension type (either a built-in one or the custom one you just wrote), and generator is a nested object describing how to fill the world with blocks. The generator has its own type, which is “One of noise, flat, or debug”. Here is what each one does:

  • noise — “The generator used in all the default dimensions.” This builds real, varied terrain from noise. It’s how the Overworld and Nether are made. It’s complicated, and we’ll describe its shape later in this chapter but save the deep machinery for Chapter 45.
  • flat — “The generator type used for superflat worlds.” Flat layers of your choosing. This is what we’ll use for the arena, because a void is just the simplest possible flat world.
  • debug — a special grid-of-every-block generator with “no additional fields”; not something you’d ship in a real pack.

We want flat. The flat generator takes one extra field, settings, holding the “Superflat settings.” So what do superflat settings look like? Here’s a complete, real example (it’s the server-config form, but the JSON shape is the same one the generator uses):

{"biome":"minecraft:plains","layers":[{"block":"minecraft:bedrock","height":1},{"block":"minecraft:dirt","height":2},{"block":"minecraft:grass_block","height":1}]}

That tells us the three field names we need: a biome for the whole world, and a layers list where each layer is an object with a block and a height. Here’s what they mean: layers is “interpreted from top to bottom, starting at world bottom” (so the first layer is the lowest), each layer’s height is “the height of this layer,” and block is “the block ID.”

A void is the simplest layer list there is. The Superflat page lists a built-in preset called “The Void” whose entire contents are: “Air x1.” So a void dimension is a flat world whose only layer is a single layer of air. Here’s the arena:

data/mypack/dimension/arena.json

{
  "type": "mypack:arena",
  "generator": {
    "type": "minecraft:flat",
    "settings": {
      "biome": "minecraft:the_void",
      "layers": [
        {
          "block": "minecraft:air",
          "height": 1
        }
      ]
    }
  }
}

Look at how the two files connect. The definition’s "type": "mypack:arena" is the identifier of the dimension type file you wrote a moment ago: data/mypack/dimension_type/arena.json becomes the ID mypack:arena, exactly the namespace-and-path naming you’ve used since Chapter 8. The generator is minecraft:flat, and its settings say: one layer, one block tall, made of air, over the minecraft:the_void biome. That’s an empty world. The file itself sits at data/mypack/dimension/arena.json, so the new dimension’s own identifier is mypack:arena too: a dimension file’s ID and the dimension it makes share a name.

Try It! — give the arena a floor. An all-air void means players fall forever, which is great if your minigame teleports them to a built platform but awkward otherwise. To give the whole dimension a solid floor, change the layers list to two layers (bedrock then air), like the void preset with a base:

"layers": [
  { "block": "minecraft:bedrock", "height": 1 },
  { "block": "minecraft:air", "height": 64 }
]

Remember layers go bottom-up, so the bedrock is the floor and the 64 layers of air sit on top of it.

Loading it: reboot, then travel in

This is the moment the warning from the start of the chapter pays off. You’ve written both files. Your instinct, after every chapter so far, is to run /reload. Don’t expect it to work here. As you learned with biomes in Chapter 42, dimensions are a dynamic registry, and the rule for those is that the game reads them only when a world loads. So:

  1. Save both files.
  2. Quit to the title screen (leave the world entirely).
  3. Open the world again.

Now mypack:arena exists. To go there, you use the command Chapter 27 introduced: execute in <dimension>. Back then you used it to reach the Nether; it works for any loaded dimension, including yours. We’ll wrap it in a function so it’s easy to run again.

data/mypack/function/goto_arena.mcfunction

execute in mypack:arena run tp @s 0 65 0

Running this function teleports you into the mypack:arena dimension and drops you at coordinates (0, 65, 0). (If you took the “give the arena a floor” Try It, you’ll land on or above the bedrock; if you kept the pure void, you’ll need something to stand on; see “What Can Go Wrong.”) That single line is the whole journey: execute in switches the execution dimension to yours, and run tp teleports you there. To get back, point another function at minecraft:overworld the same way.

Figure (to be captured). the chat showing the goto_arena function running, and the player now standing in the new dimension

Noise generators and biome sources

The arena used the flat generator because a void is the cleanest possible example. But the real worlds (anything with hills, caves, oceans, ores) use the noise generator. You won’t master noise generation until Chapter 45, but you should understand the shape of a noise dimension now, because it’s the other half of how dimensions work.

The noise generator takes two extra fields:

  • settings — “Settings for the noise generator.” This is either the identifier of a noise settings file or an inline settings object. Noise settings are “for generating the shape of the terrain and noise caves, and what blocks the terrain is generated with,” stored at data/<namespace>/worldgen/noise_settings. There are ready-made vanilla ones you can point at by ID: minecraft:overworld, minecraft:amplified, minecraft:nether, minecraft:caves, minecraft:end, and minecraft:floating_islands.
  • biome_source — “Settings determining the biome layout.” This object has its own type choosing how biomes are arranged.

The biome-source type values are:

  • fixed — “uses one specified biome everywhere.” Its one extra field is biome: the single biome to use.
  • multi_noise — spreads many biomes across the world using climate noise. It takes either a preset (“The default parameter lists are overworld and nether”) or an explicit biomes list of parameter points.
  • checkerboard — “places biomes in a checkerboard pattern,” from a biomes list and an optional scale.
  • the_end — “The biome source used for the End dimension. This biome source has no additional fields.”

So the simplest custom terrain dimension you could build is a noise generator pointed at a vanilla noise settings preset, with a fixed biome source. Here’s the structure:

data/mypack/dimension/canyon.json

{
  "type": "minecraft:overworld",
  "generator": {
    "type": "minecraft:noise",
    "settings": "minecraft:overworld",
    "biome_source": {
      "type": "minecraft:fixed",
      "biome": "minecraft:desert"
    }
  }
}

This makes a dimension that generates with the Overworld’s terrain shape but is desert everywhere, using the built-in minecraft:overworld dimension type. Notice we reused a vanilla dimension type (minecraft:overworld) here instead of writing one. Both files are independent, so you can mix and match.

The inside of a noise settings file is Chapter 45. A noise settings file contains a “noise router” (a collection of density functions for terrain, biome layout, aquifers, and ore veins) and a “surface rule” (which blocks make the surface). Writing your own noise settings (density functions, the router, surface rules, carvers) is exactly what Chapter 45 covers, so we’ll save the internals for there. For this chapter, always point settings at one of the vanilla preset IDs above; don’t hand-write the object.

Practice

1. A sky dimension of floating slabs. Build a second dimension, mypack:sky, that’s a thin floating platform high in an otherwise empty world: the start of a sky-island minigame. Make a dimension type for it (you can copy arena.json and rename it), then write the dimension file with a flat generator whose layers stack a few solid blocks on top of empty space below. Because flat layers start at the world bottom and the lowest layer fills first, put your solid blocks as the first layers and leave it at that. Everything above the last layer is open sky:

data/mypack/dimension/sky.json

{
  "type": "mypack:sky",
  "generator": {
    "type": "minecraft:flat",
    "settings": {
      "biome": "minecraft:plains",
      "layers": [
        {
          "block": "minecraft:stone",
          "height": 3
        },
        {
          "block": "minecraft:grass_block",
          "height": 1
        }
      ]
    }
  }
}

That’s a four-block-thick slab (three stone, one grass) over the plains biome, with open air above it and the void below. Don’t forget the matching data/mypack/dimension_type/sky.json, and reboot the world before you execute in mypack:sky run tp @s 0 100 0 to visit it.

2. A teleport pair. Write two functions, goto_arena (you already have it) and goto_overworld, each using execute in <dimension> run tp so a player can hop into the arena and back out. Hook them up however you like: a Chapter 34 trigger, a dialog button from Chapter 39, or just run them by hand.

3. (Try It, harder) A fixed-biome noise world. Using the canyon.json structure above as a model, make a noise dimension that generates with minecraft:floating_islands settings and a fixed biome source set to minecraft:the_end: a custom End-island world reachable from the Overworld. Point its type at minecraft:the_end so it gets End-style physics.

What Can Go Wrong

/reload did nothing, and the dimension isn’t there. This is the single most common surprise. Dimensions are a dynamic registry; the game reads them only when the world loads. /reload rebuilds functions and recipes, but not dimensions. The fix is always the same: quit to the title screen and reopen the world. If you edit a dimension or dimension-type file later, you reboot again. There’s no /reload shortcut for these.

The world won’t load, or the dimension is missing after a reboot. Almost always a bad value in the dimension type. The usual culprits: min_y or height that isn’t a multiple of 16, a height outside the 16–4064 range, or a logical_height larger than height. Re-check those three numbers against the rules in the field list. A misspelled field name or a missing comma will do it too, the same JSON-shape mistakes from Chapter 8.

I teleported in and fell forever. If you kept the pure-air void (one layer of air, no floor) and teleported a player in with nothing to stand on, they’ll drop through the empty world. Either give the dimension a floor (the “give the arena a floor” Try It), build a platform with /setblock or /fill before sending players in, or teleport them onto a structure you’ve placed. A void is meant to be empty; it’s your job to put something under the players.

Modern Minecraft — you don’t need a mod for this. Older tutorials treat custom dimensions as a Forge/Fabric-only feature, because for years they were. Since the world-generation data-pack system landed, a plain data pack can add dimensions with no mod loader at all: just the two JSON files you wrote in this chapter. If a guide tells you to install a mod to make a new dimension, it’s out of date for current Java Edition.