Each row is a camera pose. The first row is the start; duration is the ticks taken to glide from the previous row to this one (20 ticks is one second). Yaw 0 faces south, 90 faces west; pitch is degrees down.
The line is the route the camera travels through your keyframes in order. Marker 1 is the start. Top-down shows the ground track, side shows the height profile.
The rig handles position, the player handles camera angle. On cleanup the player is dismounted and the rig removed so nothing is left in the world.
# cutscene:play
# Camera path cutscene generated by minecraftmaps.com
# Place this at data/cutscene/function/play.mcfunction inside a datapack,
# then run it with: /function cutscene:play
# The nearest player rides an invisible text_display rig for smooth
# position, and is rotated each tick for the camera angle. Linear motion
# is exact; eased segments reshape the per-tick speed.
scoreboard objectives add cam_rig_t dummy
# First run: build the rig at keyframe 1, mount and aim the player.
execute unless entity @e[type=minecraft:text_display,tag=cam_rig,limit=1] run summon minecraft:text_display 0 78.38 0 {teleport_duration:0,Tags:["cam_rig"],Glowing:0b,text:'""',background:0,see_through:1b,billboard:"fixed"}
execute unless entity @a[tag=cam_rig_rider,limit=1] run tag @p add cam_rig_rider
execute unless entity @a[tag=cam_rig_rider,limit=1] as @a[tag=cam_rig_rider,limit=1] run ride @s mount @e[type=minecraft:text_display,tag=cam_rig,limit=1]
execute unless entity @a[tag=cam_rig_rider,limit=1] as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 10
execute unless score #cam_rig cam_rig_t matches 0.. run scoreboard players set #cam_rig cam_rig_t 0
scoreboard players add #cam_rig cam_rig_t 1
# Segment 1/3: glide rig position over 60t
execute if score #cam_rig cam_rig_t matches 1 run data merge entity @e[type=minecraft:text_display,tag=cam_rig,limit=1] {teleport_duration:60}
execute if score #cam_rig cam_rig_t matches 1 run tp @e[type=minecraft:text_display,tag=cam_rig,limit=1] 0 76.38 24
# Segment 2/3: glide rig position over 60t
execute if score #cam_rig cam_rig_t matches 61 run data merge entity @e[type=minecraft:text_display,tag=cam_rig,limit=1] {teleport_duration:60}
execute if score #cam_rig cam_rig_t matches 61 run tp @e[type=minecraft:text_display,tag=cam_rig,limit=1] 20 74.38 24
# Segment 3/3: glide rig position over 60t
execute if score #cam_rig cam_rig_t matches 121 run data merge entity @e[type=minecraft:text_display,tag=cam_rig,limit=1] {teleport_duration:60}
execute if score #cam_rig cam_rig_t matches 121 run tp @e[type=minecraft:text_display,tag=cam_rig,limit=1] 20 72.38 0
# Per-tick camera rotation (rotates the rider, not the rig).
execute if score #cam_rig cam_rig_t matches 1 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.9972
execute if score #cam_rig cam_rig_t matches 2 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.9889
execute if score #cam_rig cam_rig_t matches 3 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.975
execute if score #cam_rig cam_rig_t matches 4 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.9556
execute if score #cam_rig cam_rig_t matches 5 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.9306
execute if score #cam_rig cam_rig_t matches 6 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.9
execute if score #cam_rig cam_rig_t matches 7 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.8639
execute if score #cam_rig cam_rig_t matches 8 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.8222
execute if score #cam_rig cam_rig_t matches 9 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.775
execute if score #cam_rig cam_rig_t matches 10 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.7222
execute if score #cam_rig cam_rig_t matches 11 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.6639
execute if score #cam_rig cam_rig_t matches 12 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.6
execute if score #cam_rig cam_rig_t matches 13 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.5306
execute if score #cam_rig cam_rig_t matches 14 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.4556
execute if score #cam_rig cam_rig_t matches 15 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.375
execute if score #cam_rig cam_rig_t matches 16 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.2889
execute if score #cam_rig cam_rig_t matches 17 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.1972
execute if score #cam_rig cam_rig_t matches 18 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 9.1
execute if score #cam_rig cam_rig_t matches 19 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 8.9972
execute if score #cam_rig cam_rig_t matches 20 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 8.8889
execute if score #cam_rig cam_rig_t matches 21 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 8.775
execute if score #cam_rig cam_rig_t matches 22 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 8.6556
execute if score #cam_rig cam_rig_t matches 23 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 8.5306
execute if score #cam_rig cam_rig_t matches 24 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 8.4
execute if score #cam_rig cam_rig_t matches 25 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 8.2639
execute if score #cam_rig cam_rig_t matches 26 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 8.1222
execute if score #cam_rig cam_rig_t matches 27 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 7.975
execute if score #cam_rig cam_rig_t matches 28 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 7.8222
execute if score #cam_rig cam_rig_t matches 29 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 7.6639
execute if score #cam_rig cam_rig_t matches 30 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 7.5
execute if score #cam_rig cam_rig_t matches 31 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 7.3361
execute if score #cam_rig cam_rig_t matches 32 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 7.1778
execute if score #cam_rig cam_rig_t matches 33 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 7.025
execute if score #cam_rig cam_rig_t matches 34 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 6.8778
execute if score #cam_rig cam_rig_t matches 35 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 6.7361
execute if score #cam_rig cam_rig_t matches 36 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 6.6
execute if score #cam_rig cam_rig_t matches 37 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 6.4694
execute if score #cam_rig cam_rig_t matches 38 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 6.3444
execute if score #cam_rig cam_rig_t matches 39 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 6.225
execute if score #cam_rig cam_rig_t matches 40 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 6.1111
execute if score #cam_rig cam_rig_t matches 41 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 6.0028
execute if score #cam_rig cam_rig_t matches 42 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.9
execute if score #cam_rig cam_rig_t matches 43 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.8028
execute if score #cam_rig cam_rig_t matches 44 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.7111
execute if score #cam_rig cam_rig_t matches 45 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.625
execute if score #cam_rig cam_rig_t matches 46 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.5444
execute if score #cam_rig cam_rig_t matches 47 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.4694
execute if score #cam_rig cam_rig_t matches 48 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.4
execute if score #cam_rig cam_rig_t matches 49 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.3361
execute if score #cam_rig cam_rig_t matches 50 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.2778
execute if score #cam_rig cam_rig_t matches 51 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.225
execute if score #cam_rig cam_rig_t matches 52 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.1778
execute if score #cam_rig cam_rig_t matches 53 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.1361
execute if score #cam_rig cam_rig_t matches 54 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.1
execute if score #cam_rig cam_rig_t matches 55 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.0694
execute if score #cam_rig cam_rig_t matches 56 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.0444
execute if score #cam_rig cam_rig_t matches 57 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.025
execute if score #cam_rig cam_rig_t matches 58 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.0111
execute if score #cam_rig cam_rig_t matches 59 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5.0028
execute if score #cam_rig cam_rig_t matches 60 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 0 5
execute if score #cam_rig cam_rig_t matches 61 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 1.5 4.9167
execute if score #cam_rig cam_rig_t matches 62 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 3 4.8333
execute if score #cam_rig cam_rig_t matches 63 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 4.5 4.75
execute if score #cam_rig cam_rig_t matches 64 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 6 4.6667
execute if score #cam_rig cam_rig_t matches 65 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 7.5 4.5833
execute if score #cam_rig cam_rig_t matches 66 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 9 4.5
execute if score #cam_rig cam_rig_t matches 67 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 10.5 4.4167
execute if score #cam_rig cam_rig_t matches 68 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 12 4.3333
execute if score #cam_rig cam_rig_t matches 69 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 13.5 4.25
execute if score #cam_rig cam_rig_t matches 70 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 15 4.1667
execute if score #cam_rig cam_rig_t matches 71 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 16.5 4.0833
execute if score #cam_rig cam_rig_t matches 72 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 18 4
execute if score #cam_rig cam_rig_t matches 73 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 19.5 3.9167
execute if score #cam_rig cam_rig_t matches 74 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 21 3.8333
execute if score #cam_rig cam_rig_t matches 75 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 22.5 3.75
execute if score #cam_rig cam_rig_t matches 76 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 24 3.6667
execute if score #cam_rig cam_rig_t matches 77 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 25.5 3.5833
execute if score #cam_rig cam_rig_t matches 78 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 27 3.5
execute if score #cam_rig cam_rig_t matches 79 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 28.5 3.4167
execute if score #cam_rig cam_rig_t matches 80 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 30 3.3333
execute if score #cam_rig cam_rig_t matches 81 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 31.5 3.25
execute if score #cam_rig cam_rig_t matches 82 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 33 3.1667
execute if score #cam_rig cam_rig_t matches 83 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 34.5 3.0833
execute if score #cam_rig cam_rig_t matches 84 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 36 3
execute if score #cam_rig cam_rig_t matches 85 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 37.5 2.9167
execute if score #cam_rig cam_rig_t matches 86 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 39 2.8333
execute if score #cam_rig cam_rig_t matches 87 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 40.5 2.75
execute if score #cam_rig cam_rig_t matches 88 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 42 2.6667
execute if score #cam_rig cam_rig_t matches 89 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 43.5 2.5833
execute if score #cam_rig cam_rig_t matches 90 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 45 2.5
execute if score #cam_rig cam_rig_t matches 91 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 46.5 2.4167
execute if score #cam_rig cam_rig_t matches 92 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 48 2.3333
execute if score #cam_rig cam_rig_t matches 93 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 49.5 2.25
execute if score #cam_rig cam_rig_t matches 94 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 51 2.1667
execute if score #cam_rig cam_rig_t matches 95 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 52.5 2.0833
execute if score #cam_rig cam_rig_t matches 96 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 54 2
execute if score #cam_rig cam_rig_t matches 97 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 55.5 1.9167
execute if score #cam_rig cam_rig_t matches 98 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 57 1.8333
execute if score #cam_rig cam_rig_t matches 99 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 58.5 1.75
execute if score #cam_rig cam_rig_t matches 100 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 60 1.6667
execute if score #cam_rig cam_rig_t matches 101 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 61.5 1.5833
execute if score #cam_rig cam_rig_t matches 102 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 63 1.5
execute if score #cam_rig cam_rig_t matches 103 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 64.5 1.4167
execute if score #cam_rig cam_rig_t matches 104 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 66 1.3333
execute if score #cam_rig cam_rig_t matches 105 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 67.5 1.25
execute if score #cam_rig cam_rig_t matches 106 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 69 1.1667
execute if score #cam_rig cam_rig_t matches 107 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 70.5 1.0833
execute if score #cam_rig cam_rig_t matches 108 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 72 1
execute if score #cam_rig cam_rig_t matches 109 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 73.5 0.9167
execute if score #cam_rig cam_rig_t matches 110 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 75 0.8333
execute if score #cam_rig cam_rig_t matches 111 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 76.5 0.75
execute if score #cam_rig cam_rig_t matches 112 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 78 0.6667
execute if score #cam_rig cam_rig_t matches 113 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 79.5 0.5833
execute if score #cam_rig cam_rig_t matches 114 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 81 0.5
execute if score #cam_rig cam_rig_t matches 115 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 82.5 0.4167
execute if score #cam_rig cam_rig_t matches 116 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 84 0.3333
execute if score #cam_rig cam_rig_t matches 117 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 85.5 0.25
execute if score #cam_rig cam_rig_t matches 118 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 87 0.1667
execute if score #cam_rig cam_rig_t matches 119 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 88.5 0.0833
execute if score #cam_rig cam_rig_t matches 120 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 90 0
execute if score #cam_rig cam_rig_t matches 121 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 92.975 -0.1653
execute if score #cam_rig cam_rig_t matches 122 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 95.9 -0.3278
execute if score #cam_rig cam_rig_t matches 123 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 98.775 -0.4875
execute if score #cam_rig cam_rig_t matches 124 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 101.6 -0.6444
execute if score #cam_rig cam_rig_t matches 125 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 104.375 -0.7986
execute if score #cam_rig cam_rig_t matches 126 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 107.1 -0.95
execute if score #cam_rig cam_rig_t matches 127 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 109.775 -1.0986
execute if score #cam_rig cam_rig_t matches 128 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 112.4 -1.2444
execute if score #cam_rig cam_rig_t matches 129 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 114.975 -1.3875
execute if score #cam_rig cam_rig_t matches 130 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 117.5 -1.5278
execute if score #cam_rig cam_rig_t matches 131 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 119.975 -1.6653
execute if score #cam_rig cam_rig_t matches 132 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 122.4 -1.8
execute if score #cam_rig cam_rig_t matches 133 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 124.775 -1.9319
execute if score #cam_rig cam_rig_t matches 134 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 127.1 -2.0611
execute if score #cam_rig cam_rig_t matches 135 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 129.375 -2.1875
execute if score #cam_rig cam_rig_t matches 136 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 131.6 -2.3111
execute if score #cam_rig cam_rig_t matches 137 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 133.775 -2.4319
execute if score #cam_rig cam_rig_t matches 138 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 135.9 -2.55
execute if score #cam_rig cam_rig_t matches 139 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 137.975 -2.6653
execute if score #cam_rig cam_rig_t matches 140 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 140 -2.7778
execute if score #cam_rig cam_rig_t matches 141 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 141.975 -2.8875
execute if score #cam_rig cam_rig_t matches 142 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 143.9 -2.9944
execute if score #cam_rig cam_rig_t matches 143 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 145.775 -3.0986
execute if score #cam_rig cam_rig_t matches 144 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 147.6 -3.2
execute if score #cam_rig cam_rig_t matches 145 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 149.375 -3.2986
execute if score #cam_rig cam_rig_t matches 146 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 151.1 -3.3944
execute if score #cam_rig cam_rig_t matches 147 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 152.775 -3.4875
execute if score #cam_rig cam_rig_t matches 148 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 154.4 -3.5778
execute if score #cam_rig cam_rig_t matches 149 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 155.975 -3.6653
execute if score #cam_rig cam_rig_t matches 150 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 157.5 -3.75
execute if score #cam_rig cam_rig_t matches 151 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 158.975 -3.8319
execute if score #cam_rig cam_rig_t matches 152 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 160.4 -3.9111
execute if score #cam_rig cam_rig_t matches 153 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 161.775 -3.9875
execute if score #cam_rig cam_rig_t matches 154 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 163.1 -4.0611
execute if score #cam_rig cam_rig_t matches 155 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 164.375 -4.1319
execute if score #cam_rig cam_rig_t matches 156 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 165.6 -4.2
execute if score #cam_rig cam_rig_t matches 157 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 166.775 -4.2653
execute if score #cam_rig cam_rig_t matches 158 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 167.9 -4.3278
execute if score #cam_rig cam_rig_t matches 159 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 168.975 -4.3875
execute if score #cam_rig cam_rig_t matches 160 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 170 -4.4444
execute if score #cam_rig cam_rig_t matches 161 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 170.975 -4.4986
execute if score #cam_rig cam_rig_t matches 162 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 171.9 -4.55
execute if score #cam_rig cam_rig_t matches 163 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 172.775 -4.5986
execute if score #cam_rig cam_rig_t matches 164 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 173.6 -4.6444
execute if score #cam_rig cam_rig_t matches 165 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 174.375 -4.6875
execute if score #cam_rig cam_rig_t matches 166 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 175.1 -4.7278
execute if score #cam_rig cam_rig_t matches 167 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 175.775 -4.7653
execute if score #cam_rig cam_rig_t matches 168 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 176.4 -4.8
execute if score #cam_rig cam_rig_t matches 169 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 176.975 -4.8319
execute if score #cam_rig cam_rig_t matches 170 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 177.5 -4.8611
execute if score #cam_rig cam_rig_t matches 171 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 177.975 -4.8875
execute if score #cam_rig cam_rig_t matches 172 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 178.4 -4.9111
execute if score #cam_rig cam_rig_t matches 173 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 178.775 -4.9319
execute if score #cam_rig cam_rig_t matches 174 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 179.1 -4.95
execute if score #cam_rig cam_rig_t matches 175 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 179.375 -4.9653
execute if score #cam_rig cam_rig_t matches 176 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 179.6 -4.9778
execute if score #cam_rig cam_rig_t matches 177 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 179.775 -4.9875
execute if score #cam_rig cam_rig_t matches 178 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 179.9 -4.9944
execute if score #cam_rig cam_rig_t matches 179 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 179.975 -4.9986
execute if score #cam_rig cam_rig_t matches 180 as @a[tag=cam_rig_rider,limit=1] run tp @s ~ ~ ~ 180 -5
# Re-schedule until the path ends.
execute if score #cam_rig cam_rig_t matches ..179 run schedule function cutscene:play 1t
# Cleanup on the final tick.
execute if score #cam_rig cam_rig_t matches 180.. as @a[tag=cam_rig_rider,limit=1] run ride @s dismount
execute if score #cam_rig cam_rig_t matches 180.. run kill @e[type=minecraft:text_display,tag=cam_rig,limit=1]
execute if score #cam_rig cam_rig_t matches 180.. run tag @a[tag=cam_rig_rider] remove cam_rig_rider
execute if score #cam_rig cam_rig_t matches 180.. run scoreboard players reset #cam_rig cam_rig_t
Put this file at data/cutscene/function/play.mcfunction inside a datapack, reload, then run it.
1. Drop the file into a datapack at the path shown above and run /reload.
2. Stand where the cutscene should start, then run /function cutscene:play. The nearest player is mounted on the rig and carried along the path.
3. Press F1 to hide the HUD and the held item for a clean shot. When the path ends the function dismounts you and removes the rig.
One cutscene at a time. The function refuses to start a second show while a rig already exists, so do not run it again, or have two people start it together, until the first run has finished.
How the angle works. Riding only moves your position, never your view, so the function turns the camera by rotating you each tick. Linear keeps a constant turn rate; the eased options speed the turn up or slow it down across the segment.
The rig is an invisible text_display. It has no hitbox and is removed on cleanup, so it leaves nothing behind in the world.
This tool turns a list of camera keyframes into a single runnable .mcfunction. Each keyframe is a position and an angle; each segment has a length in ticks. The exported function summons an invisible display entity at the start pose, mounts the nearest player on it with /ride so movement is smooth, and rotates that player every tick to aim the camera. The result is a controlled flythrough, all in vanilla Java with no mods.
Position and rotation are two separate jobs. For position, a display entity can be interpolated: set its teleport_duration and the client smoothly tweens its position to the new value over that many ticks instead of jumping. Seat the player on it with /ride and the camera position inherits that glide for free.
Rotation is the catch most camera tutorials get wrong. A vehicle in Minecraft only carries a passenger's position, never their view, so riding can never turn the camera. The fix is to rotate the player directly with /tp using relative position and absolute yaw and pitch, once per tick. The generated function drives both with a per-tick loop: a /schedule self-call advances a scoreboard counter, fires each segment glide at its boundary, steps the camera angle, and on the final tick dismounts the player and removes the rig, leaving the world clean.
Each row in the editor is one camera pose. Here is what every column controls:
The two previews read your keyframes live. The top-down view plots X against Z, so it shows the ground track of the shot; the side view plots X against Y, so it shows the height profile. Marker 1 is always the start.
Two different parts of the camera ease differently. The position is interpolated by the engine over teleport_duration, and the engine only moves in a straight line at constant speed, so the travel between keyframes is always linear no matter which easing you pick.
The camera angle is set by the function itself, one exact yaw and pitch per tick, so the easing applies fully to the turn. ease in starts the turn slowly and speeds up; ease out begins fast and settles into the next angle; ease in-out does both. Because the angle is written per tick, the curve is exact, not an approximation.
1. Use linear for orbits, steady flythroughs and anything where a constant turn rate reads well.
2. Use ease in-out on segments where the camera changes direction, so the look does not snap from one heading to the next.
3. For an eased path, not just an eased turn, split a long move into more keyframes. The position itself cannot ease, so extra keyframes are how you fake acceleration in the travel.
If you want to read or tweak the exported function by hand, these are the moving parts:
Display entities, the /ride command, /tp and /schedule are vanilla Java features. Display entities and /ride arrived in 1.19.4 and teleport_duration in 1.20.2, so the generated cutscene runs on any release from 1.20.2 onward. To run it you need a datapack and command permission, either single player with cheats on or operator on a server.
Place keyframes for each camera pose, give each segment a length in ticks, then export the .mcfunction this tool builds. Drop the file into a datapack at data/<namespace>/function/<name>.mcfunction, run /reload, stand at the start and run /function <namespace>:<name>. The function summons an invisible display entity, mounts the nearest player on it for smooth movement, and rotates that player every tick to aim the camera, so you ride a controlled camera path with no mods.
Position and rotation are handled separately. For position, the rig is a display entity with a teleport_duration tag: when it is teleported, the client glides it to the new spot over that many ticks instead of snapping, and the riding player moves with it. Rotation cannot come from riding, because a vehicle in Minecraft only sets a passenger's position and never its view angle. So the function turns the camera by teleporting the player's own yaw and pitch a small step every tick. The two together give a smooth flythrough that also looks where you told it to.
Rotation can ease. Each tick the function sets the player's exact yaw and pitch for that moment, so the ease in, ease out and ease in-out curves shape the turn precisely: ease in starts the turn slow, ease out finishes it slow. Position is interpolated by the engine, which only moves in a straight line at constant speed between keyframes, so the path itself does not ease. For an eased look on a long move, split it into more keyframes. Linear gives a constant turn rate and constant travel speed.
No. The output is plain vanilla Java commands using display entities, the /ride command, /tp and /schedule. Display entities and /ride arrived in 1.19.4 and teleport_duration in 1.20.2, so any release from 1.20.2 onward runs the cutscene as generated. It works in single player with cheats on and on a server where you can load a datapack.
By default the generated function dismounts the player and removes the rig once the last tick fires, leaving nothing in the world. To stop a cutscene early, run /tag @s remove cam_rig_rider then /ride @s dismount, and /kill @e[type=text_display,tag=cam_rig] (swap in your rig tag). Clear the pending self-call with /schedule clear <namespace>:<name> so it does not resume, then /scoreboard players reset #cam_rig cam_rig_t to free the tick counter.
Two things to check. First, yaw uses Minecraft's convention where 0 faces south, 90 faces west, 180 north and 270 east, so set angles to match the F3 screen. Second, the camera always turns the shortest way between keyframes, so a yaw change from 10 to 350 degrees spins 20 degrees the short way rather than all the way around. To force the long path, add an in-between keyframe partway through the turn.
Need to place the display entities by hand, or browse more Minecraft tools: