> ## 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.

# Tracks

> How named media tracks work in Reactor

## What is a track?

A **track** is a named media stream between your app and the model. Tracks carry video, audio, or
any other data the model expects or produces. Every track has a name that both sides use to identify
it.

***

## Server-driven track configuration

Track configuration is **server-driven**. Each model declares the tracks it exposes in its
**schema**, which is the same schema documented on each model's reference page. Your app does not
need to declare tracks up front; the SDK reads the schema and sets up the WebRTC connection
accordingly.

For the authoritative list of tracks a model exposes, see its Schema page in the
[Model API Reference](/model-api-reference/overview). For example,
[Helios](/model-api-reference/helios/schema) or [LingBot](/model-api-reference/lingbot/schema).

Every track has a `name`, a `kind` (`"video"` or `"audio"`), and a `direction`:

* **`recvonly`** tracks are outputs from the model to your app (e.g. generated video).
* **`sendonly`** tracks are inputs from your app to the model (e.g. webcam feed).

A model's schema lists its tracks like this:

```json Schema (tracks) theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
{
  "tracks": [
    { "name": "webcam",     "kind": "video", "direction": "sendonly" },
    { "name": "mic",        "kind": "audio", "direction": "sendonly" },
    { "name": "main_video", "kind": "video", "direction": "recvonly" },
    { "name": "main_audio", "kind": "audio", "direction": "recvonly" }
  ]
}
```

***

## Output tracks (model to app)

The SDK receives the model's output tracks automatically. Read them by name once the track
arrives.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  reactor.on("trackReceived", (name, track) => {
    if (name === "main_video") {
      const video = document.querySelector("video");
      if (video) video.srcObject = new MediaStream([track]);
    }
  });
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  import { ReactorProvider, ReactorView } from "@reactor-team/js-sdk";

  <ReactorProvider modelName="your-model-name" jwtToken={token}>
    <ReactorView className="w-full aspect-video" />
  </ReactorProvider>
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  @reactor.on_track("main_video")
  def on_main_video(track):
      print("Received output track:", track)

  # Or read the current set of received tracks at any time:
  tracks = reactor.get_remote_tracks()
  ```
</CodeGroup>

If the model exposes multiple output tracks (e.g. a depth map or audio), they are all received automatically.

The model emits frames to its output tracks the whole time the session runs; pausing and resuming only switches whether your client receives them, like subscribing and unsubscribing. By default every output is subscribed as soon as the connection is `ready`. Connect with `autoResumeTracks: false` to start unsubscribed, then call `resumeTrack(name)` to begin receiving a track and `pauseTrack(name)` to stop. See [`connect()`](/sdk-reference/reactor-class#connect).

***

## Input tracks (app to model)

Some models accept input tracks (e.g. a webcam feed for video-to-video transformation). Input tracks are **explicit**: the model receives nothing on a `sendonly` track until your app publishes a media track to it. Connecting alone never starts sending, so publish once the connection reaches `ready`. The JavaScript variant captures the webcam with `getUserMedia`; in React the `WebcamStream` component captures and publishes for you.

<CodeGroup>
  ```typescript JavaScript theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  reactor.on("statusChanged", async (status) => {
    if (status === "ready") {
      const stream = await navigator.mediaDevices.getUserMedia({ video: true });
      await reactor.publishTrack("webcam", stream.getVideoTracks()[0]);
    }
  });
  ```

  ```tsx React theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
  import { WebcamStream } from "@reactor-team/js-sdk";

  <WebcamStream track="webcam" className="w-48 aspect-video" />
  ```

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

  @reactor.on_status(ReactorStatus.READY)
  async def on_ready(status):
      # track is an aiortc MediaStreamTrack
      await reactor.publish_track("webcam", track)
  ```
</CodeGroup>

Call `unpublishTrack(name)` (or `unpublish_track(name)` in Python) to stop sending a track.
Typically you do this on disconnect or when the source media stops.

<Note>
  Only one connection may publish a given input track at a time. In a session shared by multiple
  connections, publishing a track that another connection already holds is rejected until that
  connection unpublishes it.
</Note>

***

## Naming convention

Here's how track names map between the model and your app:

<Frame>
  <img src="https://mintcdn.com/reactortechnologiesinc/oCeFlbp0H3XO2l0l/diagrams/tracks.svg?fit=max&auto=format&n=oCeFlbp0H3XO2l0l&q=85&s=db1cf7dd55ba921e0d45584b2efaee86" alt="Track naming: model track names must match app track names" width="680" height="210" data-path="diagrams/tracks.svg" />
</Frame>

For output tracks, the SDK handles naming automatically. For input tracks (e.g. webcam), the track name you publish must match the attribute name defined in the model's Python class.

<Warning>
  If a track is not working, check that the track name you are using matches the model's declared
  track name. See the model's Schema page in the
  [Model API Reference](/model-api-reference/overview) for the authoritative list.
</Warning>

Check the [Model API Reference](/model-api-reference/overview) to see what tracks a model exposes.
