Wiki 9Minecraft Wiki 9Minecraft

Orbital Camera Command and Script Recipe

Create a smooth 360-degree rotating camera around a player, block, or entity using minecraft:free.

5Sections
3Code examples
3Known errors

An orbital camera circles a fixed focal point in the world while keeping its lens locked onto the center of the circle. Add-on creators use this technique for lobby fly-throughs, boss introductions, and build showcases where a static angle feels too flat. Because Minecraft Bedrock does not expose a native orbit sub-command, you build the effect by driving the `minecraft:free` camera preset along a calculated circular path on a repeating tick timer.

How Orbital Motion Works in Bedrock

The `/camera` command lets you detach the player's viewport from their body when you select the `minecraft:free` preset. Unlike the first-person or third-person presets, the free camera stays exactly where you put it in world space and only moves when you issue another `/camera` instruction. To make that camera orbit a target, you must continuously feed it new coordinates positioned along the circumference of a horizontal circle, while simultaneously telling it to face the center of that circle.

Every step of the orbit requires three inputs: a center coordinate, a radius in blocks, and a current angle. For a given angle θ (in radians), the camera's X offset from the center is `cos(θ) * radius` and its Z offset is `sin(θ) * radius`. The Y coordinate is usually held slightly above the center point so the camera looks down at a mild pitch rather than staring horizontally through grass and fences. If you increment the angle slightly on every game tick and apply a short linear ease between updates, the discrete jumps blend together into a smooth, continuous rotation.

You have two practical ways to drive this loop inside a Behavior Pack. If you need a dynamic orbit that can follow a moving entity, adjust its radius on the fly, or run at arbitrary speeds without hundreds of lines of `.mcfunction` boilerplate, use the `@minecraft/server` Script API. If you are building a pure command-block map or want a fixed showcase loop around a static monument without enabling scripts, a scoreboard counter paired with stepped `.mcfunction` waypoints gets the job done reliably.

Command ArgumentWhat it doesNotes
minecraft:freeDetaches the camera from the player entityRequired preset for custom world-space positioning
ease <time> <type>Interpolates movement from the previous camera stateUse 'linear' with a duration matching your update interval
pos <x> <y> <z>Sets the world coordinates of the camera lensSupports absolute numbers, tilde (~) relative, and caret (^) local coordinates
facing <x> <y> <z>Rotates the lens to look directly at a coordinateCan be swapped for 'facing <entity>' to track a moving mob or player
clearReturns the player's camera back to their normal perspectiveAlways run this when your cutscene or showcase ends

Dynamic Orbit with the Script API

Before writing any TypeScript or JavaScript, your Behavior Pack `manifest.json` must declare a `script` module and a dependency on `@minecraft/server`. The `Player.camera` API exists in stable releases, meaning you do not need to turn on the Beta APIs experimental toggle on the world so long as you use the command executor or the stable camera methods. Using `runCommandAsync` or the native camera interface inside a `system.runInterval` loop gives you full trigonometric control via standard JavaScript `Math.cos` and `Math.sin`.

When setting up the manifest, make sure the `entry` field points to the exact path of your script file inside the `scripts/` folder. If the version of `@minecraft/server` in your manifest does not match what your target Bedrock client supports, the pack will fail to load the script engine entirely.

BP/manifest.json
{
  "format_version": 2,
  "header": {
    "name": "Orbital Camera Demo",
    "description": "Demonstrates a scripted orbital camera setup.",
    "uuid": "a183b290-4c5d-4e6f-8a9b-0c1d2e3f4a5b",
    "version": [1, 0, 0],
    "min_engine_version": [1, 20, 80]
  },
  "modules": [
    {
      "type": "script",
      "language": "javascript",
      "uuid": "b294c301-5d6e-4f7a-9b0c-1d2e3f4a5b6c",
      "entry": "scripts/main.js",
      "version": [1, 0, 0]
    }
  ],
  "dependencies": [
    {
      "module_name": "@minecraft/server",
      "version": "1.11.0"
    }
  ]
}

Writing the Orbit Loop in JavaScript

The cleanest way to run a scripted orbit is to update the camera every 2 game ticks (0.1 seconds) and apply a 0.1-second `linear` ease to every step. Updating every single tick (0.05 seconds) can occasionally cause visual micro-stuttering if the server thread experiences a slight delay, because the previous ease finishes before the next command arrives. A 2-tick interval with a matching 0.1-second linear ease gives the client renderer enough buffer to keep the motion completely fluid.

In the script below, we listen for a `/scriptevent` trigger so you can start and stop the orbit from a command block, dialogue button, or function file. When a player receives the `wiki:start_orbit` event, the script captures their current location as the center point, steps an angle forward on an interval, and uses the `/camera` command structure to position the lens 8 blocks out and 3 blocks up. Sending `wiki:stop_orbit` clears the interval and restores the player's normal view.

BP/scripts/main.js
import { world, system } from "@minecraft/server";

const activeOrbits = new Map();

function startOrbit(player, radius = 8, heightOffset = 3, speedDegrees = 3) {
  stopOrbit(player);

  let currentAngle = 0;
  const tickInterval = 2;
  const easeSeconds = (tickInterval * 0.05).toFixed(2);

  const jobId = system.runInterval(() => {
    if (!player.isValid()) {
      system.clearRun(jobId);
      activeOrbits.delete(player.id);
      return;
    }

    const center = player.location;
    const radians = (currentAngle * Math.PI) / 180;

    const camX = (center.x + Math.cos(radians) * radius).toFixed(3);
    const camY = (center.y + heightOffset).toFixed(3);
    const camZ = (center.z + Math.sin(radians) * radius).toFixed(3);

    const focusX = center.x.toFixed(3);
    const focusY = (center.y + 1.0).toFixed(3);
    const focusZ = center.z.toFixed(3);

    player.runCommandAsync(
      `camera @s set minecraft:free ease ${easeSeconds} linear pos ${camX} ${camY} ${camZ} facing ${focusX} ${focusY} ${focusZ}`
    );

    currentAngle = (currentAngle + speedDegrees) % 360;
  }, tickInterval);

  activeOrbits.set(player.id, jobId);
}

