Skip to main content
This page documents the complete Helios wire surface: every command you send with reactor.sendCommand(), the events the model emits back, and two end-to-end examples. For the conceptual model and a quick start, see the overview.

Commands

Send commands to the model using reactor.sendCommand(). Below are all available commands:
CommandDescription
set_promptSet the prompt (at chunk 0, or current chunk if running)
schedule_promptSchedule a prompt at a specific chunk index
set_imageSet or change a reference image (works mid-generation)
set_conditioningSet prompt and reference image together atomically (avoids a first-chunk race)
set_image_strengthAdjust how strongly the reference image anchors output (0.0–1.0)
set_sr_scaleSet super-resolution scale: "off", "2x", or "4x"
set_seedSet a seed for reproducible output. Same seed = same video
startBegin video generation
pausePause generation
resumeResume generation
resetReset to initial state

set_prompt

Convenience wrapper around schedule_prompt that automatically picks the right chunk index so you don’t have to track it yourself:
  • Not started: schedules at chunk 0.
  • Paused: schedules at the current chunk (takes effect on resume).
  • Running: schedules at the next chunk (current chunk is already being processed).
The easiest way to change the prompt. Call set_prompt and the model figures out when to apply it.Parameters:
ParameterTypeRequiredDescription
promptstringYesThe prompt text to use for generation
Example:
// Set initial prompt (before starting)
await reactor.sendCommand("set_prompt", {
  prompt: "A serene mountain landscape at sunrise",
});

// Change prompt mid-generation. Automatically applies at the next chunk
await reactor.sendCommand("set_prompt", {
  prompt: "The landscape transitions to a stormy ocean",
});
const { setPrompt } = useHelios();

// Set initial prompt (before starting)
await setPrompt({ prompt: "A serene mountain landscape at sunrise" });

// Change prompt mid-generation. Automatically applies at the next chunk
await setPrompt({ prompt: "The landscape transitions to a stormy ocean" });
# Set initial prompt (before starting)
await reactor.send_command("set_prompt", {
    "prompt": "A serene mountain landscape at sunrise",
})

# Change prompt mid-generation. Automatically applies at the next chunk
await reactor.send_command("set_prompt", {
    "prompt": "The landscape transitions to a stormy ocean",
})

Messages from model

Listen for messages with reactor.on("message", ...) (imperative) or the useReactorMessage() hook (React). Every message is delivered as JSON { "type": "<name>", "data": { … } }.
MessageWhenPayload
stateOn connect, after every command, and after every chunkFull session snapshot (see below)
prompt_acceptedAfter set_prompt{ prompt: string }
image_acceptedAfter set_image decodes{ width: int, height: int }
conditions_readyAfter set_prompt or set_image{ has_prompt: bool, has_image: bool }
generation_startedAfter start{ prompt: string, chunk_index: int }
chunk_completeAfter each chunk emits{ chunk_index: int, frames_emitted: int, active_prompt: string }
generation_pausedAfter pause{ chunk_index: int }
generation_resumedAfter resume{ chunk_index: int }
command_errorWhen a command is rejected{ command: string, reason: string }

state payload

state is the single source of truth for driving UI. Subscribe once and treat it as the authoritative session snapshot; you generally do not need to track the individual messages above yourself.
FieldTypeMeaning
runningboolstarted && !paused. Equivalent to “frames are actively streaming”.
startedboolTrue once start has been accepted; stays true through pauses. Cleared by reset.
pausedboolTrue while generation is paused via pause.
image_setboolTrue once a reference image has been set this session. Cleared only by reset.
current_chunkintZero-based index of the chunk currently being generated. 0 before the first chunk and after reset.
current_frameintRunning total of frames emitted on main_video since the last reset.
current_promptstring | nullThe prompt currently driving generation, or null if none set. Stays null for the first state message after start, until the first chunk begins emitting.
image_strengthnumberCurrent image_strength (0.0–1.0). Ignored when no reference image is set.
scheduled_promptsobjectPending scheduled prompts, keyed by the chunk index at which they take effect.
Example handler:
reactor.on("message", (msg) => {
  switch (msg.type) {
    case "state":
      console.log(`Chunk ${msg.data.current_chunk} · frame ${msg.data.current_frame}`);
      console.log(`Running: ${msg.data.running} · prompt: ${msg.data.current_prompt}`);
      break;
    case "image_accepted":
      console.log(`Reference image set: ${msg.data.width}x${msg.data.height}`);
      break;
    case "generation_started":
      console.log(`Generation started: ${msg.data.prompt}`);
      break;
    case "command_error":
      console.error(`${msg.data.command} rejected: ${msg.data.reason}`);
      break;
  }
});
import {
  useHeliosState,
  useHeliosImageAccepted,
  useHeliosGenerationStarted,
  useHeliosCommandError,
  type HeliosStateMessage,
} from "@reactor-models/helios";

