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/fast-h3

v1.4.0

Published

Strongly-typed SDK for the FastH3 model on Reactor

Readme

@reactor-models/fast-h3

Typed JavaScript + React SDK for the FastH3 model on Reactor. Version v1.4.0.


Get started

Scaffold a starter app for FastH3 with create-reactor-app:

npx create-reactor-app my-app --model=fast-h3
pnpm dlx create-reactor-app my-app --model=fast-h3

Install

npm install @reactor-models/fast-h3
pnpm add @reactor-models/fast-h3

The package exports a plain-JavaScript client and a set of React bindings. Import whichever you need from @reactor-models/fast-h3:

import { FastH3Model } from "@reactor-models/fast-h3";
import { FastH3Provider, useFastH3 } from "@reactor-models/fast-h3";

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 { FastH3Provider } from "@reactor-models/fast-h3";
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 (
    <FastH3Provider jwtToken={token} connectOptions={{ autoConnect: true }}>
      <ReactorView className="w-full aspect-video" />
    </FastH3Provider>
  );
}

Connect

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.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 { FastH3Provider, useFastH3 } from "@reactor-models/fast-h3";

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 } = useFastH3();
  return <span>Status: {status}</span>;
}

export default function App() {
  const token = use(tokenPromise);
  return (
    <FastH3Provider jwtToken={token}>
      <Controller />
    </FastH3Provider>
  );
}

Events

Client-to-model commands. The typed surface is FastH3Model (one method per event) in plain JS, and useFastH3() 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).

pop

Remove one clip by its UUID from whichever queue holds it, freeing its slot. Works on generating and built clips alike; a build already running for it is discarded when it completes. The clip that is playing is in neither queue — stop is the command that cuts it. A built clip stays in queue_update.history after popping, so clips referencing its frame are unaffected; an unbuilt clip that queued clips reference is refused, since popping it would leave them without an endpoint — pop those first. Emits clip_popped, queue_update and state_update, or command_error when no queued clip has that id or queued clips still reference its last frame.

Returns: clip_popped{ type: "clip_popped", clip: null } (or undefined when the send fails).

| Parameter | Type | Description | |---|---|---| | clip_id | string | UUID of the queued clip to remove, from clip_queued or queue_update. (default "") |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

const reply = await fastH3.pop({ clip_id: "" });

if (reply) {
  console.log("clip_popped", reply.clip);
}

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { pop } = useFastH3();

  return <button onClick={() => pop({ clip_id: "" })}>pop</button>;
}

move

Reposition one clip within the queue that holds it — the generation queue for a clip still to build, the playout queue for a built one; clips never move between queues except by building. position 0 is the front: the next build, or what bare play and autoplay take next. Values past the end mean the back. Replies clip_moved with the queue and the resulting position, and emits queue_update; command_error when no queued clip has that id.

Returns: clip_moved{ type: "clip_moved", clip: null, queue: "", position: 0 } (or undefined when the send fails).

| Parameter | Type | Description | |---|---|---| | clip_id | string | UUID of the queued clip to move, from any clip-referencing message. (default "") | | position | number | Target position in the clip's queue, 0 = front; clamped to the end. (min 0, default 0) |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

const reply = await fastH3.move({ clip_id: "", position: 0 });

if (reply) {
  console.log(
    "clip_moved",
    reply.clip,
    reply.queue,
    reply.position,
  );
}

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { move } = useFastH3();

  return <button onClick={() => move({ clip_id: "", position: 0 })}>move</button>;
}

play

Play one clip from the playout queue. Blank clip_id plays the front clip; a UUID plays that specific one. Playing consumes the entry: it leaves the queue, clip_started marks its first frames, and when it ends the stream holds on black until the next play. Emits queue_update and state_update, or command_error when a clip is already playing, the id is unknown, or the clip is still generating.

Returns: nothing — the awaited call resolves undefined once the model's handler has run.

| Parameter | Type | Description | |---|---|---| | clip_id | string | UUID of the clip to play, from clip_generated or queue_update. Blank plays the playout queue's front clip. (default "") |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

await fastH3.play({ clip_id: "" });

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { play } = useFastH3();

  return <button onClick={() => play({ clip_id: "" })}>play</button>;
}

stop

