Skip to main content
This page documents the complete SANA-Streaming wire surface: every command you send, the messages the model emits back, and an end-to-end example. For the conceptual model and a quick start, see the overview. The recommended way to drive SANA-Streaming from JavaScript or React is the typed @reactor-models/sana-streaming SDK. It wraps every command below as a typed method (setPrompt({ prompt })), every message as a typed subscription (useSanaStreamingState), and each track as a typed view component, all checked at compile time. Under the hood it speaks the wire protocol this page documents, which is also what you send by name from the base Reactor client, for example in Python, which has no typed package: send_command("set_prompt", { … }). The examples below follow one convention: React snippets call hooks inside a component rendered under <SanaStreamingProvider>; JavaScript snippets call methods on a connected SanaStreamingModel named sana; Python snippets use the base Reactor client named reactor.

Tracks

Output resolution is 1280 × 704. In live mode you publish your camera to the camera track; in file mode the source arrives by upload (set_video), not as a track. Commands that change generation take effect on chunk boundaries. Chunks hold 24 frames through the middle of a run; the first and last chunks of a run can be shorter. In the typed React SDK, render the output with <SanaStreamingMainVideoView> and read incoming tracks with useSanaStreamingTrack("main_video"); both are pre-bound to these names. There is also a <SanaStreamingCameraView> that acquires and publishes the webcam for you, but it gives no hook to set the camera’s content hint, so publish the camera track yourself when you need that (see the warning below).
Browsers shrink and grow the camera’s resolution mid-stream to cope with bandwidth, and a resolution change mid-chunk crashes the live session. Set track.contentHint = "detail" on the camera track before you publish it; the browser then holds resolution steady and adapts the frame rate instead, which the model handles fine.

Session lifecycle

SANA-Streaming session states: IDLE, RUNNING, PAUSED, with set commands staging in IDLE, start moving to RUNNING, set_prompt applying mid-stream, pause and resume between RUNNING and PAUSED, and reset returning to IDLE
Once the connection reaches ready, the session begins in IDLE. Stage a source there (a published camera in live mode, set_video in file mode) and, to steer the edit, a prompt; start transitions to RUNNING; pause moves to PAUSED; resume returns to RUNNING; reset clears the source, prompt, and progress and returns to IDLE from any state. While RUNNING, a new set_prompt lands at the next chunk boundary without stopping the stream, and set_anchor_interval turns on (or adjusts) re-grounding on the fly — anchoring is off by default. See Sessions for the separate connection-level lifecycle (disconnected → connecting → waiting → ready) the session passes through before reaching the states above. A prompt is optional: start with no prompt and the model streams a near-reconstruction of the source; set or change one at any time to steer the edit. The source is the only hard requirement to start (a published camera in live mode, an accepted clip in file mode). In file mode a run also ends on its own: every source frame gets transformed, then generation_complete reports the total chunk count and the session returns to IDLE with the clip, prompt, and seed still staged. Send start again to replay the clip from the top; reset is only needed to swap in a different clip or to clear the staging. On completion the main_video track freezes on the last transformed frame rather than going dark, so blank or overlay the stage yourself.

Commands

In the typed SDK each command is a method on useSanaStreaming() (React) or a SanaStreamingModel instance (JavaScript); from the base client you send it by name with send_command(). Below are all available commands:

set_mode

Select where the source video comes from: "live" (a published camera track) or "file" (a clip set with set_video). Repeating the command is safe, even with the same mode, so always send set_mode then start as a pair; the flow works no matter what was set before. Mode is fixed once a run starts; reset to switch.Parameters:Example:

Messages

The model emits the following messages. In React, subscribe to each with its typed hook (shown in the table); in JavaScript, with on<Message> on a SanaStreamingModel instance; in Python, with @reactor.on_message("<name>"). React also offers useSanaStreamingMessage for one handler over the whole union. The typed hooks hand you a flat, fully-typed object (msg.running, msg.reason); the base client delivers the same data as JSON { "type": "<name>", "data": { … } }. video_accepted reports the decoded source’s pre-crop width and height; num_frames and num_latent_frames arrive as 0 because the clip is decoded and VAE-encoded lazily during generation, not at upload. conditions_ready lets you check that start will succeed (only a video is required; a prompt is optional).

state payload

state is the single source of truth for the session’s observable state. Subscribe once and gate your UI on it (start buttons, mode toggles, transport controls) rather than tracking individual acknowledgements yourself. current_prompt is typed unknown on the wire (the field is free-form), but the model only ever sends a string or null; narrow it on your side. One timing quirk: right after a restart, state still reports the previous run’s final current_chunk until the new run’s first chunk_complete lands (indices restart at 0). Don’t drive progress UI from current_chunk in that window.

Complete example

Live mode in React, file mode from Python:
Prefer the plain-JavaScript client over React? Construct new SanaStreamingModel(), await sana.connect(jwt), then call the same typed methods (sana.setPrompt({ … }), sana.start()) and subscribe with sana.onState(…) / sana.onChunkComplete(…). The model name and tracks are baked into the class.