---
title: "Tatolab docs"
description: "What Tatolab is, the problem it solves, and where to start."
type: "docs-page"
url: "https://tatolab.com/docs"
site: "Tatolab"
section: "Docs"
---

# Tatolab

Build live streams and share what they produce with other streams, tools and agents.

[Start the quickstart](https://tatolab.com/docs/quickstart) · [Install Tatolab](https://tatolab.com/docs/install)

## The problem

Live data tends to stay inside the program that produces it. A camera feed, a microphone, a sensor, or the output of a model usually belongs to the one process that opened it. Everything else that needs it, such as a recorder, a dashboard, a teammate's script or an agent, needs capture code of its own, a way to move the data, a format both sides agree on, and a way to find out why it stopped.

Tatolab gives live data one shape and one home on each machine:

- **A stream** is a function that connects **nodes**: sources, steps that transform data, and consumers. Data moves between them as **bags** of named values.
- **One runtime per machine**, `tatolabd`, runs every stream on that machine, from any project.
- **Every output has an address**, `<stream>/<node>/<port>`. Expose one, and other streams on the machine can read it. The stream that produces it doesn't change, and doesn't need to know who's reading. An exposed output also gets a URL, served over MoQ by a relay the runtime runs on the machine.
- **You can see inside while it runs**: the live graph, a sample of what any connected output carries, and a JSON Lines log for every stream.

## Two streams, sharing

Here, one stream opens a camera and reports motion. A second stream, in another project, reads those reports and writes them to a file:

```python title="motion-watcher/stream.py"
from tatolab.stream import CameraSource, StreamBuilder, stream

from nodes.motion_detector import MotionDetector


@stream
def motion_watcher(stream_builder: StreamBuilder) -> None:
    """Watches the camera for motion, and shares the camera and the reports."""
    camera = stream_builder.add(CameraSource, name="camera")
    detector = stream_builder.add(MotionDetector, name="detector")
    stream_builder.connect(camera.output("video"), detector.input("video"))
    stream_builder.expose(camera.output("video"))
    stream_builder.expose(detector.output("motion"))
```

```python title="motion-log/stream.py"
from tatolab.stream import StreamBuilder, stream

from nodes.motion_event_log import MotionEventLog


@stream
def motion_log(stream_builder: StreamBuilder) -> None:
    """Writes each start and stop of motion to motion-events.jsonl."""
    event_log = stream_builder.add(MotionEventLog)
    stream_builder.connect(
        stream_builder.remote_output("motion_watcher/detector/motion"),
        event_log.input("motion"),
    )
```

The camera is opened once. Each node you write runs in its own process, so a slow or failing reader can't stall the stream it reads from. Either stream can be stopped, changed and started again without touching the other. The [tutorial](https://tatolab.com/docs/tutorial) builds both, node by node.

![motion_watcher exposes its motion reports, and motion_log, in another project, reads them by their address.](https://tatolab.com/media/docs/share-light.jpg)

## What Tatolab takes care of

| You'd otherwise write | Tatolab |
|---|---|
| A way for a second program to get the same live data | Expose an output, and any other stream on the machine reads it by its address. |
| Isolation, so one bad consumer can't take everything down | Every node you write runs in its own process, with your project's packages. A reader that falls behind drops data at its own input, and the drop is counted. |
| Logging and a way to see what's happening | `tatolab graph` shows every node, link and counter; `tatolab tap` samples a port; each stream writes a JSON Lines log. |
| A way for an agent to work with it | The runtime serves MCP. An agent can read the graph, sample ports, look at frames, read logs, and add or connect nodes. Computer-use agents such as Grok Bot and Muse also open an exposed output by its URL. See [Agents and MCP](https://tatolab.com/docs/share/agents). |
| The fiddly media parts | Built-in camera, microphone, speaker, window, hardware H.264 and H.265, Opus and MP4 recording. Frames stay in GPU memory and reach PyTorch without a trip through the CPU, for the streams that need it. |

## At a glance

- **Platforms.** Linux on x86_64 with a Vulkan GPU (we test on NVIDIA), and Apple silicon Macs with macOS 15 or later. The runtime needs a GPU. Windows isn't supported.
- **SDKs.** Python, with `tatolab-stream` from PyPI. See [Python SDK](https://tatolab.com/docs/install/python).
- **Installing.** One command: Homebrew on a Mac, `curl` on Linux. See [Install](https://tatolab.com/docs/install).
- **Sharing on one machine.** Streams read each other's exposed outputs through shared memory, and agents on the machine work with every stream.
- **URLs for every exposed output.** The runtime runs a MoQ (Media over QUIC) relay on each machine, and the relay serves each exposed output at a URL: tools on the machine read private outputs, your other machines read public ones over your tailnet, and a relay on the internet that the machine joins carries them further. See [Across machines](https://tatolab.com/docs/share/across-machines).
- **Screen capture.** A screen, or a window such as a simulator or a browser, becomes a stream you can share like any other.

## Where to start

- **New to Tatolab?** [Install it](https://tatolab.com/docs/install), then follow the [quickstart](https://tatolab.com/docs/quickstart): a running stream in about five minutes, once Tatolab is installed.
- **Want the whole picture?** [How Tatolab works](https://tatolab.com/docs/how-it-works) explains streams, nodes, exposure and the runtime.
- **Ready to build?** The [tutorial](https://tatolab.com/docs/tutorial) builds a stream, shares its output with a second one, and hands both to an agent.
- **Have a specific job?** [Share](https://tatolab.com/docs/share/between-streams) covers exposing outputs and agents. **Build** covers [nodes](https://tatolab.com/docs/build/nodes), [audio](https://tatolab.com/docs/build/audio), [recording](https://tatolab.com/docs/build/recording), [GPU effects](https://tatolab.com/docs/build/gpu-effects) and [models](https://tatolab.com/docs/build/models). **Run** covers [keeping streams running](https://tatolab.com/docs/run/manage), [inspecting them](https://tatolab.com/docs/run/inspect), [logs](https://tatolab.com/docs/run/logs) and [deploying to a machine](https://tatolab.com/docs/run/deploy).
- **Wondering if it fits your problem?** Read the [use cases](https://tatolab.com/docs/use-cases), or browse the [examples](https://tatolab.com/docs/examples).

## Get help

- Found a bug or a confusing page? Open an issue on [GitHub](https://github.com/tatolab/streamlib/issues), or email [hello@tatolab.com](mailto:hello@tatolab.com).
- Using an AI assistant? Every page here is also Markdown: add `.md` to the address, or point your assistant at [/llms.txt](https://tatolab.com/llms.txt).