Cut the clip that is playing. Whatever is queued on the output tracks is dropped and the picture goes to black within a fraction of a second (with set_flush_on_clip_end off, the transport drains what it holds and freezes on the last frame instead), and the session is back where a finished clip leaves it — the queue is untouched and the next play starts clean. With autoplay on this acts as a skip: the next ready clip starts on its own, so send set_autoplay off first to hold the stream. Emits clip_stopped and state_update, or command_error when no clip is playing.

Returns: nothing — the awaited call resolves undefined once the model's handler has run.

No parameters.

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

await fastH3.stop();

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { stop } = useFastH3();

  return <button onClick={() => stop()}>stop</button>;
}

reset

Return every condition to its default, drop both queues' clips and the retained history, and clear the output tracks. A clip that is playing is cut, with a clip_stopped to mark it. Valid at any time. Replies session_reset and emits queue_update and state_update.

Returns: session_reset{ type: "session_reset", was_playing: true, cleared_clips: 0 } (or undefined when the send fails).

No parameters.

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

const reply = await fastH3.reset();

if (reply) {
  console.log("session_reset", reply.was_playing, reply.cleared_clips);
}

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { reset } = useFastH3();

  return <button onClick={() => reset()}>reset</button>;
}

enqueue

Queue one clip generation. The clip enters the generation queue (at position, or the back), builds when its turn comes, and then joins the back of the playout queue, announced by clip_generated. The prompt is what the clip will show; the metadata is an opaque string echoed back on every message that references the clip, for frontends to carry their own tracking data. The clip opens from text alone, from an uploaded still (starting_frame — the image animates forward), or from an existing clip's last frame (continue_from_clip_id — any clip in the queues or in queue_update.history); a continued clip simply waits its turn until its source is built, wherever the two sit in the queue. An uploaded ending_frame or a generated clip's clean last frame (ending_from_clip_id) can accompany any opener — or stand alone — to make the clip animate into and close on that image. Set both clip-id fields to the same UUID to leave and return to one generated frame. The clip's canvas is the session's; its length is the seconds passed here (snapped to what the model can produce) or the session default, and its seed is the one passed here or the session's advancing default. Builds run through the queue in order; watch queue_update for the clip turning ready. Replies clip_queued with the clip's UUID and emits queue_update and state_update, or command_error when the queue is full, the prompt is empty or over the model's token budget, both a starting frame and a source clip are given, both ending conditions are given, a source clip is unknown or no longer retained, or an upload is not a decodable image.

Returns: clip_queued{ type: "clip_queued", clip: null } (or undefined when the send fails).

| Parameter | Type | Description | |---|---|---| | seed | number \| null | Seed for this clip. Omitted or null, the session's default is used and advances by one; passing a seed leaves the default untouched, so explicit and automatic seeding do not interfere. (min 0, default null) | | prompt | string | What the clip should show, up to 4000 characters and 1024 tokens — whichever is reached first, which for Latin script is the character count and for a denser script the token count. Past either, the enqueue is refused and says which bound and by how much; nothing is silently trimmed. Fixed once enqueued; a different scene is a new enqueue. (maxLength 4000, default "") | | seconds | number \| null | Length of this clip in seconds, between 5.167 and 14.375, snapped to the nearest length the model can produce; the clip's structure reports the effective value. Omitted or null, the session default applies. A length the deployment has not built before pays a one-off compile cost on its first build. (min 5.167, max 14.375, default null) | | metadata | string | Free-form string stored with the clip and echoed back on every message that references it. The model never reads it; use it to correlate clips with your own records — who asked for it, which group it belongs to, display text. (maxLength 2000, default "") | | position | number \| null | Where the clip enters the generation queue: 0 is the front (the next build), larger values count back from there, and anything past the end — or omitted — appends. The clip already building is unaffected either way. queue_update reports the resulting order. (min 0, default null) | | ending_frame | FileRef \| null | A still image the clip animates into and closes on — its last frame. Common formats decode; the frame is fitted to the session canvas. Combine it with a starting_frame (or a continue_from_clip_id opener) for first-and-last generation, or send it alone to fix only the ending. Omit for a clip with no fixed end or one ending on a retained generated frame. At most one of this and ending_from_clip_id; independent of how the clip opens, so it pairs with either opener. (default null) | | starting_frame | FileRef \| null | A still image the clip opens from and animates forward — image-to-video. Common formats decode; the frame is fitted to the session canvas. To continue from a video, extract the frame you want and send it here. Omit for a clip opening from text or from another clip; at most one of this and continue_from_clip_id. (default null) | | ending_from_clip_id | string | UUID of the clip whose clean retained last frame this clip animates into and closes on. Any clip in either queue or in queue_update.history qualifies, including one that has not built yet: this clip then waits for it. Blank for no generated ending target; at most one of this and ending_frame. This is independent of the opener, so it may equal continue_from_clip_id to create a hold that starts and ends on the same generated frame. (default "") | | continue_from_clip_id | string | UUID of the clip whose last frame this one opens from and animates forward — how clips chain into a continuing scene. Any clip in either queue or in queue_update.history qualifies, including one that has not built yet: this clip then waits for it. Blank for a clip opening from text or from an uploaded frame; at most one of this and starting_frame. (default "") |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

