@reactor-models/ltx2
v5.0.1
Published
Strongly-typed SDK for the Ltx2 model on Reactor
Downloads
1,116
Maintainers
Readme
@reactor-models/ltx2
Typed JavaScript + React SDK for the Ltx2 model on Reactor. Version v5.0.1.
Get started
Scaffold a starter app for Ltx2 with create-reactor-app:
npx create-reactor-app my-app --model=ltx2pnpm dlx create-reactor-app my-app --model=ltx2Install
npm install @reactor-models/ltx2pnpm add @reactor-models/ltx2The package exports a plain-JavaScript client and a set of React bindings. Import whichever you need from @reactor-models/ltx2:
import { Ltx2Model } from "@reactor-models/ltx2";import { Ltx2Provider, useLtx2 } from "@reactor-models/ltx2";React 18 or later is required when using the provider and hooks. The token-loading examples below use React 19's use(); on React 18, fetch the JWT in a useEffect and pass it to the provider once it resolves.
Authenticate
Reactor uses short-lived JWTs for session auth. You hold your API key on your server, mint a token on demand, and the client never sees the raw key. Tokens are valid for 6 hours — if one leaks, it expires on its own.
Mint a JWT with POST https://api.reactor.inc/tokens and the Reactor-API-Key header; the response JSON is { "jwt": "..." }.
JavaScript (Next.js route handler)
// app/api/reactor/token/route.ts
import { NextResponse } from "next/server";
export async function POST() {
const res = await fetch("https://api.reactor.inc/tokens", {
method: "POST",
headers: { "Reactor-API-Key": process.env.REACTOR_API_KEY! },
});
const { jwt } = await res.json();
return NextResponse.json({ jwt });
}React (provider)
Call the /api/reactor/token route above from a client component and pass the result to the provider:
"use client";
import { use } from "react";
import { Ltx2Provider } from "@reactor-models/ltx2";
import { ReactorView } from "@reactor-team/js-sdk";
async function getToken() {
const r = await fetch("/api/reactor/token", { method: "POST" });
const { jwt } = await r.json();
return jwt;
}
const tokenPromise = getToken();
export default function App() {
const token = use(tokenPromise);
return (
<Ltx2Provider jwtToken={token} connectOptions={{ autoConnect: true }}>
<ReactorView className="w-full aspect-video" />
</Ltx2Provider>
);
}Connect
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);React
The provider takes the JWT as a prop; fetch it from the same /api/reactor/token route the Authenticate example mints:
"use client";
import { use } from "react";
import { Ltx2Provider, useLtx2 } from "@reactor-models/ltx2";
async function getToken() {
const r = await fetch("/api/reactor/token", { method: "POST" });
const { jwt } = await r.json();
return jwt;
}
const tokenPromise = getToken();
function Controller() {
const { status } = useLtx2();
return <span>Status: {status}</span>;
}
export default function App() {
const token = use(tokenPromise);
return (
<Ltx2Provider jwtToken={token}>
<Controller />
</Ltx2Provider>
);
}Events
Client-to-model commands. The typed surface is Ltx2Model (one method per event) in plain JS, and useLtx2() in React — every field name below matches the parameter name the method accepts. Every awaited call resolves once the model's handler has completed; a command whose handler returns a message resolves with that reply (see each event's "Returns" line).
stop
Stop the run in flight within about a second, keeping every condition — image, script, and settings — so start immediately begins a fresh take with the same setup. Emits generation_stopped and state_update once a take has ended, or command_error if nothing is in flight.
Returns: nothing — the awaited call resolves undefined once the model's handler has run.
No parameters.
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);
await ltx2.stop();React
"use client";
import { useLtx2 } from "@reactor-models/ltx2";
function Example() {
const { stop } = useLtx2();
return <button onClick={() => stop()}>stop</button>;
}pause
Freeze the output stream mid-run on its current frame. Generation keeps running ahead into a bounded buffer, so resume continues instantly. Emits generation_paused and state_update, or command_error if no run is in flight or it is already paused.
Returns: generation_paused — { type: "generation_paused", seconds_sent: 0 } (or undefined when the send fails).
No parameters.
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);
const reply = await ltx2.pause();
if (reply) {
console.log("generation_paused", reply.seconds_sent);
}React
"use client";
import { useLtx2 } from "@reactor-models/ltx2";
function Example() {
const { pause } = useLtx2();
return <button onClick={() => pause()}>pause</button>;
}reset
Return every condition to its default and the model to waiting for new ones. Valid at any time. During a run this also stops it within about a second — a moment of already-generated video and audio may still play out, and no generation_complete follows. To stop without losing the conditions, use stop. Emits generation_reset and state_update.
Returns: generation_reset — { type: "generation_reset", was_generating: true } (or undefined when the send fails).
No parameters.
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);
const reply = await ltx2.reset();
if (reply) {
console.log("generation_reset", reply.was_generating);
}React
"use client";
import { useLtx2 } from "@reactor-models/ltx2";
function Example() {
const { reset } = useLtx2();
return <button onClick={() => reset()}>reset</button>;
}start
Begin generating with the conditions as they stand. Requires an avatar image and a script; everything else has a default. Emits generation_started then streams (window_progress per window), or command_error if a condition is missing or a run is already in flight.
Returns: nothing — the awaited call resolves undefined once the model's handler has run.
No parameters.
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);
await ltx2.start();React
"use client";
import { useLtx2 } from "@reactor-models/ltx2";
function Example() {
const { start } = useLtx2();
return <button onClick={() => start()}>start</button>;
}resume
Continue a paused stream exactly where it froze, with no warm-up. Emits generation_resumed and state_update, or command_error if the stream is not paused.
Returns: generation_resumed — { type: "generation_resumed", seconds_sent: 0 } (or undefined when the send fails).
No parameters.
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);
const reply = await ltx2.resume();
if (reply) {
console.log("generation_resumed", reply.seconds_sent);
}React
"use client";
import { useLtx2 } from "@reactor-models/ltx2";
function Example() {
const { resume } = useLtx2();
return <button onClick={() => resume()}>resume</button>;
}setWpm
Override the speaking pace in words per minute (default 140). The accepted range is deployment-configured (80-220 unless overridden) and reported as wpm_min/wpm_max in state_update. The pace lays the script out over the run and sets the script-derived length. Valid at any time: while idle it applies immediately; during a run it queues for the next take (listed in state_update.queued_changes). Emits wpm_accepted and state_update on success, or command_error if the value is out of range.
Returns: wpm_accepted — { type: "wpm_accepted", wpm: 0, derived_seconds: 0, effective_seconds: 0 } (or undefined when the send fails).
| Parameter | Type | Description |
|---|---|---|
| wpm | number | Words per minute, within the deployment's accepted range (see wpm_min/wpm_max in state_update; 80-220 by default). (default 0) |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);
const reply = await ltx2.setWpm({ wpm: 0 });
if (reply) {
console.log(
"wpm_accepted",
reply.wpm,
reply.derived_seconds,
reply.effective_seconds,
);
}React
"use client";
import { useLtx2 } from "@reactor-models/ltx2";
function Example() {
const { setWpm } = useLtx2();
return <button onClick={() => setWpm({ wpm: 0 })}>setWpm</button>;
}setSeed
Set the random seed for the next run. The same conditions with the same seed reproduce the same take. Valid at any time: while idle it applies immediately; during a run it queues for the next take (listed in state_update.queued_changes). Emits seed_accepted and state_update on success.
Returns: seed_accepted — { type: "seed_accepted", seed: 0 } (or undefined when the send fails).
| Parameter | Type | Description |
|---|---|---|
| seed | number | Any integer. (default 0) |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);
const reply = await ltx2.setSeed({ seed: 0 });
if (reply) {
console.log("seed_accepted", reply.seed);
}React
"use client";
import { useLtx2 } from "@reactor-models/ltx2";
function Example() {
const { setSeed } = useLtx2();
return <button onClick={() => setSeed({ seed: 0 })}>setSeed</button>;
}setPrompt
Describe how the avatar looks and moves while speaking. Optional — the default suits a straight-to-camera talking head, and an empty text restores it. Valid at any time: while idle it applies immediately; during a run it queues for the next take (listed in state_update.queued_changes). Emits prompt_accepted and state_update on success.
Returns: prompt_accepted — { type: "prompt_accepted", prompt: "A sunset over the ocean" } (or undefined when the send fails).
| Parameter | Type | Description |
|---|---|---|
| prompt | string | Scene description, up to 800 characters. Read once at start; each generation window embeds its slice of the script into this description. (maxLength 800, default "") |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);
const reply = await ltx2.setPrompt({ prompt: "A sunset over the ocean" });
if (reply) {
console.log("prompt_accepted", reply.prompt);
}React
"use client";
import { useLtx2 } from "@reactor-models/ltx2";
function Example() {
const { setPrompt } = useLtx2();
return <button onClick={() => setPrompt({ prompt: "A sunset over the ocean" })}>setPrompt</button>;
}setScript
Set the words the avatar speaks. The run's length derives from the script at the session's words-per-minute pacing unless an explicit duration is set. Valid at any time: while idle it applies immediately; during a run it queues for the next take (listed in state_update.queued_changes). Emits script_accepted and state_update on success, or command_error if the text is blank.
Returns: script_accepted — { type: "script_accepted", words: 0, derived_seconds: 0, effective_seconds: 0 } (or undefined when the send fails).
| Parameter | Type | Description |
|---|---|---|
| script | string | The speech, up to 10000 characters of plain text — more than the longest run the deployment allows can deliver. Read once at start, so it cannot be changed part-way through a run. (maxLength 10000, default "") |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);
const reply = await ltx2.setScript({ script: "" });
if (reply) {
console.log(
"script_accepted",
reply.words,
reply.derived_seconds,
reply.effective_seconds,
);
}React
"use client";
import { useLtx2 } from "@reactor-models/ltx2";
function Example() {
const { setScript } = useLtx2();
return <button onClick={() => setScript({ script: "" })}>setScript</button>;
}setAvatarImage
Provide the still image the avatar is anchored to: its identity, framing, and background hold for the whole run. Valid at any time: while idle it applies immediately; during a run it queues for the next take (listed in state_update.queued_changes). Emits avatar_image_accepted and state_update on success, or command_error if the file is not a readable image.
Returns: avatar_image_accepted — { type: "avatar_image_accepted", width: 0, height: 0, filename: "" } (or undefined when the send fails).
| Parameter | Type | Description |
|---|---|---|
| avatar_image | FileRef | Reference to a file uploaded via the Reactor upload protocol. (default null) |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);
const fileRef = await ltx2.uploadFile(blob);
const reply = await ltx2.setAvatarImage({ avatar_image: fileRef });
if (reply) {
console.log(
"avatar_image_accepted",
reply.width,
reply.height,
reply.filename,
);
}React
"use client";
import { useLtx2 } from "@reactor-models/ltx2";
function Example() {
const { setAvatarImage, uploadFile } = useLtx2();
async function handlePick(file: File) {
const ref = await uploadFile(file);
await setAvatarImage({ avatar_image: ref });
}
return <input type="file" onChange={(e) => handlePick(e.target.files![0])} />;
}setDurationSeconds
Set an explicit run length in seconds, overriding the script-derived one; zero returns to deriving it from the script. Values are clamped to the deployment's 4-120 second range. Valid at any time: while idle it applies immediately; during a run it queues for the next take (listed in state_update.queued_changes). Emits duration_accepted and state_update on success.
Returns: duration_accepted — { type: "duration_accepted", duration_seconds: 0, effective_seconds: 0 } (or undefined when the send fails).
| Parameter | Type | Description |
|---|---|---|
| duration_seconds | number | Run length in seconds, or zero to derive it from the script at the session's pacing. (default 0) |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
await ltx2.connect(jwt);
const reply = await ltx2.setDurationSeconds({ duration_seconds: 0 });
if (reply) {
console.log("duration_accepted", reply.duration_seconds, reply.effective_seconds);
}React
"use client";
import { useLtx2 } from "@reactor-models/ltx2";
function Example() {
const { setDurationSeconds } = useLtx2();
return <button onClick={() => setDurationSeconds({ duration_seconds: 0 })}>setDurationSeconds</button>;
}Messages
Model-to-client messages. Register a typed listener with on… on Ltx2Model, or a useLtx2… hook in React, to receive only the messages you care about.
state_update
Emitted on connect and after every change to the session's state.
One snapshot of everything observable, so a client can render from this alone instead of accumulating the individual messages below.
Listener: onStateUpdate · React hook: useLtx2StateUpdate
| Field | Type | Description |
|---|---|---|
| wpm | number | Speaking pace in words per minute. |
| seed | number | Random seed the next run uses. |
| ready | boolean | An avatar image and a script are set, so start is valid. |
| paused | boolean | The output stream is paused mid-run; resume continues it instantly. |
| prompt | string | Scene description in effect. |
| script | string \| null | Script in effect, or null when none is set. |
| wpm_max | number | Highest speaking pace this deployment accepts via set_wpm. |
| wpm_min | number | Lowest speaking pace this deployment accepts via set_wpm. |
| finished | boolean | The last run ended and the model is idle. Change a condition or send reset, then start again for a new take. |
| generating | boolean | A run is in flight. Condition changes sent while this is true queue for the next take (see queued_changes). |
| seconds_sent | number | Seconds of video and audio sent so far this run. |
| window_index | number | Zero-based index of the most recent generation window streamed this run, or -1 before the first. |
| total_windows | number | Windows the current run will stream; zero before one starts. |
| queued_changes | string[] | Condition fields changed during the run in flight, in the order first changed (e.g. script, wpm). They are already the values shown in this snapshot and take effect on the next take; empty when nothing is queued or no run is in flight. |
| valid_commands | string[] | Names of the commands the session would accept right now. Use this to enable or grey out controls instead of re-deriving the state machine client-side; any command not listed would return command_error. |
| duration_seconds | number | Explicit run length in seconds, or zero when the length derives from the script. |
| has_avatar_image | boolean | An avatar image is set. |
| effective_seconds | number | Length the next run will actually have: the explicit duration when set, otherwise the script-derived length. Zero until a script is set. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onStateUpdate((msg) => {
console.log(
"state_update",
msg.wpm,
msg.seed,
msg.ready,
msg.paused,
msg.prompt,
msg.script,
msg.wpm_max,
msg.wpm_min,
msg.finished,
msg.generating,
msg.seconds_sent,
msg.window_index,
msg.total_windows,
msg.queued_changes,
msg.valid_commands,
msg.duration_seconds,
msg.has_avatar_image,
msg.effective_seconds,
);
});
await ltx2.connect(jwt);React
import { useLtx2StateUpdate } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2StateUpdate((msg) => {
console.log(
"state_update",
msg.wpm,
msg.seed,
msg.ready,
msg.paused,
msg.prompt,
msg.script,
msg.wpm_max,
msg.wpm_min,
msg.finished,
msg.generating,
msg.seconds_sent,
msg.window_index,
msg.total_windows,
msg.queued_changes,
msg.valid_commands,
msg.duration_seconds,
msg.has_avatar_image,
msg.effective_seconds,
);
});wpm_accepted
Emitted when set_wpm is accepted.
Listener: onWpmAccepted · React hook: useLtx2WpmAccepted
| Field | Type | Description |
|---|---|---|
| wpm | number | Speaking pace now in effect. |
| derived_seconds | number | Script-derived length at the new pacing; zero when no script is set yet. |
| effective_seconds | number | Length the next run will actually have. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onWpmAccepted((msg) => {
console.log(
"wpm_accepted",
msg.wpm,
msg.derived_seconds,
msg.effective_seconds,
);
});
await ltx2.connect(jwt);React
import { useLtx2WpmAccepted } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2WpmAccepted((msg) => {
console.log(
"wpm_accepted",
msg.wpm,
msg.derived_seconds,
msg.effective_seconds,
);
});command_error
Emitted when a command is rejected. The command had no effect.
Listener: onCommandError · React hook: useLtx2CommandError
| Field | Type | Description |
|---|---|---|
| reason | string | Why it was rejected. |
| command | string | Name of the command that was rejected. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onCommandError((msg) => {
console.log("command_error", msg.reason, msg.command);
});
await ltx2.connect(jwt);React
import { useLtx2CommandError } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2CommandError((msg) => {
console.log("command_error", msg.reason, msg.command);
});seed_accepted
Emitted when set_seed is accepted.
Listener: onSeedAccepted · React hook: useLtx2SeedAccepted
| Field | Type | Description |
|---|---|---|
| seed | number | Seed the next run uses. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onSeedAccepted((msg) => {
console.log("seed_accepted", msg.seed);
});
await ltx2.connect(jwt);React
import { useLtx2SeedAccepted } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2SeedAccepted((msg) => {
console.log("seed_accepted", msg.seed);
});prompt_accepted
Emitted when set_prompt is accepted.
Listener: onPromptAccepted · React hook: useLtx2PromptAccepted
| Field | Type | Description |
|---|---|---|
| prompt | string | Scene description now in effect. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onPromptAccepted((msg) => {
console.log("prompt_accepted", msg.prompt);
});
await ltx2.connect(jwt);React
import { useLtx2PromptAccepted } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2PromptAccepted((msg) => {
console.log("prompt_accepted", msg.prompt);
});script_accepted
Emitted when set_script is accepted.
Listener: onScriptAccepted · React hook: useLtx2ScriptAccepted
| Field | Type | Description |
|---|---|---|
| words | number | Words counted in the script. |
| derived_seconds | number | Length the script implies at the session's pacing. Used as the run length unless an explicit duration is set. |
| effective_seconds | number | Length the next run will actually have. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onScriptAccepted((msg) => {
console.log(
"script_accepted",
msg.words,
msg.derived_seconds,
msg.effective_seconds,
);
});
await ltx2.connect(jwt);React
import { useLtx2ScriptAccepted } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2ScriptAccepted((msg) => {
console.log(
"script_accepted",
msg.words,
msg.derived_seconds,
msg.effective_seconds,
);
});window_progress
Emitted once per generation window streamed on the output tracks.
Listener: onWindowProgress · React hook: useLtx2WindowProgress
| Field | Type | Description |
|---|---|---|
| seconds_sent | number | Seconds of video and audio sent so far, this window included. |
| window_index | number | Zero-based index of the window just streamed. |
| total_seconds | number | Length of the run in seconds. |
| total_windows | number | Windows this run will stream. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onWindowProgress((msg) => {
console.log(
"window_progress",
msg.seconds_sent,
msg.window_index,
msg.total_seconds,
msg.total_windows,
);
});
await ltx2.connect(jwt);React
import { useLtx2WindowProgress } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2WindowProgress((msg) => {
console.log(
"window_progress",
msg.seconds_sent,
msg.window_index,
msg.total_seconds,
msg.total_windows,
);
});generation_reset
Emitted when reset is accepted.
Every condition is back to its default and the model is waiting for new ones.
Listener: onGenerationReset · React hook: useLtx2GenerationReset
| Field | Type | Description |
|---|---|---|
| was_generating | boolean | A run was in flight and has been stopped, so no generation_complete will follow it. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onGenerationReset((msg) => {
console.log("generation_reset", msg.was_generating);
});
await ltx2.connect(jwt);React
import { useLtx2GenerationReset } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2GenerationReset((msg) => {
console.log("generation_reset", msg.was_generating);
});duration_accepted
Emitted when set_duration_seconds is accepted.
Listener: onDurationAccepted · React hook: useLtx2DurationAccepted
| Field | Type | Description |
|---|---|---|
| duration_seconds | number | Explicit run length now in effect; zero means the length derives from the script again. |
| effective_seconds | number | Length the next run will actually have. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onDurationAccepted((msg) => {
console.log("duration_accepted", msg.duration_seconds, msg.effective_seconds);
});
await ltx2.connect(jwt);React
import { useLtx2DurationAccepted } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2DurationAccepted((msg) => {
console.log("duration_accepted", msg.duration_seconds, msg.effective_seconds);
});generation_failed
Emitted when a run stops early because something went wrong.
The model then idles; adjust the conditions and start again.
Listener: onGenerationFailed · React hook: useLtx2GenerationFailed
| Field | Type | Description |
|---|---|---|
| reason | string | What went wrong. |
| seconds_sent | number | Seconds streamed before it stopped. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onGenerationFailed((msg) => {
console.log("generation_failed", msg.reason, msg.seconds_sent);
});
await ltx2.connect(jwt);React
import { useLtx2GenerationFailed } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2GenerationFailed((msg) => {
console.log("generation_failed", msg.reason, msg.seconds_sent);
});generation_paused
Emitted when pause is accepted.
The stream freezes on the last frame; generation keeps running ahead into
a bounded buffer, so resume continues without any warm-up.
Listener: onGenerationPaused · React hook: useLtx2GenerationPaused
| Field | Type | Description |
|---|---|---|
| seconds_sent | number | Seconds streamed before the pause. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onGenerationPaused((msg) => {
console.log("generation_paused", msg.seconds_sent);
});
await ltx2.connect(jwt);React
import { useLtx2GenerationPaused } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2GenerationPaused((msg) => {
console.log("generation_paused", msg.seconds_sent);
});generation_resumed
Emitted when resume is accepted. The stream continues where it froze.
Listener: onGenerationResumed · React hook: useLtx2GenerationResumed
| Field | Type | Description |
|---|---|---|
| seconds_sent | number | Seconds streamed so far. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onGenerationResumed((msg) => {
console.log("generation_resumed", msg.seconds_sent);
});
await ltx2.connect(jwt);React
import { useLtx2GenerationResumed } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2GenerationResumed((msg) => {
console.log("generation_resumed", msg.seconds_sent);
});generation_started
Emitted once when a run begins, before any frame is sent.
The first window must denoise before anything can stream — a few seconds with the bootstrap window, longer when it is disabled. Treat it as the cue to show progress, not to expect video instantly.
Listener: onGenerationStarted · React hook: useLtx2GenerationStarted
| Field | Type | Description |
|---|---|---|
| width | number | Width of every frame on main_video. |
| height | number | Height of every frame on main_video. |
| seconds | number | Length of the run in seconds. |
| total_windows | number | Windows this run will stream, each one a window_progress and a stretch of video and audio on the output tracks. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onGenerationStarted((msg) => {
console.log(
"generation_started",
msg.width,
msg.height,
msg.seconds,
msg.total_windows,
);
});
await ltx2.connect(jwt);React
import { useLtx2GenerationStarted } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2GenerationStarted((msg) => {
console.log(
"generation_started",
msg.width,
msg.height,
msg.seconds,
msg.total_windows,
);
});generation_stopped
Emitted when a run ends early because stop was accepted.
Every condition is kept, so start immediately begins a fresh take with
the same setup (edit anything first — the model is idle).
Listener: onGenerationStopped · React hook: useLtx2GenerationStopped
| Field | Type | Description |
|---|---|---|
| seconds_sent | number | Seconds streamed before the stop. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onGenerationStopped((msg) => {
console.log("generation_stopped", msg.seconds_sent);
});
await ltx2.connect(jwt);React
import { useLtx2GenerationStopped } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2GenerationStopped((msg) => {
console.log("generation_stopped", msg.seconds_sent);
});generation_complete
Emitted when a run reaches the end of its script.
The model then idles; change a condition or send reset, then start
again for a new take.
Listener: onGenerationComplete · React hook: useLtx2GenerationComplete
| Field | Type | Description |
|---|---|---|
| seconds_sent | number | Seconds streamed over the run. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onGenerationComplete((msg) => {
console.log("generation_complete", msg.seconds_sent);
});
await ltx2.connect(jwt);React
import { useLtx2GenerationComplete } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2GenerationComplete((msg) => {
console.log("generation_complete", msg.seconds_sent);
});avatar_image_accepted
Emitted when set_avatar_image is accepted and the image decodes.
Listener: onAvatarImageAccepted · React hook: useLtx2AvatarImageAccepted
| Field | Type | Description |
|---|---|---|
| width | number | Width of the uploaded image in pixels, as supplied and before it is fitted to the generation canvas. |
| height | number | Height of the uploaded image in pixels, as supplied and before it is fitted to the generation canvas. |
| filename | string | File name as uploaded. |
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onAvatarImageAccepted((msg) => {
console.log(
"avatar_image_accepted",
msg.width,
msg.height,
msg.filename,
);
});
await ltx2.connect(jwt);React
import { useLtx2AvatarImageAccepted } from "@reactor-models/ltx2";
// Inside a React component wrapped by <Ltx2Provider>:
useLtx2AvatarImageAccepted((msg) => {
console.log(
"avatar_image_accepted",
msg.width,
msg.height,
msg.filename,
);
});Tracks
Named media channels between your app and the Ltx2 model. Use the typed helpers below — Ltx2Model.publish<Track> / on<Track> in plain JS, and useLtx2Track or the per-track <Ltx2<Track>View> components in React — so track names are checked at compile time.
main_video
A video channel you subscribe to — the model publishes this for your app to render.
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onMainVideo((track, stream) => {
// attach to a <video> element, pipe to a canvas, etc.
videoEl.srcObject = stream;
});
await ltx2.connect(jwt);React
"use client";
import { Ltx2MainVideoView } from "@reactor-models/ltx2";
// Inside a component wrapped by <Ltx2Provider>:
export function Example() {
return <Ltx2MainVideoView className="w-full aspect-video" />;
}main_audio
A audio channel you subscribe to — the model publishes this for your app to render.
JavaScript
import { Ltx2Model } from "@reactor-models/ltx2";
const ltx2 = new Ltx2Model();
ltx2.onMainAudio((track, stream) => {
// attach to a <audio> element, pipe to a canvas, etc.
videoEl.srcObject = stream;
});
await ltx2.connect(jwt);React
"use client";
import { useLtx2Track } from "@reactor-models/ltx2";
// Inside a component wrapped by <Ltx2Provider>:
export function Example() {
const track = useLtx2Track("main_audio");
// attach `track` to an <audio> element via a ref + srcObject.
return null;
}