Tracks
Unlike the generation-only models, X2 takes an inbound track: the client publishes the video to be
edited on
source, and the model streams the transformed result back on main_video. The model’s
native source pacing is 24 fps, and the output plays at 24 fps with no frame interpolation and no
upscaling. The model chooses the output resolution (a bucket around 832p) once at the first
generation, from the source stream’s aspect ratio. It holds for the whole session and arrives in
generation_started and in every state_update.
Session lifecycle
There is nostart command and no staged or started state machine to drive. Generation begins on
its own once two things are true: a non-empty prompt is set, and source frames are arriving. It runs
until the session ends, a new reference image forces an automatic restart, or you call reset.
Reset stops generation and clears the prompt, reference image, and pointer. See
Sessions for the connection-level lifecycle
(disconnected → connecting → waiting → ready) the session passes through first.
Every control follows block semantics: the model generates one block at a time, a prompt change
applies from the next block, it samples the pointer once per block, and the backlog policy switches
at the next block.
Commands
Send commands withreactor.sendCommand() on the base SDK, or the typed methods on the generated
model client / useX2() hook. Below are all available commands:
set_prompt
Set the editing instruction that guides the re-render (for example a character swap or insertion).
Generation needs a non-empty prompt before it begins; setting one while generating applies from the
next block. Emits prompt_accepted, then state_update.
Parameters:
set_reference_image
Provide a reference image of a character or object to insert or swap into the video. Upload the file
first with uploadFile(), then pass the returned FileRef. Emits
reference_image_accepted and state_update on success, or command_error if the file cannot be
decoded as an image.
The reference can be set before generation or replaced while generating. A mid-run replacement
restarts the stream on its own (generation_stopped with reason reference_image_changed, then a
fresh generation_started) and the new image conditions the edit from its first block. The prompt
stays set across the swap, so the edit resumes without a re-arm; no reset is needed.
Parameters:
set_pointer
Update the drag pointer that steers the edited subject’s motion, setting its position and press
state together. Valid at any time; the model samples the pointer once per generated block, and it
only has an effect while active is true. Coordinates map to the output frame, so account for any
letterboxing your player introduces when you map pointer events. Emits pointer_changed, then
state_update.
Parameters:
set_pointer_x, set_pointer_y, set_pointer_active
Single-field variants of set_pointer, for updating one pointer field at a time. Same semantics:
sampled once per block, effective only while the pointer is active. Prefer set_pointer when you
have all three values; the position and press state then change in one step.
Parameters (one per command):
set_keep_backlog
Pick the source-frame consumption policy, applied from the next block:
false(default): newest frames. The model always reads the most recent source frames and drops any backlog, keeping latency bounded when inference runs slower than the source. Right for live sources like a webcam, where the edit should track “now”.true: every frame. The model consumes every source frame in order, for smoother motion at the cost of a growing delay. Right for pre-recorded clips and for drag-to-animate, where smoothness matters more than latency.
state_update. reset does not clear this setting, unlike the prompt, reference image, and
pointer: it persists until the session ends, so re-sync any UI toggle to the default on disconnect
rather than on reset.
Parameters:
reset
Stop generation and clear the prompt, reference image, and pointer, returning the session to waiting
for new conditions. Valid at any time. Emits generation_stopped with reason reset if a run was
active, and state_update. Takes no arguments.
Note two things reset leaves alone: the output resolution (chosen once per session, at the first
generation) and the keep_backlog policy (persists until the session ends).
Messages
X2 emits the following messages, each arriving as JSON{ "type": "<name>", "data": { … } }.
A
generation_stopped with reason reference_image_changed is an automatic restart: a fresh
generation_started follows at once, so treat it as a hiccup rather than an end state. Only reason
reset means the session is back to waiting for conditions; drive any “clear the drafts” UI off
that reason, not off every stop.
state_update payload
state_update is the single source of truth for driving UI. It arrives on connect and after every
observable state change, always carrying the full state. Subscribe once and reduce it into your app
state; the only fact it does not carry is the decoded reference image’s dimensions, which come from
the discrete reference_image_accepted ack.
Example handler: