Skip to main content

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.

Available in @reactor-team/js-sdk 2.10 and above. The Python SDK does not expose recordings yet.

What is a clip?

Once a model has recording enabled, your app can ask Reactor for two types of clip from a live session:
  • Snap clip: the last N seconds of the session.
  • Full recording: everything from the start of the session up to now.
Both return a Clip object you can hand to the SDK’s player to preview, or to its download helpers to save as a single MP4. The same object works either way.
Reactor does not host clips. The URL you receive from Reactor expires after 24 hours, so it is not suitable for sharing. If you want users to keep a clip, download it immediately and host the resulting MP4 yourself.
Reactor holds clips for 24 hours and then deletes them. None of this data is used for training.

Capturing a clip

Recording lives on the Reactor instance. Inside a ReactorProvider, reach it via useReactor().
const snap = await reactor.requestClip(10);
const full = await reactor.requestRecording();
requestClip takes a duration in seconds and grabs the live session up to that point in the past. It is capped server-side at 5 minutes by default. requestRecording takes no arguments and grabs the full session from the start of recording up to now. Both resolve with the same Clip object.
Both methods can only be called when the connection status is "ready". They throw a RecordingError with code DISCONNECTED otherwise, and a request still in flight when the session disconnects is rejected with the same code.

Previewing a clip

Play a captured clip in place. In React, drop a ClipPlayer into your UI and pass the clip; it renders a native <video controls> element. Imperatively, assemble the clip into an MP4 with downloadClipAsFile() and point a <video> at the blob.
// downloadClipAsFile assembles the clip into a single MP4 and authenticates
// with the jwt. Point a <video> element at the resulting blob to play it.
const blob = await reactor.downloadClipAsFile(clip, null, { jwt });

const video = document.querySelector("video")!;
video.src = URL.createObjectURL(blob);
video.controls = true;
ClipPlayer does not require a ReactorProvider in the tree, so it keeps working after the session has ended.
ClipPlayer streams the clip as HTTP Live Streaming (HLS), a protocol that breaks video into small chunks served over HTTP. Safari plays it natively, while Chrome, Firefox, and Edge need hls.js installed as an optional peer dependency. The imperative downloadClipAsFile route above returns a plain MP4 and needs no extra dependency.
pnpm add hls.js
See ClipPlayer for the full prop list.

Downloading a clip

ClipDownloadButton renders a plain <button> that reflects download state and triggers a browser save dialog when clicked. Pass the same token you use to connect as getJwt so the component can authenticate the download.
React
import { ClipDownloadButton } from "@reactor-team/js-sdk";

<ClipDownloadButton clip={clip} getJwt={() => jwt} />;
For non-React apps, or when you need the assembled MP4 as a Blob, call reactor.downloadClipAsFile() directly.
JavaScript
await reactor.downloadClipAsFile(clip, "highlight.mp4");

const blob = await reactor.downloadClipAsFile(clip, null);
Building your own download UI? The headless useClipDownload hook exposes the same state machine as ClipDownloadButton, so you can render a progress bar, a menu item, or anything else.

Putting it together

A small React app that displays a live model, captures a 10-second snap, and previews it alongside a download button.
React
"use client";

import { useState } from "react";
import {
  ClipPlayer,
  ClipDownloadButton,
  ReactorProvider,
  ReactorView,
  useReactor,
} from "@reactor-team/js-sdk";
import type { Clip } from "@reactor-team/js-sdk";

export default function App({ token }: { token: string }) {
  return (
    <ReactorProvider
      modelName="your-model-name"
      jwtToken={token}
      connectOptions={{ autoConnect: true }}
    >
      <ReactorView className="w-full aspect-video rounded-xl" />
      <Controls token={token} />
    </ReactorProvider>
  );
}

function Controls({ token }: { token: string }) {
  const { status, reactor } = useReactor((s) => ({
    status: s.status,
    reactor: s.internal.reactor,
  }));
  const [clip, setClip] = useState<Clip | null>(null);

  return (
    <div className="mt-4 flex gap-3 items-center">
      <button
        disabled={status !== "ready"}
        onClick={async () => setClip(await reactor.requestClip(10))}
      >
        Snap last 10s
      </button>

      {clip && (
        <div className="fixed inset-0 bg-black/80 flex items-center justify-center">
          <div className="bg-neutral-900 p-4 rounded-xl w-[640px]">
            <ClipPlayer clip={clip} getJwt={() => token} />
            <div className="mt-3 flex justify-end gap-2">
              <ClipDownloadButton clip={clip} getJwt={() => token} />
              <button onClick={() => setClip(null)}>Close</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

Error handling

Any recording-specific failure throws a RecordingError. The React components surface it inline already, so you only need to catch it when you call requestClip or requestRecording directly.
JavaScript
import { RecordingError } from "@reactor-team/js-sdk";

try {
  const clip = await reactor.requestClip(10);
} catch (err) {
  if (err instanceof RecordingError) {
    console.error(err.code, err.reason);
  }
}
See RecordingError for the full list of codes.