Skip to main content
This is a self-contained API walkthrough. It uses the typed SDK to edit a webcam stream. The snippets define their own DOM elements and token route. The example template provides a complete interface with source selection.

Installation and setup

Create a Next.js app with TypeScript and the App Router, then install the typed SDK:
Add your Reactor API key to .env.local:
Keep this key on the server. Restart the development server after changing the environment file.

How XMAX X2.1 works

The browser publishes a source video track and receives main_video. Once source frames arrive, set_prompt enables generation. An empty string is a valid prompt. A null prompt in state_update means no prompt was set. Use command replies to show whether an input was accepted. Use state_update and received video to show what the model does. The typed SDK flattens command replies, so await model.setPrompt(...) returns a payload with .prompt. Base SDK replies have the envelope { type, data }.

Authentication

Create app/api/reactor/token/route.ts. The route exchanges the server key for a session-scoped token and returns its expiry. The token can create sessions only for this model.
The Python tab is an alternative broker for an existing Python backend. The Next.js app below uses the route above.

Publish and view video

Replace app/page.tsx with this client component. It creates both video elements, starts on a user click, and releases the camera on disconnect.
app/page.tsx
Run npm run dev, open the local page, and click Connect. Allow camera access. If playback is blocked, handle the displayed playback error with an explicit play control.

Add references and drag control

For a reference picker, upload the selected File, then await setReferenceImage. Keep the previous selection until the reply confirms acceptance. The new reference applies from the next block without a stop or restart message. Expect one short pause and a visible transition over the next few blocks.
For drag control, sample positions at the source’s frame cadence. Keep the last four samples, oldest first, and call setPointers({ frames }). Each sample contains zero to eight { x, y } points normalized to the output frame. Account for any letterboxing when converting screen coordinates. Use pointer capture and touch-action: none on the interaction surface. On release, cancellation, or window blur, send setPointers({ frames: [[], [], [], []] }). The schema explains how stale windows hold their newest positions.

Surfacing command_error

Listen for SDK errors for the whole connection lifetime. The example uses model.on("error", ...). The supplied model schema does not declare a typed command_error message or hook. A typed command can resolve to undefined when it fails. Read getLastError() and keep the prior UI selection when a reply is absent. reset declares no reply payload, so undefined alone does not indicate failure for that command. Use SDK errors and the next state_update to confirm its effect. Await replies for the command you sent. A general message listener is not a substitute for the correlated reply. Keep listeners active for later errors, and remove temporary listeners when their operation ends.