const fileRef = await fastH3.uploadFile(blob);
const reply = await fastH3.enqueue({ ending_frame: fileRef, seed: null, prompt: "A sunset over the ocean", seconds: null, metadata: "", position: null, starting_frame: null, ending_from_clip_id: "", continue_from_clip_id: "" });

if (reply) {
  console.log("clip_queued", reply.clip);
}

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { enqueue, uploadFile } = useFastH3();

  async function handlePick(file: File) {
    const ref = await uploadFile(file);
    await enqueue({ ending_frame: ref, seed: null, prompt: "A sunset over the ocean", seconds: null, metadata: "", position: null, starting_frame: null, ending_from_clip_id: "", continue_from_clip_id: "" });
  }

  return <input type="file" onChange={(e) => handlePick(e.target.files![0])} />;
}

setSeed

Set the default seed — the one an enqueue without a seed of its own uses, advancing it by one, so re-enqueuing the same prompts in the same order reproduces the same clips. Clips already in the queue keep the seed they were enqueued with. Valid at any time. Emits seed_accepted and state_update.

Returns: seed_accepted{ type: "seed_accepted", seed: 0 } (or undefined when the send fails).

| Parameter | Type | Description | |---|---|---| | seed | number | Default seed for enqueues that carry none. Reproduction is close rather than exact: the deployment runs fused kernels that can reorder arithmetic. (min 0, default 1000) |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

const reply = await fastH3.setSeed({ seed: 1000 });

if (reply) {
  console.log("seed_accepted", reply.seed);
}

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { setSeed } = useFastH3();

  return <button onClick={() => setSeed({ seed: 1000 })}>setSeed</button>;
}

getQueue

Return both queues' contents — generation (waiting to build) and playout (built, playable) — plus history, the built clips no longer queued that continue_from_clip_id or ending_from_clip_id can still name, every clip as its full structure. The same payload the model broadcasts as queue_update. Valid at any time.

Returns: queue_update{ type: "queue_update", history: null, playout: null, generation: null } (or undefined when the send fails).

No parameters.

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

const reply = await fastH3.getQueue();

if (reply) {
  console.log(
    "queue_update",
    reply.history,
    reply.playout,
    reply.generation,
  );
}

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { getQueue } = useFastH3();

  return <button onClick={() => getQueue()}>getQueue</button>;
}

getState

Return a snapshot of everything the session exposes except the queue's contents (get_queue carries those): the conditions in force, what is playing, progress counters, and the commands that are valid right now. The same payload the model broadcasts as state_update. Valid at any time.

Returns: state_update{ type: "state_update", seed: 0, width: 0, aspect: "", height: 0, playing: true, autoplay: true, clip_seconds: 0, clips_played: 0, seconds_sent: 0, playout_queued: 0, valid_commands: null, playing_clip_id: null, clip_seconds_max: 0, clip_seconds_min: 0, playout_capacity: 0, flush_on_clip_end: true, generation_queued: 0, generation_capacity: 0 } (or undefined when the send fails).

No parameters.

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

const reply = await fastH3.getState();

if (reply) {
  console.log(
    "state_update",
    reply.seed,
    reply.width,
    reply.aspect,
    reply.height,
    reply.playing,
    reply.autoplay,
    reply.clip_seconds,
    reply.clips_played,
    reply.seconds_sent,
    reply.playout_queued,
    reply.valid_commands,
    reply.playing_clip_id,
    reply.clip_seconds_max,
    reply.clip_seconds_min,
    reply.playout_capacity,
    reply.flush_on_clip_end,
    reply.generation_queued,
    reply.generation_capacity,
  );
}

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { getState } = useFastH3();

  return <button onClick={() => getState()}>getState</button>;
}

