Skip to content

Quickstart

Create a stream, run it, see the output it shares with other streams and agents, and change it while it runs. About five minutes once Tatolab is installed.

You’ll create a project with tatolab new, run its stream, see the output it shares, and change the stream while it runs.

What you'll run: your camera, through an effect that inverts its colors, shared so other streams and agents can use it.

You need three things:

  1. Tatolab installed, with its runtime running. Check with:

    Terminal window
    tatolab streams

    If it prints No streams in this runtime., you’re ready. If it says no runtime is running on this machine, see Install Tatolab. If you built from source, the runtime is tatolabd running in a terminal of its own: keep it running, and use another terminal for the steps below.

  2. uv, the Python package manager. uv --version should print a version. If it doesn’t, see Install uv.

  3. A camera. No camera on this machine? Add --test-pattern in step 1 and the stream uses color bars instead.

Terminal window
tatolab new my-stream
Output
Created a Tatolab stream project in `my-stream`.
Next:
cd my-stream
uv sync
tatolab dev

It wrote a small stream: your camera, through an effect that inverts its colors, exposed so other programs can read it. It also opens a window, so you can see the result while you test, and logs the picture’s brightness once a second:

my-stream/
├── stream.py the stream: which nodes, and how they connect
├── nodes/
│ ├── __init__.py
│ ├── inverting_effect.py inverts the picture's colors
│ └── brightness_meter.py logs the picture's brightness
├── pyproject.toml depends on tatolab-stream and numpy
├── .python-version 3.12
└── .gitignore

tatolab new refuses to write into a directory that already has these files, so it never overwrites your work.

Terminal window
cd my-stream
uv sync

This creates .venv/ inside the project, with Python 3.12, tatolab-stream and numpy. The runtime runs your nodes with this environment’s Python, so a package you add with uv add is available to every node in the project.

Terminal window
tatolab dev

A test window opens with the effect’s output: your camera, colors inverted. The terminal says the stream loaded, then shows its log records, one per line, timed in UTC. Once a second, one reports the picture’s brightness:

Output
tatolab dev: main loaded (4 nodes, project /home/you/my-stream); Ctrl-C stops it
14:03:22.511 [ INFO] [Rk3x9q2m…/main/python] streamlib::polyglot::python — brightness processor_id=P… height=720 mean=118.4 width=1280

The first load can take a few seconds. tatolab dev keeps running and watches your files; leave it running.

Open a second terminal. The stream runs in the machine’s runtime, not inside your first terminal:

Terminal window
tatolab streams
Output
NAME STATE NODES PROJECT
main attached 4 /home/you/my-stream

The stream is called main, after its function in stream.py. Open stream.py and look at its last line:

stream_builder.expose(effect.output("video_to_downstream"))

That exposes the effect’s output, your camera with its colors inverted, at the address main/invertingeffect/video_to_downstream: the stream, the node and the port. Exposing is how a stream shares what it makes:

  • Other streams use it. A stream in any project on this machine reads it with stream_builder.remote_output("main/invertingeffect/video_to_downstream"), and can record it, run a model on it or send it on. main doesn’t change, and doesn’t need to know who’s reading. The tutorial builds a stream that reads another.
  • Agents see it. An agent connected to the runtime can look at what the output carries. Agents and MCP shows how to connect one.
  • Browsers and your other machines get a URL. The runtime’s MoQ relay serves each exposed output at one. See Across machines.

With tatolab dev still running, open nodes/inverting_effect.py. The effect is one small function that runs for every pixel of every frame:

vec4 effect(vec4 source, ivec2 at) {
// Color channels only — inverting alpha would erase the picture.
return vec4(1.0 - source.rgb, source.a);
}

Replace it with a grayscale:

vec4 effect(vec4 source, ivec2 at) {
float gray = dot(source.rgb, vec3(0.299, 0.587, 0.114));
return vec4(vec3(gray), source.a);
}

Save the file. tatolab dev notices and loads the stream again. Each load replaces the whole stream, so the window closes and opens again, now in gray.

If your GLSL doesn’t compile, the effect can’t start: the terminal shows the error, naming the line of your GLSL that failed, and no picture reaches the window until you fix it and save again. An edit that stops the stream loading at all, such as a Python syntax error in stream.py, is different: dev keeps the last working version running until you save one that loads.

Press Ctrl-C in the first terminal. The stream stops and the window closes. The runtime keeps running, ready for the next stream.

  • tatolab dev asked the runtime to load stream.py. The runtime ran your @stream function with the project’s .venv to learn which nodes to create and how to connect them, then started them.
  • The camera and the window are built-in nodes, native code inside the runtime. Your two nodes each ran in their own Python process, started from your .venv, so neither could stall the other or the window.
  • The stream exposed its output by an address. Other streams read it by that address, agents can look at it, and the MoQ relay serves it at a URL.
  • dev watched your files and reloaded the stream when one changed. tatolab run does the same without the watching, and tatolab run -d hands the stream to the runtime to keep running after you close the terminal.
  • How Tatolab works: streams, nodes, exposure and the runtime, in plain terms.
  • Tutorial: write a node of your own, and share its output with a second stream.
  • Write a node: everything a node can declare and do.