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/x2

v0.5.2

Published

Strongly-typed SDK for the X2 model on Reactor

Readme

@reactor-models/x2

Typed JavaScript + React SDK for the X2 model on Reactor. Version v0.5.2.


Get started

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

npx create-reactor-app my-app --model=x2
pnpm dlx create-reactor-app my-app --model=x2

Install

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

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

import { X2Model } from "@reactor-models/x2";
import { X2Provider, useX2 } from "@reactor-models/x2";

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

Connect

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
await x2.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 { X2Provider, useX2 } from "@reactor-models/x2";

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

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

Events

Client-to-model commands. The typed surface is X2Model (one method per event) in plain JS, and useX2() in React — every field name below matches the parameter name the method accepts.

reset

Stop generation and clear the prompt, reference image, and pointer, returning the session to waiting for new conditions. Valid any time. Emits generation_stopped with reason reset if a run was active, and state_update.

Emits: generation_stopped, state_update

No parameters.

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
await x2.connect(jwt);

await x2.reset();

React

"use client";
import { useX2 } from "@reactor-models/x2";

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

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

setPrompt

Set the editing instruction that guides the re-render (for example a character swap or insertion). A non-empty prompt is required before generation begins; setting it while generating applies from the next block. Emits prompt_accepted, then state_update.

Emits: prompt_accepted, state_update

| Parameter | Type | Required | Description | |---|---|---|---| | prompt | string | | The editing instruction, e.g. "replace the person with the character from the reference image". Up to 1000 characters. Applies from the next generated block. (maxLength 1000, default "") |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
await x2.connect(jwt);

await x2.setPrompt({ prompt: "A sunset over the ocean" });

React

"use client";
import { useX2 } from "@reactor-models/x2";

function Example() {
  const { setPrompt } = useX2();

  return <button onClick={() => setPrompt({ prompt: "A sunset over the ocean" })}>setPrompt</button>;
}

setPointer

Update the drag pointer that steers the edited subject's motion, setting its position and press state together. Valid any time; the pointer is sampled once per generated block and only has an effect while active. Emits pointer_changed, then state_update.

Emits: pointer_changed, state_update

| Parameter | Type | Required | Description | |---|---|---|---| | x | number | | Horizontal pointer position, normalized to the output frame (0 = left, 1 = right). (min 0, max 1, default 0.5) | | y | number | | Vertical pointer position, normalized to the output frame (0 = top, 1 = bottom). (min 0, max 1, default 0.5) | | active | boolean | | Whether a drag gesture is in progress. While true, the pointer steers motion; while false, it is ignored. (default false) |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
await x2.connect(jwt);

await x2.setPointer({ x: 0.5, y: 0.5, active: false });

React

"use client";
import { useX2 } from "@reactor-models/x2";

function Example() {
  const { setPointer } = useX2();

  return <button onClick={() => setPointer({ x: 0.5, y: 0.5, active: false })}>setPointer</button>;
}

setPointerX

Set pointer_x

| Parameter | Type | Required | Description | |---|---|---|---| | pointer_x | number | | Horizontal drag-pointer position, normalized to the output frame (0 = left edge, 1 = right edge). Sampled once per generated block and only steers motion while pointer_active is true. (min 0, max 1, default 0.5) |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
await x2.connect(jwt);

await x2.setPointerX({ pointer_x: 0.5 });

React

"use client";
import { useX2 } from "@reactor-models/x2";

function Example() {
  const { setPointerX } = useX2();

  return <button onClick={() => setPointerX({ pointer_x: 0.5 })}>setPointerX</button>;
}

setPointerY

Set pointer_y

| Parameter | Type | Required | Description | |---|---|---|---| | pointer_y | number | | Vertical drag-pointer position, normalized to the output frame (0 = top edge, 1 = bottom edge). Sampled once per generated block and only steers motion while pointer_active is true. (min 0, max 1, default 0.5) |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
await x2.connect(jwt);

await x2.setPointerY({ pointer_y: 0.5 });

React

"use client";
import { useX2 } from "@reactor-models/x2";

function Example() {
  const { setPointerY } = useX2();

  return <button onClick={() => setPointerY({ pointer_y: 0.5 })}>setPointerY</button>;
}

setKeepBacklog

Set keep_backlog

| Parameter | Type | Required | Description | |---|---|---|---| | keep_backlog | boolean | | Source-frame consumption policy, applied from the next block. False (default): always read the newest frames and drop backlog, keeping latency bounded when inference is slower than the source. True: consume every frame in order for smoother motion (e.g. drag mode) at the cost of growing delay. (default false) |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
await x2.connect(jwt);