setCanvas

Choose the aspect ratio of main_video. The video track keeps one size and queued clips are built at it, so this is only valid while the queue is empty and nothing is playing. Emits canvas_accepted, carrying the exact pixel size, and state_update, or command_error while clips are queued or playing, or when the ratio is not one this model offers.

Returns: canvas_accepted{ type: "canvas_accepted", width: 0, aspect: "", height: 0 } (or undefined when the send fails).

| Parameter | Type | Description | |---|---|---| | aspect | "16:9" \| "1:1" \| "9:16" \| "4:3" | Aspect ratio of main_video. canvas_accepted and state_update report the width and height in pixels it resolves to. (default "16:9") |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

const reply = await fastH3.setCanvas({ aspect: "16:9" });

if (reply) {
  console.log(
    "canvas_accepted",
    reply.width,
    reply.aspect,
    reply.height,
  );
}

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { setCanvas } = useFastH3();

  return <button onClick={() => setCanvas({ aspect: "16:9" })}>setCanvas</button>;
}

setAutoplay

Turn autoplay on or off. On, the playout queue's front clip starts on its own whenever nothing is playing — right after a clip finishes, or the moment a build completes while the stream is idle — so a steadily fed queue plays through without a play per clip. Off (the default), the stream holds on black until an explicit play. Takes effect immediately and lasts for the session. Emits autoplay_accepted and state_update.

Returns: autoplay_accepted{ type: "autoplay_accepted", enabled: true } (or undefined when the send fails).

| Parameter | Type | Description | |---|---|---| | enabled | boolean | True plays the playout queue front-first on its own; false holds the stream after each clip until play. (default false) |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

const reply = await fastH3.setAutoplay({ enabled: false });

if (reply) {
  console.log("autoplay_accepted", reply.enabled);
}

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { setAutoplay } = useFastH3();

  return <button onClick={() => setAutoplay({ enabled: false })}>setAutoplay</button>;
}

setClipSeconds

Set the default length for enqueues that carry no seconds of their own. The value is snapped to the nearest length the model can produce, so read the effective one back from clip_length_accepted. Clips already in the queue keep the length they were enqueued with. Longer clips carry a scene further; shorter ones build faster. Valid at any time. Emits clip_length_accepted and state_update.

Returns: clip_length_accepted{ type: "clip_length_accepted", frames: 0, clip_seconds: 0 } (or undefined when the send fails).

| Parameter | Type | Description | |---|---|---| | seconds | number | Clip length in seconds, between 5.167 and 14.375. Snapped to the nearest length the model can produce, so the value that takes effect can differ slightly; state_update.clip_seconds always carries the one in force. (min 5.167, max 14.375, default 14.375) |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

const reply = await fastH3.setClipSeconds({ seconds: 14.375 });

if (reply) {
  console.log("clip_length_accepted", reply.frames, reply.clip_seconds);
}

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { setClipSeconds } = useFastH3();

  return <button onClick={() => setClipSeconds({ seconds: 14.375 })}>setClipSeconds</button>;
}

setFlushOnClipEnd

Set whether the stream cuts to black when a clip ends, is stopped, or a non-continuing clip follows it. On (the default) those boundaries flush to black at once. Off, the stream holds the last frame instead — stop then drains what the transport already holds (up to a couple of seconds) before the picture freezes, rather than snapping to black. Either way, autoplay chains a clip whose continue_from_clip_id names the clip just finished with no cut at all, and reset always clears the tracks. Takes effect at the next boundary and lasts for the session. Emits flush_accepted and state_update.

Returns: flush_accepted{ type: "flush_accepted", enabled: true } (or undefined when the send fails).

| Parameter | Type | Description | |---|---|---| | enabled | boolean | True cuts to black at every non-continuing clip boundary; false holds the last frame there instead. (default true) |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
await fastH3.connect(jwt);

const reply = await fastH3.setFlushOnClipEnd({ enabled: true });

if (reply) {
  console.log("flush_accepted", reply.enabled);
}

React

"use client";
import { useFastH3 } from "@reactor-models/fast-h3";

