Why Bedrock lacks a native sphere command
Every native block-placing command in Bedrock Edition works on a rectangular grid. `/setblock` targets a single integer coordinate, and `/fill` takes two opposite corners of a cuboid. While target selectors have radial arguments like `r` (maximum radius) and `rm` (minimum radius) that define spherical detection zones, those arguments only filter entities—they cannot be passed directly to `/fill` to mask out a block volume.
Because of that limitation, generating a sphere requires translating spherical math into individual block updates. You have three practical ways to do this in a Behaviour Pack: rotating an entity across pitch and yaw angles while placing blocks at a fixed local distance (`^ ^ ^radius`), running a pre-calculated list of `/setblock` and `/fill` offsets inside an `.mcfunction` file, or looping over a 3D bounding box in JavaScript and testing each block's Euclidean distance from the center. The right choice depends on whether you need a fixed-radius effect, an interactive world-building brush, or a massive structure that must not freeze the server tick.
| Method | Best For | Drawbacks |
|---|---|---|
| Entity Rotation (`^ ^ ^r`) | Quick in-game carving or hollow shells without a Behaviour Pack | Slow to complete; leaves gaps at large radii if step angles are too wide |
| Static `.mcfunction` Offsets | Fixed-size bombs, spells, or arena hazards that trigger instantly | Radius is hardcoded; requires a separate function file for each size |
| Script API (`@minecraft/server`) | Dynamic world-edit brushes, variable radii, and custom block palettes | Requires scripting setup in `manifest.json`; large radii need tick-splitting |
The Entity Rotation Trick (Pure Commands)
If you are working purely with command blocks or basic functions, you can draw a sphere by summoning a marker entity (such as an `armor_stand`) at the center point and rotating it incrementally. Local coordinates (`^left ^up ^forward`) always resolve relative to the executing entity's facing direction. When an entity at the center of a sphere places a block at `^ ^ ^8`, that block lands exactly 8 blocks away along its line of sight. If you rotate the entity through all possible yaw (horizontal) and pitch (vertical) angles, the placed blocks trace out a hollow sphere.
To build a solid sphere or carve a spherical crater with this method, replace the single `/setblock` at the tip of the ray with several overlapping `/fill` commands stepped along the look vector, or use `/execute` to place blocks at `^ ^ ^1`, `^ ^ ^2`, and so on up to your radius. Keep in mind that `/fill` still draws an axis-aligned box even when given caret coordinates—it simply uses the two calculated caret points as opposite corners. A small box like `/fill ^-1 ^-1 ^8 ^1 ^1 ^8 stone` thickens the brush stroke so you can rotate the entity in larger degree steps (such as 5 or 10 degrees) without leaving pinholes in the shell.
In a Behaviour Pack, you can run a full latitude sweep in a single tick using recursive functions, or spread the horizontal rotation across multiple ticks using a repeating command block or `tick.json`. Below is a two-function setup where a master function rotates a tagged armor stand horizontally each tick and delegates the vertical pitch sweep to a helper function so an entire vertical rib completes instantly.
# Run every tick as and at the center armor_stand: /execute as @e[type=armor_stand,tag=sphere_gen] at @s run function sphere/step_yaw
# Reset pitch to straight up (-90) while keeping current yaw (~)
teleport @s ~ ~ ~ ~ -90
# Call the recursive pitch sweep to draw one vertical arc from top to bottom
function sphere/sweep_pitch
# Advance horizontal yaw by 6 degrees for the next tick
teleport @s ~ ~ ~ ~6 ~
# Track steps using a scoreboard so the entity cleans itself up after 60 ticks (360 degrees)
scoreboard players add @s sphere_steps 1
execute if score @s sphere_steps matches 60.. run kill @sCompleting the Vertical Arc in a Function
The helper function `sweep_pitch.mcfunction` handles the vertical angle from `-90` (straight up) to `90` (straight down). Because Bedrock command selectors do not have a built-in `rx` or `rxm` filter in the new execute syntax the way older command formats did, the cleanest way to stop the recursion is a scoreboard counter that resets every tick, or by running a fixed chain of commands.
When placing blocks at `^ ^ ^7`, using a 3x3x3 `/fill` brush (`^-1 ^-1 ^7 ^1 ^1 ^7`) patches the gaps between steps around the equator where the rays diverge widest. If you want to carve out terrain instead of building a shell, swap `minecraft:stone` for `minecraft:air` and run multiple `/fill` lines at `^ ^ ^2`, `^ ^ ^4`, and `^ ^ ^6` to hollow out the interior.
# Place a small 3x3x3 cluster 7 blocks ahead of the current facing angle
fill ^-1 ^-1 ^7 ^1 ^1 ^7 minecraft:stone
# Tilt pitch down by 6 degrees
teleport @s ~ ~ ~ ~ ~6
# Increment the vertical step counter
scoreboard players add @s pitch_step 1
# 180 degrees / 6 degrees per step = 30 steps. Recurse until we hit 30.
execute if score @s pitch_step matches ..29 run function sphere/sweep_pitch
# Reset the counter once the stack unwinds
scoreboard players set @s pitch_step 0Voxel Sphere Generation with the Script API
While the armor stand rotation trick is clever, it wastes work near the poles (where rays overlap heavily) and struggles to make clean, single-block-thick shells without overlapping `/fill` cubes. If your add-on already uses `@minecraft/server`, calculating the sphere in JavaScript is faster, completely accurate on the voxel grid, and lets you pass any radius or block type on the fly.
The standard algorithm iterates through a 3D bounding box from `-radius` to `+radius` on the X, Y, and Z axes relative to a center coordinate. For every offset `(dx, dy, dz)`, you compute the squared distance from the center: `dx * dx + dy * dy + dz * dz`. Comparing squared distances against `radius * radius` avoids calling `Math.sqrt()` thousands of times inside a tight triple loop. Adding `0.5` to the radius threshold (so `rSquared = (radius + 0.5) * (radius + 0.5)`) produces much rounder-looking voxel spheres in Minecraft than a strict integer comparison, which tends to leave flat single-block spikes sticking out of the six cardinal faces.
To triggers this from a command block, an NPC, or an `.mcfunction`, listen to ` system.afterEvents.scriptEventReceive`. This lets you create a custom command syntax like `/scriptevent wiki:sphere 6 minecraft:glass` or `/scriptevent wiki:hsphere 10 minecraft:sea_lantern` that executes at the caller's location.
import { system, BlockPermutation } from "@minecraft/server";
system.afterEvents.scriptEventReceive.subscribe((event) => {
if (event.id !== "wiki:sphere" && event.id !== "wiki:hsphere") return;
const source = event.sourceEntity || event.sourceBlock;
if (!source) return;
const dimension = source.dimension;
const center = source.location;
// Parse message: "<radius> [block_identifier]"
const args = event.message.trim().split(/\s+/);
const radius = Math.min(Math.max(parseInt(args[0], 10) || 5, 1), 32);
const blockName = args[1] || "minecraft:stone";
const isHollow = event.id === "wiki:hsphere";
let permutation;
try {
permutation = BlockPermutation.resolve(blockName);
} catch (e) {
return;
}
const maxDistSq = (radius + 0.5) * (radius + 0.5);
const minDistSq = isHollow ? (radius - 0.5) * (radius - 0.5) : -1;
const cx = Math.floor(center.x);
const cy = Math.floor(center.y);
const cz = Math.floor(center.z);
for (let dx = -radius; dx <= radius; dx++) {
for (let dy = -radius; dy <= radius; dy++) {
for (let dz = -radius; dz <= radius; dz++) {
const distSq = dx * dx + dy * dy + dz * dz;
if (distSq <= maxDistSq && distSq >= minDistSq) {
const block = dimension.getBlock({
x: cx + dx,
y: cy + dy,
z: cz + dz
});
if (block) {
block.setPermutation(permutation);
}
}
}
}
}
});Splitting Large Spheres Across Ticks with a Job Queue
Because the volume of a bounding box grows cubically (`(2r + 1)^3`), a radius of 10 checks 9,261 blocks, while a radius of 30 checks 226,981 blocks. Running hundreds of thousands of `dimension.getBlock()` and `block.setPermutation()` calls inside a single event callback will exceed the Script API watchdog time limit and halt your script execution, or cause a severe lag spike for everyone in the world.
For spheres larger than roughly 12 to 15 blocks in radius, replace the synchronous `for` loop with a generator function passed to `system.runJob()`. The Bedrock runtime automatically slices generator jobs across multiple ticks, yielding execution whenever the current tick's scripting budget runs low and resuming cleanly on the next tick. You simply place a `yield;` statement inside the innermost loop.
import { system, BlockPermutation } from "@minecraft/server";
function* generateSphereJob(dimension, center, radius, permutation, isHollow) {
const maxDistSq = (radius + 0.5) * (radius + 0.5);
const minDistSq = isHollow ? (radius - 0.5) * (radius - 0.5) : -1;
const cx = Math.floor(center.x);
const cy = Math.floor(center.y);
const cz = Math.floor(center.z);
for (let dx = -radius; dx <= radius; dx++) {
for (let dy = -radius; dy <= radius; dy++) {
for (let dz = -radius; dz <= radius; dz++) {
const distSq = dx * dx + dy * dy + dz * dz;
if (distSq <= maxDistSq && distSq >= minDistSq) {
try {
const block = dimension.getBlock({
x: cx + dx,
y: cy + dy,
z: cz + dz
});
if (block) {
block.setPermutation(permutation);
}
} catch (err) {
// Ignore blocks outside world height limits or in unloaded chunks
}
yield;
}
}
}
}
}
system.afterEvents.scriptEventReceive.subscribe((event) => {
if (event.id !== "wiki:bigsphere") return;
const source = event.sourceEntity || event.sourceBlock;
if (!source) return;
const args = event.message.trim().split(/\s+/);
const radius = Math.min(Math.max(parseInt(args[0], 10) || 15, 1), 64);
const blockName = args[1] || "minecraft:stone";
const isHollow = args[2] === "hollow";
try {
const perm = BlockPermutation.resolve(blockName);
system.runJob(
generateSphereJob(source.dimension, source.location, radius, perm, isHollow)
);
} catch (e) {
// Invalid block identifier passed in message
}
});Wiring the Script into manifest.json
For the `/scriptevent` sphere handler to run, your Behaviour Pack `manifest.json` must declare a `script` module pointing to your entry file and a dependency on `@minecraft/server`. Because `system.afterEvents.scriptEventReceive`, `system.runJob`, `Dimension.getBlock`, and `BlockPermutation.resolve` are all part of the stable 1.x release of `@minecraft/server`, you do not need to enable the Beta APIs toggle in world settings.
{
"format_version": 2,
"header": {
"name": "Sphere Command Utility",
"description": "Adds /scriptevent sphere builders using stable @minecraft/server",
"uuid": "a8f4910e-7b23-4c9a-9e12-403958172631",
"version": [1, 0, 0],
"min_engine_version": [1, 20, 80]
},
"modules": [
{
"type": "script",
"language": "javascript",
"uuid": "d2c19485-3a11-4f8b-8c44-918273645512",
"entry": "scripts/sphere_job.js",
"version": [1, 0, 0]
}
],
"dependencies": [
{
"module_name": "@minecraft/server",
"version": "1.11.0"
}
]
}Which versions this applies to
Applies to Bedrock Edition 1.19.50 and newer for the new `/execute` syntax used in `.mcfunction` files. The Script API examples use stable `@minecraft/server` 1.11.0+ APIs (Bedrock 1.20.80+) and do not require any experimental toggles.
When it does not work
| What you see | Why | The fix |
|---|---|---|
| Function sphere/sweep_pitch stopped midway with 'Function ... exceeded the maximum command limit of 10000' | A single `.mcfunction` invocation (including all nested recursive calls triggered within the same command) cannot execute more than 10,000 commands at once. If you try to loop both yaw and pitch inside the same tick via recursion, you will rapidly exceed 10,000 commands. | Run only the vertical pitch sweep recursively inside a single tick (around 150–300 commands total), and advance the horizontal yaw once per tick using a repeating command block or `BP/functions/tick.json`. |
| Script error: '[Scripting][error]-Script execution timed out' or watchdog warning when building a sphere with radius > 20 | Calling `dimension.getBlock()` and `block.setPermutation()` tens of thousands of times inside a synchronous `for` loop locks the server thread beyond the per-tick watchdog budget. | Convert your coordinate loop into a `function*` generator with a `yield;` after each placed block, and schedule it with `system.runJob()` so Bedrock spreads the work across multiple ticks. |
| The bottom or top of the sphere gets flattened or throws an unhandled exception in the Content Log | When the sphere's center is near the top or bottom of the dimension (for example below Y = -64 or above Y = 319 in the Overworld), `dimension.getBlock()` returns `undefined` or throws a location out-of-bounds error, and `/fill` fails with `Cannot place blocks outside of the world`. | In scripts, wrap `dimension.getBlock()` in a `try/catch` block or clamp `cy + dy` to `dimension.heightRange.min` and `dimension.heightRange.max - 1`. In command functions, avoid using multi-block `/fill` brushes right against the build limit. |
| Pure-command sphere has visible rings of empty air around the middle (equator) while the poles are solid | Angular steps spread further apart the further you move from the center and the closer the pitch gets to `0` (the horizon). At radius 15, a 10-degree rotation step skips more than 2.5 blocks horizontally. | Either decrease your teleport rotation increment (for example from `~10` down to `~3` or `~4`) or thicken the placement tip from `/setblock ^ ^ ^15` to a 3x3x3 `/fill ^-1 ^-1 ^15 ^1 ^1 ^15`. |
Reference, straight from the game files
Read out of the game itself, so the ids match the version you are building for.
