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

# Image Input

> Capture video frames on demand and send them to the LLM with a prompt.

For a snapshot-style "look at this" interaction, ask for frames on the reply itself:
`session.reply()` takes a `frames` count and the runtime shows the model that many of the
newest camera frames alongside your prompt. The count travels, not the pixels. This works in
[Cascade](/build/configure-a-pipeline/modes) pipelines and is the simplest way to add vision.

## Capture and send

<CodeGroup>
  ```python title="Python" Python theme={null}
  # Show the model the two most recent frames and ask it to describe them
  await session.reply(
      "Describe what you see in this frame in one sentence.",
      frames=2,
  )
  ```

  ```typescript title="Node JS" Node JS theme={null}
  // Show the model the two most recent frames and ask it to describe them
  await session.reply('Describe what you see in this frame in one sentence.', {
    frames: 2,
  });
  ```
</CodeGroup>

`frames` is a count, not a list of images -- at most `Session.MAX_FRAMES` (5). A negative
count or one above the maximum raises `ValueError`. Capturing needs `Room(vision=True)`;
without it there is no video track to capture from.

## Trigger a capture

A common pattern is to capture when the client sends a [pub/sub](/build/modalities/text/chat)
message, for example, a "capture" button in your app:

<CodeGroup>
  ```python title="Python" Python theme={null}
  from zeroruntime import Agent, PubSubSubscribeConfig, Room

  class VisionAgent(Agent):
      async def on_enter(self) -> None:
          await self.session.subscribe_to_pubsub(
              PubSubSubscribeConfig(topic="CHAT", cb=self.on_chat)
          )

      async def on_chat(self, frame: dict, backlog: bool) -> None:
          if backlog or frame.get("message") != "capture_frames":
              return
          await self.session.reply(
              "Analyze this frame and describe what you see.", frames=2
          )

  # Turn the video track on; the topic is subscribed once the session exists.
  zeroruntime.invoke(AGENT_ID, room=Room(vision=True))
  ```

  ```typescript title="Node JS" Node JS theme={null}
  import * as zeroruntime from '@zeroruntime/js-sdk';
  import { Agent, PubSubSubscribeConfig, Room } from '@zeroruntime/js-sdk';

  class VisionAgent extends Agent {
    async on_enter(): Promise<void> {
      await this.session!.subscribe_to_pubsub(
        PubSubSubscribeConfig({ topic: 'CHAT', cb: this.on_chat.bind(this) }),
      );
    }

    async on_chat(frame: Record<string, any>, backlog: boolean): Promise<void> {
      if (backlog || frame.message !== 'capture_frames') {
        return;
      }
      await this.session!.reply('Analyze this frame and describe what you see.', { frames: 2 });
    }
  }

  // Turn the video track on; the topic is subscribed once the session exists.
  zeroruntime.invoke(AGENT_ID, { room: Room({ vision: true }) });
  ```
</CodeGroup>

You can also build `ImageContent` directly from PIL images, NumPy arrays, or `av.VideoFrame`s
for fully custom flows.

## Encoding

Before frames reach the model they're encoded with `EncodeOptions`: JPEG by default, resized (default `1024×1024`) and compressed (`quality=75`). Raise these when you need higher fidelity
(for example, to read fine text on camera) at the cost of more tokens and latency.

## References

<Tabs>
  <Tab title="Python">
    #### Examples

    <CardGroup cols={2}>
      <Card title="Vision (Cascade)" icon="github" href="https://github.com/ZeroRuntimeAI/zeroruntime-python-examples/blob/main/vision/vision_cascade.py">
        Send images to a cascade agent.
      </Card>
    </CardGroup>
  </Tab>

  <Tab title="Node JS">
    #### Examples

    <CardGroup cols={2}>
      <Card title="Vision (Cascade)" icon="github" href="https://github.com/ZeroRuntimeAI/zeroruntime-js-examples/blob/main/vision/vision_cascade.ts">
        Send images to a cascade agent.
      </Card>
    </CardGroup>
  </Tab>
</Tabs>