function Example() {
  const { setFlushOnClipEnd } = useFastH3();

  return <button onClick={() => setFlushOnClipEnd({ enabled: true })}>setFlushOnClipEnd</button>;
}

Messages

Model-to-client messages. Register a typed listener with on… on FastH3Model, or a useFastH3… hook in React, to receive only the messages you care about.

clip_moved

Emitted when move repositions a clip within its queue.

Listener: onClipMoved · React hook: useFastH3ClipMoved

| Field | Type | Description | |---|---|---| | clip | { "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_ending_frame"?: boolean; "has_starting_frame": boolean; "ending_from_clip_id"?: string \| null; "continue_from_clip_id": string \| null } | The clip that moved. | | queue | string | Which queue it moved within: generation or playout. | | position | number | The clip's resulting position in that queue, 0 = front. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onClipMoved((msg) => {
  console.log(
    "clip_moved",
    msg.clip,
    msg.queue,
    msg.position,
  );
});
await fastH3.connect(jwt);

React

import { useFastH3ClipMoved } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3ClipMoved((msg) => {
  console.log(
    "clip_moved",
    msg.clip,
    msg.queue,
    msg.position,
  );
});

clip_failed

Emitted when a clip's generation fails.

The clip leaves the queue and the queue moves on; nothing else is affected.

Listener: onClipFailed · React hook: useFastH3ClipFailed

| Field | Type | Description | |---|---|---| | clip | { "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_ending_frame"?: boolean; "has_starting_frame": boolean; "ending_from_clip_id"?: string \| null; "continue_from_clip_id": string \| null } | The clip whose build failed. | | reason | string | What went wrong. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onClipFailed((msg) => {
  console.log("clip_failed", msg.clip, msg.reason);
});
await fastH3.connect(jwt);

React

import { useFastH3ClipFailed } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3ClipFailed((msg) => {
  console.log("clip_failed", msg.clip, msg.reason);
});

clip_popped

Emitted when pop removes a clip from either queue.

The clip's slot is free again immediately. A build already running for it is discarded when it completes; the GPUs cannot abandon it mid-build.

Listener: onClipPopped · React hook: useFastH3ClipPopped

| Field | Type | Description | |---|---|---| | clip | { "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_ending_frame"?: boolean; "has_starting_frame": boolean; "ending_from_clip_id"?: string \| null; "continue_from_clip_id": string \| null } | The clip that left its queue. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onClipPopped((msg) => {
  console.log("clip_popped", msg.clip);
});
await fastH3.connect(jwt);

React

import { useFastH3ClipPopped } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3ClipPopped((msg) => {
  console.log("clip_popped", msg.clip);
});

clip_queued

Emitted when enqueue accepts a generation request.

Listener: onClipQueued · React hook: useFastH3ClipQueued

| Field | Type | Description | |---|---|---| | clip | { "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_ending_frame"?: boolean; "has_starting_frame": boolean; "ending_from_clip_id"?: string \| null; "continue_from_clip_id": string \| null } | The queued clip, UUID included. ready is false here; clip_generated announces it crossing into the playout queue. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onClipQueued((msg) => {
  console.log("clip_queued", msg.clip);
});
await fastH3.connect(jwt);

React

import { useFastH3ClipQueued } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3ClipQueued((msg) => {
  console.log("clip_queued", msg.clip);
});

clip_started

Emitted as a clip begins streaming on the output tracks.

Listener: onClipStarted · React hook: useFastH3ClipStarted

| Field | Type | Description | |---|---|---| | clip | { "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_ending_frame"?: boolean; "has_starting_frame": boolean; "ending_from_clip_id"?: string \| null; "continue_from_clip_id": string \| null } | The clip now playing. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onClipStarted((msg) => {
  console.log("clip_started", msg.clip);
});
await fastH3.connect(jwt);

React

import { useFastH3ClipStarted } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3ClipStarted((msg) => {
  console.log("clip_started", msg.clip);
});

clip_stopped

Emitted when stop cuts a playing clip.

The rest of the clip is discarded — a stopped clip cannot be resumed — and the stream holds on black until the next play, exactly as after clip_finished.

Listener: onClipStopped · React hook: useFastH3ClipStopped

