> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reactor.inc/llms.txt
> Use this file to discover all available pages before exploring further.

# X2 Overview

> What X2 is, its key features, and a quick start.

X2 is a real-time video-to-video editing model from XMAX. You stream video into it, such as a webcam
stream, a video clip, or even a still image. Give it an editing instruction and it streams back a
re-rendered version of that video live. A reference image lets you insert or swap a specific
character or object into the scene, and clicking and dragging with your mouse lets you steer the
edited subject's motion.

Three things set X2 apart from the other models in the catalog:

* It edits a live inbound video stream rather than generating from scratch: the client publishes a
  `source` track and receives the transformed `main_video` track back.
* A reference image carries visual identity: "the specified character" in your prompt means the
  image you uploaded, so swaps and insertions anchor to a concrete subject.
* A click-and-drag pointer steers the edited subject in real time, which turns a still image into a
  controllable animation.

The X2 reference is split across four pages: this overview, the complete
[command and event schema](/model-api-reference/x2/schema), the
[prompt guide](/model-api-reference/x2/prompt-guide) for writing prompts that steer the model, and
an end-to-end [tutorial](/model-api-reference/x2/tutorial).

Build on X2 with the typed [`@reactor-models/x2`](https://www.npmjs.com/package/@reactor-models/x2)
SDK. For plain JavaScript it has an `X2Model` class with named methods (`setPrompt`,
`setReferenceImage`, `setPointer`, …). For React 18+ it has an `<X2Provider>` with hooks (`useX2`,
one hook per message) and track view components (`X2MainVideoView`, `X2SourceView`). It wraps the
same base wire protocol every Reactor model speaks, so you can also drive it from the base
[`Reactor`](/sdk-reference/reactor-class) client by command name. See
[Typed Model SDKs](/sdk-reference/typed-model-sdk).

## At a glance

| Spec               | Value                                                                  |
| ------------------ | ---------------------------------------------------------------------- |
| **Model name**     | `xmax/x2`                                                              |
| **Pricing**        | Private preview; rate not yet published                                |
| **Output rate**    | 24 fps (no frame interpolation, no upscaling)                          |
| **Resolution**     | 832p; aspect ratio follows the source stream, fixed per session        |
| **Session length** | Unlimited; the model edits for as long as the session is open          |
| **Input**          | Video stream + text prompt + optional reference image and drag pointer |

The model name is the string you pass when you open a session; the typed SDK bakes it in
(`new X2Model()`), and with the base class you pass it yourself
(`new Reactor({ modelName: "xmax/x2" })`). The output resolution is not fixed ahead of time. The
model picks a resolution bucket of about 832p to match the incoming `source` stream's aspect ratio
at the first generation. That choice holds for the whole session, and arrives in the
`generation_started` message and the `state_update` snapshot. See
[Pricing & Billing](/resources/billing) for how billing works.

## Key features

<CardGroup cols={3}>
  <Card title="Live video-to-video editing" icon="clapperboard">
    Publish any video stream to the `source` track and receive it re-rendered per your prompt on
    `main_video` in real time.
  </Card>

  <Card title="Reference-anchored swaps" icon="image-plus">
    Upload a reference image of a character or object, then prompt swaps and insertions against it.
    Swap it mid-run and the stream restarts, conditioned on the new image from its first block.
  </Card>

  <Card title="Drag-to-steer motion" icon="pointer">
    A drag pointer steers the edited subject while held. Stream a still image as the source and
    the pointer becomes a drag-to-animate control.
  </Card>
</CardGroup>

Beyond these, you can hot-swap the prompt mid-stream with `set_prompt` (the change applies from the
next generated block), and trade latency for smoothness per use case with `set_keep_backlog`. The
[prompt guide](/model-api-reference/x2/prompt-guide) covers how to write effective prompts.

## Quick start

<Tabs>
  <Tab title="npm">
    ```shell theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
    npx create-reactor-app my-x2-app --model=x2
    ```
  </Tab>

  <Tab title="pnpm">
    ```shell theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
    pnpm create reactor-app my-x2-app --model=x2
    ```
  </Tab>
</Tabs>

A minimal connect-and-edit flow looks like:

<CodeGroup>
  ```typescript TypeScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  import { X2Model } from "@reactor-models/x2";

  const video = document.querySelector("video")!;
  const x2 = new X2Model();

  // Render edited frames as soon as they arrive.
  x2.onMainVideo((track, stream) => {
    video.srcObject = stream;
    void video.play();
  });

  const jwt = await getToken(); // token minted on your server
  await x2.connect(jwt);

  // Publish the stream to edit: a webcam here, but any MediaStreamTrack works.
  const media = await navigator.mediaDevices.getUserMedia({ video: true });
  await x2.publishSource(media.getVideoTracks()[0]);

  // There is no start command: generation begins on its own once a
  // non-empty prompt is set and source frames are arriving.
  await x2.setPrompt({
    prompt: "replace the character in the video with the character from the reference image",
  });
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  import asyncio
  import os
  from reactor_sdk import Reactor, ReactorStatus

  async def main():
      reactor = Reactor(model_name="xmax/x2", api_key=os.environ["REACTOR_API_KEY"])

      # Edited frames arrive as (H, W, 3) uint8 RGB NumPy arrays.
      @reactor.on_frame
      def on_frame(frame):
          print(f"Frame: {frame.shape}")

      # No start command: generation begins once a non-empty prompt is set
      # and source frames are arriving on the `source` track.
      @reactor.on_status(ReactorStatus.READY)
      async def on_ready(status):
          await reactor.send_command("set_prompt", {
              "prompt": "the specified character interacts with the scene",
          })

      await reactor.connect()
      await asyncio.Event().wait()  # run until interrupted

  asyncio.run(main())
  ```
</CodeGroup>

## How it works

On connect the model is live but idle. There is no `start` command. To get your first edited stream:

1. Connect to the model.
2. Publish a video stream to the [`source` track](/model-api-reference/x2/schema#tracks): a webcam,
   a playing clip, or a still image repeated as a constant feed.
3. Set a prompt with [`set_prompt`](/model-api-reference/x2/schema#set_prompt). Generation starts on
   its own once a non-empty prompt is set and source frames are arriving; the `generation_started`
   message carries the output resolution.
4. To anchor a character or object swap, upload and set a reference image with
   [`set_reference_image`](/model-api-reference/x2/schema#set_reference_image). Replacing it mid-run
   restarts the stream, and the new image conditions the edit from its first block.
5. Steer the edited subject with the [drag pointer](/model-api-reference/x2/schema#set_pointer), and
   pick a latency policy with [`set_keep_backlog`](/model-api-reference/x2/schema#set_keep_backlog).
6. Reset with [`reset`](/model-api-reference/x2/schema#reset) to stop generation and clear the
   prompt, reference image, and pointer.

<Note>
  X2 generates video one block at a time, and every control follows block semantics: a prompt change
  applies from the next block, the model samples the pointer once per block, and the backlog policy
  switches at the next block. The model chooses the output resolution once, at the first generation,
  from the source stream's aspect ratio; it stays fixed for the whole session, even across resets.
  See the [schema](/model-api-reference/x2/schema) for the full surface.
</Note>
