clips
Manage dashcam clips and analyze recorded gameplay frame by frame.
The clips tool interfaces with the dashcam system. The dashcam continuously buffers spatial data in memory. When you press F9 or the agent calls add_marker, the buffer is flushed to a clip file (SQLite). Clips can then be queried frame by frame.
How clips are created
Clips are saved in four ways:
| Trigger | Source | Description |
|---|---|---|
| Press F9 or click ⚑ in-game button | Human | Saves dashcam buffer as a "human" clip |
clips { "action": "add_marker" } | Agent | Saves dashcam buffer as an "agent" clip |
StageRuntime.marker() in GDScript | Code | Saves dashcam buffer from game code |
clips { "action": "save" } | Agent | Force-flush buffer immediately |
Each clip captures ring buffer contents (up to 60 seconds before the trigger) plus approximately 30 seconds of post-capture.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
action | any"add_marker" | "save" | "status" | "list" | "delete" | "markers" | "snapshot_at" | "trajectory" | "query_range" | "diff_frames" | "find_event" | "screenshot_at" | "screenshots" | required | Action to perform. |
at_frame | number | optional | Frame number for snapshot_at. |
at_time_ms | number | optional | Timestamp (ms) for snapshot_at. Finds nearest frame. |
clip_id | string | optional | Clip to operate on (from list response). Defaults to most recent clip if omitted. |
condition | any | optional | Condition for query_range. Object with "type" key. Types: "moved" (threshold), "proximity" (target, threshold), "velocity_spike" (threshold), "property_change" (property), "state_transition" (property), "signal_emitted" (signal), "entered_area", "collision". Example: {"type": "proximity", "target": "walls/*", "threshold": 0.5} |
detail | string | optional | Detail level for snapshot_at: "summary", "standard", "full". |
event_filter | string | optional | Event filter for find_event (substring match). |
event_type | string | optional | Event type for find_event. |
frame_a | number | optional | Frame A for diff_frames. |
frame_b | number | optional | Frame B for diff_frames. |
from_frame | number | optional | Start of frame range for query_range / find_event. |
marker_frame | number | optional | Frame to attach marker to (add_marker). Defaults to current. |
marker_label | string | optional | Marker label (add_marker, save). |
node | string | optional | Node path for query_range. |
properties | string[] | optional | Properties to sample in trajectory. Default: ["position"]. Options: position, rotation_deg, velocity, speed, or any state property name. |
sample_interval | number | optional | Sample every Nth frame for trajectory. Default: 1. |
to_frame | number | optional | End of frame range for query_range / find_event. |
token_budget | number | optional | Soft token budget. |
Actions
Clip management
add_marker
Mark the current moment and trigger a clip capture. This is what the F9 key triggers from the in-game flag button.
{
"action": "add_marker",
"marker_label": "player_clips_wall"
}| Parameter | Type | Description |
|---|---|---|
marker_label | string | Optional label for the marker |
marker_frame | integer | Frame to mark (defaults to current frame) |
Response:
{
"action": "add_marker",
"clip_id": "clip_1741987200",
"marker_id": "m_a1b2c3",
"marker_label": "player_clips_wall",
"result": "ok"
}save
Force-save the dashcam buffer as a clip immediately, without adding a marker.
{
"action": "save"
}Response:
{
"action": "save",
"clip_id": "clip_1741987200",
"result": "ok",
"frame_count": 512
}status
Get dashcam buffer state, buffer size, and configuration.
{
"action": "status"
}Response:
{
"action": "status",
"state": "buffering",
"buffer_frames": 1247,
"pre_window_deliberate_sec": 60,
"byte_cap_mb": 1024
}States: buffering (running normally), post_capture (saving post-trigger window), disabled (dashcam off).
list
List all available clips.
{
"action": "list"
}Response:
{
"clips": [
{
"clip_id": "chase_bug_01",
"frame_count": 512,
"duration_ms": 8533,
"created_at": "2026-03-12T14:30:00Z",
"markers": [
{ "marker_id": "m_a1b2c3", "frame": 337, "marker_label": "player_clips_wall" }
]
}
]
}delete
Remove a clip by clip_id.
{
"action": "delete",
"clip_id": "chase_bug_01"
}Response:
{
"action": "delete",
"clip_id": "chase_bug_01",
"result": "ok"
}markers
List all markers in a saved clip.
{
"action": "markers",
"clip_id": "chase_bug_01"
}Response:
{
"clip_id": "chase_bug_01",
"markers": [
{ "marker_id": "m_a1b2c3", "frame": 337, "marker_label": "player_clips_wall", "source": "human" }
]
}Clip analysis
snapshot_at
Get the spatial state at a specific frame.
{
"action": "snapshot_at",
"clip_id": "chase_bug_01",
"at_frame": 337,
"detail": "full"
}| Parameter | Type | Description |
|---|---|---|
clip_id | string | Which clip to query |
at_frame | integer | Frame number (use this or at_time_ms) |
at_time_ms | integer | Timestamp in ms (use this or at_frame) |
detail | string | "summary" or "full" |
Response:
{
"clip_id": "chase_bug_01",
"at_frame": 337,
"timestamp_ms": 5617,
"nodes": {
"Player": {
"class": "CharacterBody3D",
"global_position": [8.92, 0.0, -3.14],
"velocity": [45.3, 0.0, 0.0]
},
"Wall_East": {
"class": "StaticBody3D",
"global_position": [9.0, 0.0, -3.0]
}
}
}trajectory
Get position/property timeseries across a frame range.
{
"action": "trajectory",
"clip_id": "chase_bug_01",
"node": "Player",
"from_frame": 325,
"to_frame": 350,
"properties": ["position", "velocity"],
"sample_interval": 1
}| Parameter | Type | Default | Description |
|---|---|---|---|
clip_id | string | required | Which clip to query |
node | string | required | Node to track |
from_frame | integer | required | First frame (inclusive) |
to_frame | integer | required | Last frame (inclusive) |
properties | string[] | ["position"] | Properties to track |
sample_interval | integer | 1 | Sample every N frames |
query_range
Search frames for spatial conditions.
{
"action": "query_range",
"clip_id": "chase_bug_01",
"from_frame": 325,
"to_frame": 350,
"node": "Player",
"condition": {
"type": "velocity_spike"
}
}| Parameter | Type | Description |
|---|---|---|
clip_id | string | Which clip to query |
from_frame | integer | First frame (inclusive) |
to_frame | integer | Last frame (inclusive) |
node | string | Node to filter on (optional) |
condition | object | Condition filter |
Condition types:
| Type | Description |
|---|---|
moved | Frames where the node moved more than a threshold |
proximity | Frames where two nodes are within a distance |
velocity_spike | Frames with a sudden velocity increase |
property_change | Frames where a property changed value |
state_transition | Frames where an AnimationTree/FSM state changed |
signal_emitted | Frames where a signal was emitted |
entered_area | Frames where a body entered an Area3D |
collision | Frames with a collision event |
diff_frames
Compare two frames to see what changed.
{
"action": "diff_frames",
"clip_id": "chase_bug_01",
"frame_a": 336,
"frame_b": 337
}find_event
Search for a specific event type within a frame range.
{
"action": "find_event",
"clip_id": "chase_bug_01",
"event_type": "signal_emitted",
"event_filter": { "signal": "body_entered" },
"from_frame": 300,
"to_frame": 400
}screenshot_at
Get the viewport screenshot nearest to a frame or timestamp.
{
"action": "screenshot_at",
"clip_id": "chase_bug_01",
"at_frame": 337
}| Parameter | Type | Description |
|---|---|---|
clip_id | string | Which clip to query |
at_frame | integer | Frame number (use this or at_time_ms) |
at_time_ms | integer | Timestamp in ms (use this or at_frame) |
screenshots
List screenshot metadata in a clip (frame numbers, timestamps).
{
"action": "screenshots",
"clip_id": "chase_bug_01"
}Marker sources
| Source | Trigger |
|---|---|
human | F9 key or in-game flag button |
agent | clips { "action": "add_marker" } |
code | StageRuntime.marker() in GDScript |
Example conversation
Tips
Always specify node in trajectory. Even small clips with all nodes can be enormous. Filter to the 2-3 nodes relevant to the bug.
Use sample_interval: 5 for long clips. Sample every 5th frame for a quick overview, then use snapshot_at to drill into specific moments.
Use query_range with conditions to filter. The proximity condition finds frames where two nodes are within a distance — exactly when collision bugs occur.
Markers are saved automatically with F9. Pressing F9 (or clicking the in-game ⚑ flag button) saves a dashcam clip — the full buffer including ~60 seconds of history before the trigger. The clip is immediately available for analysis via list and snapshot_at.