| Field | Type | Description | |---|---|---| | clip | { "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_ending_frame"?: boolean; "has_starting_frame": boolean; "ending_from_clip_id"?: string \| null; "continue_from_clip_id": string \| null } | The clip that was cut. | | seconds_sent | number | Seconds of video and audio sent since the session began. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onClipStopped((msg) => {
  console.log("clip_stopped", msg.clip, msg.seconds_sent);
});
await fastH3.connect(jwt);

React

import { useFastH3ClipStopped } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3ClipStopped((msg) => {
  console.log("clip_stopped", msg.clip, msg.seconds_sent);
});

queue_update

Emitted on connect and whenever either queue changes, and answers get_queue.

Both queues in full, front first, each entry a complete ClipInfo, plus the retained history of built clips that can still seed a new one. A change is any of: a clip enqueued or moved, a build finishing (the clip crosses from generation to playout), a clip leaving to play or by pop, or the queues being cleared by reset.

Listener: onQueueUpdate · React hook: useFastH3QueueUpdate

| Field | Type | Description | |---|---|---| | history | { "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_ending_frame"?: boolean; "has_starting_frame": boolean; "ending_from_clip_id"?: string \| null; "continue_from_clip_id": string \| null }[] | Built clips no longer queued — played, stopped, or popped — whose last frame is still retained, oldest first. No longer playable (play refuses them), but any can be named in enqueue's continue_from_clip_id or ending_from_clip_id; the oldest are evicted as new builds finish, so the front of this list is what expires next. | | playout | { "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_ending_frame"?: boolean; "has_starting_frame": boolean; "ending_from_clip_id"?: string \| null; "continue_from_clip_id": string \| null }[] | Built clips waiting to play, front first. A finished build joins at the back; bare play (and autoplay) takes the front; move reorders; playing or pop consumes. | | generation | { "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_ending_frame"?: boolean; "has_starting_frame": boolean; "ending_from_clip_id"?: string \| null; "continue_from_clip_id": string \| null }[] | Clips waiting to build, front first. Builds consume this queue from the front, one at a time, pausing only while playout is at capacity; a clip whose referenced starting or ending source is not built yet is skipped until it is, without blocking the rest. enqueue's position and move control the order. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onQueueUpdate((msg) => {
  console.log(
    "queue_update",
    msg.history,
    msg.playout,
    msg.generation,
  );
});
await fastH3.connect(jwt);

React

import { useFastH3QueueUpdate } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3QueueUpdate((msg) => {
  console.log(
    "queue_update",
    msg.history,
    msg.playout,
    msg.generation,
  );
});

state_update

Emitted on connect and after every change to the session's state.

One snapshot of everything observable except the queue's contents (those travel as queue_update), so a client can render its whole UI from this alone instead of accumulating the individual messages below.

Listener: onStateUpdate · React hook: useFastH3StateUpdate

| Field | Type | Description | |---|---|---| | seed | number | Seed the next enqueued clip will use when enqueue carries none; each such enqueue advances it by one. | | width | number | Width of every frame on main_video. | | aspect | string | Aspect ratio in effect, e.g. 16:9. | | height | number | Height of every frame on main_video. | | playing | boolean | A clip is streaming on the output tracks. | | autoplay | boolean | The playout queue's front clip starts on its own whenever nothing is playing. Off by default: playback waits for an explicit play. | | clip_seconds | number | Length a newly enqueued clip gets when enqueue carries no seconds of its own. | | clips_played | number | Clips that finished playing or were stopped since the session began. | | seconds_sent | number | Seconds of video and audio sent since the session began. | | playout_queued | number | Built clips in the playout queue, each playable right now. | | 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 be rejected. | | playing_clip_id | string \| null | UUID of the clip now playing, or null when the stream is idle. | | clip_seconds_max | number | Longest clip length set_clip_seconds accepts. | | clip_seconds_min | number | Shortest clip length set_clip_seconds accepts. | | playout_capacity | number | Most built clips the playout queue holds. Generation pauses while it is full and resumes as playing or pop frees a slot. | | flush_on_clip_end | boolean | The stream cuts to black when a clip ends, is stopped, or a non-continuing clip follows it (the default). Off, those transitions hold the last frame instead. Either way, autoplay chains a clip that continues the one just finished with no cut at all. | | generation_queued | number | Clips in the generation queue: enqueued, not yet built. | | generation_capacity | number | Most clips the generation queue holds; enqueue is refused beyond it. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onStateUpdate((msg) => {
  console.log(
    "state_update",
    msg.seed,
    msg.width,
    msg.aspect,
    msg.height,
    msg.playing,
    msg.autoplay,
    msg.clip_seconds,
    msg.clips_played,
    msg.seconds_sent,
    msg.playout_queued,
    msg.valid_commands,
    msg.playing_clip_id,
    msg.clip_seconds_max,
    msg.clip_seconds_min,
    msg.playout_capacity,
    msg.flush_on_clip_end,
    msg.generation_queued,
    msg.generation_capacity,
  );
});
await fastH3.connect(jwt);