await x2.setKeepBacklog({ keep_backlog: false });

React

"use client";
import { useX2 } from "@reactor-models/x2";

function Example() {
  const { setKeepBacklog } = useX2();

  return <button onClick={() => setKeepBacklog({ keep_backlog: false })}>setKeepBacklog</button>;
}

setPointerActive

Set pointer_active

| Parameter | Type | Required | Description | |---|---|---|---| | pointer_active | boolean | | Whether a drag gesture is in progress. While true, the current pointer_x/pointer_y position steers the motion of the edited subject; while false, the pointer is ignored. Sampled once per generated block. (default false) |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
await x2.connect(jwt);

await x2.setPointerActive({ pointer_active: false });

React

"use client";
import { useX2 } from "@reactor-models/x2";

function Example() {
  const { setPointerActive } = useX2();

  return <button onClick={() => setPointerActive({ pointer_active: false })}>setPointerActive</button>;
}

setReferenceImage

Provide a reference image of a character or object to insert or swap into the video. Can be set before generation or replaced while generating; replacing it mid-run restarts the stream (generation_stopped with reason reference_image_changed, then a fresh generation_started) so the new reference conditions it from the first block. Emits reference_image_accepted and state_update on success, or command_error if the file cannot be decoded as an image.

Emits: generation_stopped, generation_started, reference_image_accepted, state_update, command_error

| Parameter | Type | Required | Description | |---|---|---|---| | reference_image | FileRef | ✅ | Reference to a file uploaded via the Reactor presigned-URL protocol. |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
await x2.connect(jwt);

const fileRef = await x2.uploadFile(blob);
await x2.setReferenceImage({ reference_image: fileRef });

React

"use client";
import { useX2 } from "@reactor-models/x2";

