npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@reactor-models/ltx2

v5.0.1

Published

Strongly-typed SDK for the Ltx2 model on Reactor

Downloads

1,116

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=ltx2
pnpm dlx create-reactor-app my-app --model=ltx2

Install

npm install @reactor-models/ltx2
pnpm add @reactor-models/ltx2

The 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;
}