function stopOrbit(player) {
  const existingJob = activeOrbits.get(player.id);
  if (existingJob !== undefined) {
    system.clearRun(existingJob);
    activeOrbits.delete(player.id);
  }
  if (player.isValid()) {
    player.runCommandAsync("camera @s clear");
  }
}

system.afterEvents.scriptEventReceive.subscribe((event) => {
  const player = event.sourceEntity;
  if (!player || player.typeId !== "minecraft:player") return;

  if (event.id === "wiki:start_orbit") {
    startOrbit(player, 8, 3, 4);
  } else if (event.id === "wiki:stop_orbit") {
    stopOrbit(player);
  }
});

Pure Command Function Alternative (Entity Pivot)

If you cannot use scripts in your project, you cannot compute sine and cosine directly inside an `.mcfunction` file. Instead, you let the game engine handle the trigonometry for you by using an invisible helper entity—typically an `armor_stand`—as a rotating pivot at the center of the circle. When you rotate the armor stand on each tick and execute a `/camera` command using local caret coordinates (`^ ^ ^`), the game projects the camera position outward along the armor stand's line of sight.

In local coordinate notation (`^left ^up ^forward`), placing the camera at `^ ^3 ^-8` puts the lens 3 blocks above the pivot entity and 8 blocks directly behind where the pivot is looking. By adding a small yaw rotation to the armor stand every tick (`~2 ~`), the point 8 blocks behind it sweeps around in a perfect circle. To set this up, summon an armor stand with a unique tag like `orbit_pivot` at your desired focal point, make it invisible with `/effect`, and run the following function every tick via `tick.json` or a repeating command block.

BP/functions/orbit/tick.mcfunction
# Advance the orbit timer for all players currently viewing the cutscene
scoreboard players add @a[tag=in_orbit] orbit_ticks 1

# Rotate the central pivot armor stand by 2.5 degrees per tick
execute as @e[type=minecraft:armor_stand,tag=orbit_pivot] at @s run tp @s ~ ~ ~ ~2.5 ~

# Position the camera 3 blocks up and 8 blocks behind the pivot, looking at the pivot
execute as @a[tag=in_orbit] at @e[type=minecraft:armor_stand,tag=orbit_pivot,c=1] run camera @s set minecraft:free ease 0.05 linear pos ^ ^3 ^-8 facing ~ ~1 ~

# End the orbit after 144 ticks (one full 360-degree rotation at 2.5 degrees/tick)
execute as @a[tag=in_orbit,scores={orbit_ticks=144..}] run camera @s clear
execute as @a[tag=in_orbit,scores={orbit_ticks=144..}] run scoreboard players set @s orbit_ticks 0
execute as @a[tag=in_orbit,scores={orbit_ticks=0}] run tag @s remove in_orbit

Controlling the Player Body and Chunks

One quirk of the `minecraft:free` preset is that it detaches the viewport without freezing the player's physical body. While the camera is orbiting a statue or a boss arena, the player can still press WASD, walk off a cliff, or take melee damage from nearby zombies. For a polished experience, apply `/inputpermission set @s movement disabled` when the orbit begins and re-enable it right after running `/camera @s clear`.

You should also keep an eye on the radius of your orbit relative to the world's simulation distance. The client only renders chunks and entities around the player's physical body or within a reasonable distance of the camera lens. If you build an orbit with a massive radius (for example, 80 blocks out from the center), the camera may swing through unrendered terrain or fail to show entities on the far side of the circle. Keeping your radius between 4 and 24 blocks ensures that entities stay rendered and lighting remains consistent throughout the entire rotation.

Which versions this applies to

The /camera command and the minecraft:free preset are stable in Minecraft Bedrock 1.20.30 and newer without any experimental toggles. The @minecraft/server script setup shown here runs on stable API versions from 1.20.80 onward.

When it does not work

What you seeWhyThe fix
The camera movement looks jerky or rubber-bands back and forth during the orbit.You are either using an ease curve like 'in_out_sine' or 'spring' which decelerates at every single tick step, or your ease duration is shorter than the time between command updates.Always use the 'linear' ease type for continuous multi-step paths, and make sure the ease duration in seconds equals or very slightly exceeds your tick interval (for example, 0.1 seconds for a 2-tick interval).
Command log displays: 'Syntax error: Unexpected "^": at "05 linear pos >>^<< ^3 ^-8"'Bedrock does not allow you to mix local caret coordinates (^ ^ ^) and relative tilde coordinates (~ ~ ~) within the same triplet, or you omitted the 'pos' keyword before the coordinates.Keep all three numbers in the 'pos' argument as carets (^ ^3 ^-8) and all three numbers in the 'facing' argument as tildes (~ ~1 ~). Never mix ~ and ^ inside the same X Y Z set.
The player gets stuck staring at the sky or an empty field after the orbit finishes, even when they walk around.Stopping the repeating function or clearing the script interval only stops sending new positions; the client stays locked in the 'minecraft:free' state until explicitly told to leave it.Always execute 'camera <target> clear' as part of your cleanup function or script teardown, and consider hooking the 'playerLeave' or death events so players do not remain locked if the sequence is interrupted.

Reference, straight from the game files

Read out of the game itself, so the ids match the version you are building for.