Skip to content

Tatolab

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

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.

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

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"))
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 builds both, node by node.

motion_watcher exposes its motion reports, and motion_log, in another project, reads them by their address.
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.
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.
  • 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.
  • Installing. One command: Homebrew on a Mac, curl on Linux. See 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.
  • Screen capture. A screen, or a window such as a simulator or a browser, becomes a stream you can share like any other.
  • Found a bug or a confusing page? Open an issue on GitHub, or email 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.