React

import { useFastH3StateUpdate } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3StateUpdate((msg) => {
  console.log(
    "state_update",
    msg.seed,
    msg.width,
    msg.aspect,
    msg.height,
    msg.playing,
    msg.autoplay,
    msg.clip_seconds,
    msg.clips_played,
    msg.seconds_sent,
    msg.playout_queued,
    msg.valid_commands,
    msg.playing_clip_id,
    msg.clip_seconds_max,
    msg.clip_seconds_min,
    msg.playout_capacity,
    msg.flush_on_clip_end,
    msg.generation_queued,
    msg.generation_capacity,
  );
});

clip_finished

Emitted when a clip has been fully sent on the output tracks.

The stream then holds on black until the next play; nothing plays on its own.

Listener: onClipFinished · React hook: useFastH3ClipFinished

| Field | Type | Description | |---|---|---| | clip | { "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_ending_frame"?: boolean; "has_starting_frame": boolean; "ending_from_clip_id"?: string \| null; "continue_from_clip_id": string \| null } | The clip that just finished. | | seconds_sent | number | Seconds of video and audio sent since the session began, this clip included. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onClipFinished((msg) => {
  console.log("clip_finished", msg.clip, msg.seconds_sent);
});
await fastH3.connect(jwt);

React

import { useFastH3ClipFinished } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3ClipFinished((msg) => {
  console.log("clip_finished", msg.clip, msg.seconds_sent);
});

command_error

Emitted when a command is rejected. The command had no effect.

Listener: onCommandError · React hook: useFastH3CommandError

| Field | Type | Description | |---|---|---| | reason | string | Why it was rejected. | | command | string | Name of the command that was rejected. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onCommandError((msg) => {
  console.log("command_error", msg.reason, msg.command);
});
await fastH3.connect(jwt);

React

import { useFastH3CommandError } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3CommandError((msg) => {
  console.log("command_error", msg.reason, msg.command);
});

seed_accepted

Emitted when set_seed is accepted.

Listener: onSeedAccepted · React hook: useFastH3SeedAccepted

| Field | Type | Description | |---|---|---| | seed | number | Seed the next enqueued clip will use when enqueue carries none. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onSeedAccepted((msg) => {
  console.log("seed_accepted", msg.seed);
});
await fastH3.connect(jwt);

React

import { useFastH3SeedAccepted } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3SeedAccepted((msg) => {
  console.log("seed_accepted", msg.seed);
});

session_reset

Emitted when reset is accepted.

Every condition is back to its default, the queue is empty, and the output stream is cleared.

Listener: onSessionReset · React hook: useFastH3SessionReset

| Field | Type | Description | |---|---|---| | was_playing | boolean | A clip was playing and has been cut; a clip_stopped accompanies it. | | cleared_clips | number | Clips dropped from both queues, built and pending alike. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onSessionReset((msg) => {
  console.log("session_reset", msg.was_playing, msg.cleared_clips);
});
await fastH3.connect(jwt);

React

import { useFastH3SessionReset } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3SessionReset((msg) => {
  console.log("session_reset", msg.was_playing, msg.cleared_clips);
});

clip_generated

Emitted when a clip's build completes.

The clip has left the generation queue and joined the back of the playout queue, playable immediately. queue_update accompanies it with both queues' new contents.

Listener: onClipGenerated · React hook: useFastH3ClipGenerated

| Field | Type | Description | |---|---|---| | clip | { "seed": number; "ready": boolean; "frames": number; "prompt": string; "clip_id": string; "seconds": number; "metadata": string; "has_ending_frame"?: boolean; "has_starting_frame": boolean; "ending_from_clip_id"?: string \| null; "continue_from_clip_id": string \| null } | The freshly built clip, now at the back of the playout queue. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onClipGenerated((msg) => {
  console.log("clip_generated", msg.clip);
});
await fastH3.connect(jwt);