function Example() {
  const { setReferenceImage, uploadFile } = useX2();

  async function handlePick(file: File) {
    const ref = await uploadFile(file);
    await setReferenceImage({ reference_image: ref });
  }

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

Messages

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

state_update

Emitted on connect and after every observable state change; carries the full state in one payload.

Listener: onStateUpdate · React hook: useX2StateUpdate

| Field | Type | Description | |---|---|---| | width | unknown | Output frame width in pixels once a run has started, or null before the resolution is chosen. Fixed for the whole session. | | height | unknown | Output frame height in pixels once a run has started, or null before the resolution is chosen. Fixed for the whole session. | | prompt | unknown | The active editing instruction, or null when none is set. Generation does not run without a prompt. | | pointer_x | number | Current horizontal drag-pointer position, normalized to the output frame (0 = left, 1 = right). | | pointer_y | number | Current vertical drag-pointer position, normalized to the output frame (0 = top, 1 = bottom). | | generating | boolean | Whether a generation run is currently producing main_video frames. | | keep_backlog | boolean | Current source-frame policy. False: read the newest frames and drop backlog (bounded latency). True: consume every frame in order (smoother motion, growing delay). | | pointer_active | boolean | Whether a drag gesture is currently steering the edited subject. | | has_reference_image | boolean | Whether a reference image is currently conditioning generation. |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
x2.onStateUpdate((msg) => {
  console.log(
    "state_update",
    msg.width,
    msg.height,
    msg.prompt,
    msg.pointer_x,
    msg.pointer_y,
    msg.generating,
    msg.keep_backlog,
    msg.pointer_active,
    msg.has_reference_image,
  );
});
await x2.connect(jwt);

React

import { useX2StateUpdate } from "@reactor-models/x2";

// Inside a React component wrapped by <X2Provider>:
useX2StateUpdate((msg) => {
  console.log(
    "state_update",
    msg.width,
    msg.height,
    msg.prompt,
    msg.pointer_x,
    msg.pointer_y,
    msg.generating,
    msg.keep_backlog,
    msg.pointer_active,
    msg.has_reference_image,
  );
});

command_error

Emitted when a command cannot be applied; the command has no effect.

Listener: onCommandError · React hook: useX2CommandError

| Field | Type | Description | |---|---|---| | reason | string | Human-readable explanation of why the command was rejected. | | command | string | Name of the command that failed, e.g. set_reference_image. |

JavaScript

import { X2Model } from "@reactor-models/x2";

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

React

import { useX2CommandError } from "@reactor-models/x2";

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

pointer_changed

Emitted after set_pointer updates the drag pointer.

Listener: onPointerChanged · React hook: useX2PointerChanged

| Field | Type | Description | |---|---|---| | x | number | Horizontal pointer position, normalized to the output frame (0 = left, 1 = right). | | y | number | Vertical pointer position, normalized to the output frame (0 = top, 1 = bottom). | | active | boolean | Whether a drag gesture is in progress. While true, the pointer steers the edited subject's motion. |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
x2.onPointerChanged((msg) => {
  console.log(
    "pointer_changed",
    msg.x,
    msg.y,
    msg.active,
  );
});
await x2.connect(jwt);

React

import { useX2PointerChanged } from "@reactor-models/x2";

// Inside a React component wrapped by <X2Provider>:
useX2PointerChanged((msg) => {
  console.log(
    "pointer_changed",
    msg.x,
    msg.y,
    msg.active,
  );
});

prompt_accepted

Emitted after set_prompt succeeds; echoes the accepted prompt.

Listener: onPromptAccepted · React hook: useX2PromptAccepted

| Field | Type | Description | |---|---|---| | prompt | string | The editing instruction now in effect. It conditions generation from the next block onward. |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
x2.onPromptAccepted((msg) => {
  console.log("prompt_accepted", msg.prompt);
});
await x2.connect(jwt);

React

import { useX2PromptAccepted } from "@reactor-models/x2";

// Inside a React component wrapped by <X2Provider>:
useX2PromptAccepted((msg) => {
  console.log("prompt_accepted", msg.prompt);
});

generation_started

Emitted once when a generation run begins producing main_video.

Listener: onGenerationStarted · React hook: useX2GenerationStarted

| Field | Type | Description | |---|---|---| | width | number | Output frame width in pixels. Chosen once at the first generation and fixed for the whole session. | | height | number | Output frame height in pixels. Chosen once at the first generation and fixed for the whole session. | | prompt | string | The editing instruction the run started with. | | has_reference_image | boolean | Whether a reference image is conditioning this run. |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
x2.onGenerationStarted((msg) => {
  console.log(
    "generation_started",
    msg.width,
    msg.height,
    msg.prompt,
    msg.has_reference_image,
  );
});
await x2.connect(jwt);

React

import { useX2GenerationStarted } from "@reactor-models/x2";

// Inside a React component wrapped by <X2Provider>:
useX2GenerationStarted((msg) => {
  console.log(
    "generation_started",
    msg.width,
    msg.height,
    msg.prompt,
    msg.has_reference_image,
  );
});

generation_stopped

Emitted when a generation run ends; no more main_video frames follow until it restarts.

Listener: onGenerationStopped · React hook: useX2GenerationStopped

| Field | Type | Description | |---|---|---| | reason | string | Why generation stopped: reset (cleared by the reset command) or reference_image_changed (a new reference image triggered an automatic restart, and a fresh generation_started follows). |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
x2.onGenerationStopped((msg) => {
  console.log("generation_stopped", msg.reason);
});
await x2.connect(jwt);

React

import { useX2GenerationStopped } from "@reactor-models/x2";

// Inside a React component wrapped by <X2Provider>:
useX2GenerationStopped((msg) => {
  console.log("generation_stopped", msg.reason);
});

reference_image_accepted

Emitted after set_reference_image decodes an uploaded image.

Listener: onReferenceImageAccepted · React hook: useX2ReferenceImageAccepted

| Field | Type | Description | |---|---|---| | width | number | Width, in pixels, of the decoded reference image, before it is fitted to the output resolution. | | height | number | Height, in pixels, of the decoded reference image, before it is fitted to the output resolution. |

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
x2.onReferenceImageAccepted((msg) => {
  console.log("reference_image_accepted", msg.width, msg.height);
});
await x2.connect(jwt);

React

import { useX2ReferenceImageAccepted } from "@reactor-models/x2";

// Inside a React component wrapped by <X2Provider>:
useX2ReferenceImageAccepted((msg) => {
  console.log("reference_image_accepted", msg.width, msg.height);
});

Tracks

Named media channels between your app and the X2 model. Use the typed helpers below — X2Model.publish<Track> / on<Track> in plain JS, and useX2Track or the per-track <X2<Track>View> components in React — so track names are checked at compile time.

source

A video channel you push media into (for example, the user's webcam).

JavaScript

import { X2Model } from "@reactor-models/x2";

const x2 = new X2Model();
await x2.connect(jwt);

const media = await navigator.mediaDevices.getUserMedia({ video: true });
const videoTrack = media.getVideoTracks()[0];
await x2.publishSource(videoTrack);

// later, to stop sending:
await x2.unpublishSource();

React

"use client";
import { X2SourceView } from "@reactor-models/x2";

// Inside a component wrapped by <X2Provider>:
export function Example() {
  return <X2SourceView />;
}

main_video

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

JavaScript

import { X2Model } from "@reactor-models/x2";

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

React

"use client";
import { X2MainVideoView } from "@reactor-models/x2";

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