function StateDisplay() {
  const [snapshot, setSnapshot] = useState<HeliosStateMessage | null>(null);

  // `state` fires on connect, after every command, and after every chunk.
  useHeliosState((msg) => setSnapshot(msg));
  useHeliosImageAccepted((msg) => console.log(`Reference image set: ${msg.width}x${msg.height}`));
  useHeliosGenerationStarted((msg) => console.log(`Generation started: ${msg.prompt}`));
  useHeliosCommandError((msg) => console.error(`${msg.command} rejected: ${msg.reason}`));

  if (!snapshot) return null;

  return (
    <div>
      Running: {snapshot.running ? "Yes" : "No"} | Chunk: {snapshot.current_chunk} | Frame:{" "}
      {snapshot.current_frame}
    </div>
  );
}
@reactor.on_message
def handle(msg):
    data = msg["data"]
    if msg["type"] == "state":
        print(f"Chunk {data['current_chunk']} · frame {data['current_frame']}")
        print(f"Running: {data['running']} · prompt: {data['current_prompt']}")
    elif msg["type"] == "image_accepted":
        print(f"Reference image set: {data['width']}x{data['height']}")
    elif msg["type"] == "generation_started":
        print(f"Generation started: {data['prompt']}")
    elif msg["type"] == "command_error":
        print(f"{data['command']} rejected: {data['reason']}")

Complete example: prompt sequence

import { Reactor } from "@reactor-team/js-sdk";

const reactor = new Reactor({
  modelName: "helios",
});

// Set up video display
const videoElement = document.getElementById("video") as HTMLVideoElement;
reactor.on("trackReceived", (name, track, stream) => {
  videoElement.srcObject = stream;
  videoElement.play().catch(console.warn);
});

// Listen for state updates
reactor.on("message", (msg) => {
  if (msg.type === "state") {
    document.getElementById("info")!.textContent =
      `Chunk: ${msg.data.current_chunk} | Frame: ${msg.data.current_frame}`;
  }
  if (msg.type === "chunk_complete") {
    console.log(`Now showing: ${msg.data.active_prompt}`);
  }
});

// Connect (see Authentication guide for how to obtain a token)
await reactor.connect(token);

// Set a seed for reproducibility
await reactor.sendCommand("set_seed", { seed: 42 });

// Schedule prompts for a cinematic sequence
await reactor.sendCommand("schedule_prompt", {
  prompt: "A peaceful forest at dawn, soft morning light filtering through the trees",
  chunk: 0,
});

await reactor.sendCommand("schedule_prompt", {
  prompt: "Sunlight breaking through the canopy, golden rays illuminating the forest floor",
  chunk: 5,
});

await reactor.sendCommand("schedule_prompt", {
  prompt: "A deer walking through the misty forest clearing",
  chunk: 10,
});

// Start generation
await reactor.sendCommand("start", {});
import {
  HeliosProvider,
  HeliosMainVideoView,
  useHelios,
  useHeliosState,
  useHeliosChunkComplete,
} from "@reactor-models/helios";

function App({ token }: { token: string }) {
  return (
    <HeliosProvider jwtToken={token}>
      <PromptSequence />
      {/* HeliosMainVideoView handles <video> setup, srcObject, and autoplay. */}
      <HeliosMainVideoView className="h-full w-full" videoObjectFit="contain" />
    </HeliosProvider>
  );
}

