Cutscene System
Cutscene System
Section titled “Cutscene System”Cutscenes are scripted cinematic sequences that take control of the player’s camera and play a series of keyframed actions — camera movements, NPC animations, dialogue, sound effects, particle effects, and more. They’re powered by Ceremony’s cutscene engine under the hood.
Quick Start
Section titled “Quick Start”Here’s a simple cutscene that pans the camera to a location and shows dialogue:
File: config/journey/cutscenes/professor_intro.json
{ "id": "professor_intro", "duration": 8000, "keyframes": [ { "tick": 0, "type": "camera", "subtype": "move_to", "data": { "x": 100.5, "y": 72.0, "z": 200.5, "yaw": 180.0, "pitch": -10.0, "duration": 2000, "easing": "ease_in_out" } }, { "tick": 2000, "type": "dialogue", "subtype": "show", "data": { "speaker": "Professor Willow", "text": "<gold>Welcome to the world of Pokemon!", "duration": 3000 } }, { "tick": 5000, "type": "dialogue", "subtype": "show", "data": { "speaker": "Professor Willow", "text": "<gray>Your adventure begins here. Choose wisely!", "duration": 2500 } } ], "onComplete": "q.player.add_flag('intro_cutscene_seen');", "onCancel": ""}Cutscene Structure
Section titled “Cutscene Structure”| Field | Type | Required | Description |
|---|---|---|---|
id | String | Yes | Unique identifier for the cutscene |
duration | Number | Yes | Total duration in milliseconds |
keyframes | Array | Yes | Array of keyframe actions (see below) |
onComplete | String | No | MoLang script to run when the cutscene finishes normally |
onCancel | String | No | MoLang script to run if the player cancels the cutscene |
Keyframe Format
Section titled “Keyframe Format”Each keyframe fires an action at a specific moment during the cutscene:
{ "tick": 0, "type": "camera", "subtype": "move_to", "data": { }}| Field | Type | Description |
|---|---|---|
tick | Number | When this action fires (milliseconds from cutscene start) |
type | String | Action category (see action types below) |
subtype | String | Specific action within the category |
data | Object | Action-specific parameters |
Action Types
Section titled “Action Types”Camera Actions
Section titled “Camera Actions”Control the player’s camera position and rotation.
| Subtype | Description |
|---|---|
move_to | Smoothly move the camera to a position |
look_at | Rotate camera to look at coordinates |
look_at_entity | Rotate camera to track an entity |
shake | Apply camera shake effect |
zoom | Change field of view |
reset | Return camera to player |
set_position | Instantly set camera position (no interpolation) |
move_to:
{ "tick": 0, "type": "camera", "subtype": "move_to", "data": { "x": 100.5, "y": 72.0, "z": 200.5, "yaw": 180.0, "pitch": -10.0, "duration": 2000, "easing": "ease_in_out" }}look_at:
{ "tick": 2000, "type": "camera", "subtype": "look_at", "data": { "x": 105.0, "y": 70.0, "z": 195.0, "duration": 1000, "easing": "ease_out" }}look_at_entity:
{ "tick": 2000, "type": "camera", "subtype": "look_at_entity", "data": { "entity_uuid": "npc-uuid-here", "duration": 1000, "easing": "ease_out" }}shake:
{ "tick": 5000, "type": "camera", "subtype": "shake", "data": { "intensity": 0.5, "duration": 1000, "frequency": 10.0 }}zoom:
{ "tick": 3000, "type": "camera", "subtype": "zoom", "data": { "fov": 30.0, "duration": 500, "easing": "ease_in" }}Entity Actions
Section titled “Entity Actions”Control NPCs and entities during the cutscene.
| Subtype | Description |
|---|---|
spawn | Spawn an entity at a position |
despawn | Remove an entity |
move_to | Move entity to a position |
look_at | Make entity face a position |
animate | Play an entity animation |
set_pose | Set entity body pose |
set_name | Set entity display name |
spawn:
{ "tick": 0, "type": "entity", "subtype": "spawn", "data": { "entity_type": "cobblemon:npc", "x": 100.0, "y": 70.0, "z": 200.0, "yaw": 180.0, "tag": "intro_professor" }}move_to:
{ "tick": 2000, "type": "entity", "subtype": "move_to", "data": { "tag": "intro_professor", "x": 105.0, "y": 70.0, "z": 200.0, "speed": 0.5, "duration": 2000 }}animate:
{ "tick": 4000, "type": "entity", "subtype": "animate", "data": { "tag": "intro_professor", "animation": "wave" }}Block Actions
Section titled “Block Actions”Modify blocks in the world during the cutscene.
| Subtype | Description |
|---|---|
set | Change a block at a position |
fill | Fill an area with blocks |
restore | Restore blocks to their original state |
interact | Simulate block interaction (doors, levers) |
set:
{ "tick": 3000, "type": "block", "subtype": "set", "data": { "x": 100, "y": 70, "z": 200, "block": "minecraft:redstone_lamp[lit=true]" }}Effect Actions
Section titled “Effect Actions”Particles, screen effects, and visual overlays.
| Subtype | Description |
|---|---|
particle | Spawn particles at a position |
screen_fade | Fade screen to/from black |
screen_tint | Apply a color tint overlay |
title | Show title text |
vignette | Apply vignette effect |
particle:
{ "tick": 5000, "type": "effect", "subtype": "particle", "data": { "particle": "minecraft:end_rod", "x": 100.0, "y": 72.0, "z": 200.0, "count": 20, "spread": 1.0 }}screen_fade:
{ "tick": 0, "type": "effect", "subtype": "screen_fade", "data": { "from_alpha": 1.0, "to_alpha": 0.0, "duration": 1000, "color": "#000000" }}title:
{ "tick": 2000, "type": "effect", "subtype": "title", "data": { "title": "<gold>Chapter 1", "subtitle": "<gray>A New Beginning", "fade_in": 500, "stay": 2000, "fade_out": 500 }}Sound Actions
Section titled “Sound Actions”Play sounds and music.
{ "tick": 0, "type": "sound", "subtype": "play", "data": { "sound": "minecraft:music_disc.otherside", "volume": 0.8, "pitch": 1.0, "x": 100.0, "y": 70.0, "z": 200.0 }}To stop a sound:
{ "tick": 8000, "type": "sound", "subtype": "stop", "data": { "sound": "minecraft:music_disc.otherside" }}Dialogue Actions
Section titled “Dialogue Actions”Show dialogue boxes with speaker names.
{ "tick": 2000, "type": "dialogue", "subtype": "show", "data": { "speaker": "Professor Willow", "text": "<white>Your journey begins today!", "duration": 3000 }}To clear dialogue:
{ "tick": 5000, "type": "dialogue", "subtype": "clear", "data": {}}Special Actions
Section titled “Special Actions”| Subtype | Description |
|---|---|
command | Run a server command |
script | Run a MoLang script |
command:
{ "tick": 6000, "type": "special", "subtype": "command", "data": { "command": "give {player} cobblemon:poke_ball 5" }}script:
{ "tick": 7000, "type": "special", "subtype": "script", "data": { "script": "q.player.add_flag('intro_seen'); q.player.start_task('journey:first_quest');" }}Easing Functions
Section titled “Easing Functions”Camera and entity movements support easing for smooth transitions:
| Easing | Description |
|---|---|
linear | Constant speed |
ease_in | Start slow, end fast |
ease_out | Start fast, end slow |
ease_in_out | Slow start and end, fast middle |
Player State Management
Section titled “Player State Management”When a cutscene starts, Journey automatically:
- Saves the player’s current position, rotation, gamemode, and HUD state
- Locks player movement and input
- Hides the HUD (health, hunger, hotbar)
- Disables interaction with the world
When the cutscene ends (or is cancelled), all state is restored — the player returns to exactly where they were with the same gamemode and HUD settings.
Cancellation
Section titled “Cancellation”Players can cancel cutscenes by pressing a configured key (default: Escape). When cancelled:
- The
onCancelscript runs (if defined) - Player state is restored
- Any spawned cutscene entities are cleaned up
- Block changes are reverted
If onCancel is empty, cancellation simply restores the player. Use onCancel to handle cleanup that differs from normal completion:
{ "onComplete": "q.player.add_flag('watched_intro');", "onCancel": "q.player.tell_minimessage('<yellow>You can replay this cutscene later.');"}MoLang Functions
Section titled “MoLang Functions”Play and control cutscenes from any MoLang context:
| Function | Returns | Description |
|---|---|---|
q.player.play_cutscene('cutscene_id') | 1.0 | Start a cutscene for the player |
q.player.stop_cutscene() | 1.0 | Stop the current cutscene |
q.player.in_cutscene() | 1.0 / 0.0 | Check if the player is in a cutscene |
q.player.cutscene_progress() | 0.0 - 1.0 | Get current cutscene progress |
Example: Play Cutscene as Task Reward
Section titled “Example: Play Cutscene as Task Reward”{ "type": "script", "data": { "scripts": [ "q.player.play_cutscene('gym_victory_cinematic');" ] }}Example: Condition Based on Cutscene State
Section titled “Example: Condition Based on Cutscene State”{ "filter": "!q.player.in_cutscene()"}Complete Example: Gym Leader Introduction
Section titled “Complete Example: Gym Leader Introduction”A cutscene that pans to the gym leader, shows dialogue, then returns control to the player.
File: config/journey/cutscenes/brock_intro.json
{ "id": "brock_intro", "duration": 12000, "keyframes": [ { "tick": 0, "type": "effect", "subtype": "screen_fade", "data": { "from_alpha": 0.0, "to_alpha": 1.0, "duration": 500, "color": "#000000" } }, { "tick": 500, "type": "camera", "subtype": "set_position", "data": { "x": 500.5, "y": 75.0, "z": -200.5, "yaw": 0.0, "pitch": -15.0 } }, { "tick": 500, "type": "effect", "subtype": "screen_fade", "data": { "from_alpha": 1.0, "to_alpha": 0.0, "duration": 1000, "color": "#000000" } }, { "tick": 1500, "type": "camera", "subtype": "move_to", "data": { "x": 500.5, "y": 72.0, "z": -195.0, "yaw": 0.0, "pitch": -5.0, "duration": 2000, "easing": "ease_in_out" } }, { "tick": 3500, "type": "sound", "subtype": "play", "data": { "sound": "minecraft:block.note_block.pling", "volume": 0.8, "pitch": 1.2 } }, { "tick": 4000, "type": "dialogue", "subtype": "show", "data": { "speaker": "Brock", "text": "<gray>So, you've made it this far...", "duration": 3000 } }, { "tick": 7000, "type": "dialogue", "subtype": "show", "data": { "speaker": "Brock", "text": "<gold>I am Brock, the Pewter City Gym Leader!", "duration": 3000 } }, { "tick": 10000, "type": "effect", "subtype": "screen_fade", "data": { "from_alpha": 0.0, "to_alpha": 1.0, "duration": 1000, "color": "#000000" } }, { "tick": 11000, "type": "camera", "subtype": "reset", "data": {} }, { "tick": 11000, "type": "effect", "subtype": "screen_fade", "data": { "from_alpha": 1.0, "to_alpha": 0.0, "duration": 1000, "color": "#000000" } } ], "onComplete": "q.player.add_flag('met_brock');", "onCancel": ""}File Location
Section titled “File Location”Cutscene files go in config/journey/cutscenes/:
config/journey/cutscenes/├── professor_intro.json├── brock_intro.json├── gym_victory.json└── story/ ├── chapter1_opening.json └── chapter1_ending.jsonThe cutscene ID matches the filename (without extension). Use this ID in q.player.play_cutscene().
- Fade to black between camera position changes for smooth transitions
- Keep dialogue on screen for at least 2-3 seconds so players can read
- Test timing in-game — what feels right in JSON may need adjustment
- Use
onCancelto handle players who skip the cutscene - Layer sound with camera movements for immersive effect
- Start simple — a camera pan + dialogue is a great first cutscene
- For quick dialogue sequences without camera control, use Timelines instead