React

import { useFastH3ClipGenerated } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3ClipGenerated((msg) => {
  console.log("clip_generated", msg.clip);
});

flush_accepted

Emitted when set_flush_on_clip_end is accepted.

Listener: onFlushAccepted · React hook: useFastH3FlushAccepted

| Field | Type | Description | |---|---|---| | enabled | boolean | Whether the stream now cuts to black when a clip ends, is stopped, or a non-continuing clip follows it. Off, those transitions hold the last frame instead. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onFlushAccepted((msg) => {
  console.log("flush_accepted", msg.enabled);
});
await fastH3.connect(jwt);

React

import { useFastH3FlushAccepted } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3FlushAccepted((msg) => {
  console.log("flush_accepted", msg.enabled);
});

canvas_accepted

Emitted when set_canvas is accepted.

Listener: onCanvasAccepted · React hook: useFastH3CanvasAccepted

| Field | Type | Description | |---|---|---| | width | number | Width of every frame on main_video. | | aspect | string | Aspect ratio now in effect. | | height | number | Height of every frame on main_video. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onCanvasAccepted((msg) => {
  console.log(
    "canvas_accepted",
    msg.width,
    msg.aspect,
    msg.height,
  );
});
await fastH3.connect(jwt);

React

import { useFastH3CanvasAccepted } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3CanvasAccepted((msg) => {
  console.log(
    "canvas_accepted",
    msg.width,
    msg.aspect,
    msg.height,
  );
});

autoplay_accepted

Emitted when set_autoplay is accepted.

Listener: onAutoplayAccepted · React hook: useFastH3AutoplayAccepted

| Field | Type | Description | |---|---|---| | enabled | boolean | Whether ready clips now start on their own when nothing is playing. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onAutoplayAccepted((msg) => {
  console.log("autoplay_accepted", msg.enabled);
});
await fastH3.connect(jwt);

React

import { useFastH3AutoplayAccepted } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3AutoplayAccepted((msg) => {
  console.log("autoplay_accepted", msg.enabled);
});

clip_length_accepted

Emitted when set_clip_seconds is accepted.

The requested length is snapped to the nearest length the model can produce, so the value here may differ slightly from the one sent.

Listener: onClipLengthAccepted · React hook: useFastH3ClipLengthAccepted

| Field | Type | Description | |---|---|---| | frames | number | Frames each newly enqueued clip will carry. | | clip_seconds | number | Clip length now in effect, in seconds. |

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onClipLengthAccepted((msg) => {
  console.log("clip_length_accepted", msg.frames, msg.clip_seconds);
});
await fastH3.connect(jwt);

React

import { useFastH3ClipLengthAccepted } from "@reactor-models/fast-h3";

// Inside a React component wrapped by <FastH3Provider>:
useFastH3ClipLengthAccepted((msg) => {
  console.log("clip_length_accepted", msg.frames, msg.clip_seconds);
});

Tracks

Named media channels between your app and the FastH3 model. Use the typed helpers below — FastH3Model.publish<Track> / on<Track> in plain JS, and useFastH3Track or the per-track <FastH3<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 { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onMainVideo((track, stream) => {
  // attach to a <video> element, pipe to a canvas, etc.
  videoEl.srcObject = stream;
});
await fastH3.connect(jwt);

React

"use client";
import { FastH3MainVideoView } from "@reactor-models/fast-h3";

// Inside a component wrapped by <FastH3Provider>:
export function Example() {
  return <FastH3MainVideoView className="w-full aspect-video" />;
}

main_audio

A audio channel you subscribe to — the model publishes this for your app to render.

JavaScript

import { FastH3Model } from "@reactor-models/fast-h3";

const fastH3 = new FastH3Model();
fastH3.onMainAudio((track, stream) => {
  // attach to a <audio> element, pipe to a canvas, etc.
  videoEl.srcObject = stream;
});
await fastH3.connect(jwt);

React

"use client";
import { useFastH3Track } from "@reactor-models/fast-h3";

// Inside a component wrapped by <FastH3Provider>:
export function Example() {
  const track = useFastH3Track("main_audio");
  // attach `track` to an <audio> element via a ref + srcObject.
  return null;
}