function PromptSequence() {
  const { status, setSeed, schedulePrompt, start } = useHelios();

  // `state` is the single source of truth for chunk/frame progress.
  useHeliosState((msg) =>
    console.log(`Chunk: ${msg.current_chunk} | Frame: ${msg.current_frame}`),
  );
  useHeliosChunkComplete((msg) => console.log(`Now showing: ${msg.active_prompt}`));

  async function run() {
    // Set a seed for reproducibility
    await setSeed({ seed: 42 });

    // Schedule prompts for a cinematic sequence
    await schedulePrompt({
      prompt: "A peaceful forest at dawn, soft morning light filtering through the trees",
      chunk: 0,
    });
    await schedulePrompt({
      prompt: "Sunlight breaking through the canopy, golden rays illuminating the forest floor",
      chunk: 5,
    });
    await schedulePrompt({
      prompt: "A deer walking through the misty forest clearing",
      chunk: 10,
    });

    // Start generation
    await start();
  }

  return (
    <button disabled={status !== "ready"} onClick={run}>
      Start sequence
    </button>
  );
}

Complete example: image-to-video

Use a reference image to guide generation. Upload the image first, then send it together with the prompt via set_conditioning before starting, so the very first chunk is conditioned on both.
import { Reactor } from "@reactor-team/js-sdk";

const reactor = new Reactor({
  modelName: "helios",
});

// Set up video display
const videoElement = document.getElementById("video") as HTMLVideoElement;
reactor.on("trackReceived", (name, track, stream) => {
  videoElement.srcObject = stream;
  videoElement.play().catch(console.warn);
});

// Connect
await reactor.connect(token);

// Upload a reference image
const fileInput = document.getElementById("file") as HTMLInputElement;
const imageFile = fileInput.files![0];
const imageRef = await reactor.uploadFile(imageFile);

// Set the image and prompt atomically. Sending set_image + set_prompt
// separately before start races: the image can land a chunk late, so the
// first chunk renders from the prompt alone. set_conditioning commits both
// in one message. See set_conditioning above.
await reactor.sendCommand("set_conditioning", {
  image: imageRef,
  prompt: "A cinematic slow zoom into the scene, golden hour lighting, gentle motion",
});

// Start generation
await reactor.sendCommand("start", {});

// Later: swap to a new image mid-generation
const newInput = document.getElementById("new-file") as HTMLInputElement;
const newRef = await reactor.uploadFile(newInput.files![0]);
await reactor.sendCommand("set_image", { image: newRef });
import {
  HeliosProvider,
  HeliosMainVideoView,
  useHelios,
} from "@reactor-models/helios";

function App({ token }: { token: string }) {
  return (
    <HeliosProvider jwtToken={token}>
      <ImageToVideo />
      <HeliosMainVideoView className="h-full w-full" videoObjectFit="contain" />
    </HeliosProvider>
  );
}

function ImageToVideo() {
  const { status, uploadFile, setImage, setConditioning, start } = useHelios();

  async function startFromImage(imageFile: File) {
    const imageRef = await uploadFile(imageFile);

    // Set the image and prompt atomically. Sending set_image + set_prompt
    // separately before start races: the image can land a chunk late, so the
    // first chunk renders from the prompt alone. set_conditioning commits both
    // in one message. See set_conditioning above.
    await setConditioning({
      image: imageRef,
      prompt: "A cinematic slow zoom into the scene, golden hour lighting, gentle motion",
    });

    // Start generation
    await start();
  }

  // Later: swap to a new image mid-generation
  async function swapImage(newImageFile: File) {
    const newRef = await uploadFile(newImageFile);
    await setImage({ image: newRef });
  }

  return (
    <>
      <input
        type="file"
        disabled={status !== "ready"}
        onChange={(e) => e.target.files?.[0] && startFromImage(e.target.files[0])}
      />
      <input
        type="file"
        disabled={status !== "ready"}
        onChange={(e) => e.target.files?.[0] && swapImage(e.target.files[0])}
      />
    </>
  );
}