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. The camera track is the only source: publish it and the model reads it in 24-frame chunks. The model locks playout to the measured camera rate, one output frame per camera frame. It reads the freshest full chunk and drops the backlog, so latency stays around one chunk instead of growing. Commands that change generation take effect on chunk boundaries. 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. Publish your camera there and, to steer the edit, set a prompt; start transitions to RUNNING; pause moves to PAUSED; resume returns to RUNNING; reset clears the 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. start requires nothing else. It transforms whatever camera frames arrive, and the first frames land once a full chunk is buffered. A run has no length limit. It continues until you send reset or the session ends, and generation_complete then reports the total chunk count. After a reset the main_video track freezes on the last transformed frame rather than going dark, so blank or overlay the stage yourself. A session outlives the clients in it. A client that joins a live session receives the current state, addressed to it alone, and disturbs nothing: the run carries on for everyone already watching, and the new client can steer, pause, or reset it in turn. A client that leaves does not stop the run. The model applies the session’s defaults for seed and anchor interval when the session starts, not when a client connects.

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_prompt

Set the edit prompt, before start or at any point mid-stream. The model applies the new prompt at the next chunk boundary, about one chunk (~1-1.5s) after you send it, with no re-render and no break in the stream. Acknowledged by prompt_accepted.A prompt is optional: with no prompt the model streams a near-reconstruction of the source. Set or change one to steer the edit. The model processes the prompt when it edits the next chunk, so a prompt it cannot process is reported about one chunk after prompt_accepted, as a command_error and a fresh state. The model then keeps the prompt it accepted before that one.See the prompt guide for how to write edit prompts.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": { … } }. Messages that describe a chunk (chunk_complete, anchored, and the chunk’s state) go on the wire before that chunk’s frames. Every state-changing command broadcasts state before it answers the caller, so a client that waits on the reply already holds the state the reply describes.

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

The same camera edit in React and in 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.