Tau
In quantum physics, τ (tau) denotes the tangle, a measure of entanglement. For three or more parts it is what is left once every pairwise correlation has been accounted for: a property of the whole system, belonging to no pair within it.
Tau is a Python runtime for distributed multi-projector rendering in the AlloSphere. State replicates over UDP, parameters sync over OSC, and each projection gets its own warp, blend, and quad-buffer stereo output. It has no dependency on allolib.
Content can be an app class, functions registered on a runtime, a loop you own, or a plain object that the renderers draw. All four run in a window on one machine and run unchanged across the AlloSphere's cluster.
The import package is tau. The PyPI name is tau-av.
Install
Use Tau from a clone. The examples, the tests, and the deploy scripts live in the repository tree.
git clone https://github.com/kr4g/Tau.git
cd Tau
python3 -m venv .venv
.venv/bin/python -m pip install -e .
To use it as a dependency in a tree of your own, install tau-av.
python -m tau.preflight checks a machine: Python version, GL context,
shader compilation, numba JIT, calibration, ports, UDP send and receive.
Run it after install, and on any machine before it joins a cluster.
Run
.venv/bin/python -m tau.launcher
The launcher lists the apps under examples/ and apps/ and runs the
selection as a subprocess. When node agents run (see "Running in the
AlloSphere"), a launch also switches what the cluster shows.
A single app runs directly:
.venv/bin/python -m examples.boids.app --view equirect
The views are pov (perspective, the default), cross (a cubemap box),
equirect (a panorama), and anaglyph (a red/cyan stereo preview). The
v key cycles them while the app runs. Arrow keys look around and WASD
moves. --help lists the other controls and flags.
A second instance on the same network elects as a replica and follows the first. Every start prints a role banner: host, role, broadcast target, renderer, calibration.
Writing content
Apps you write go in apps/. Code they import goes in ext/. Both ship
empty and git ignores their contents, so a pull never touches them. An app
in apps/<name>/ runs as python -m apps.<name>.app, and the launcher
discovers it there.
An app class
Subclass DistributedApp and override hooks. A numpy dtype declared as
state_type becomes a replicated block: the primary writes it, and every
renderer reads it.
class Cloud(tau.DistributedApp):
state_type = np.dtype([("pos", np.float32, (256, 3))])
sync_nav = True # replicas follow the primary camera
def on_animate(self, dt):
if self.is_primary():
self.state()["pos"] += drift(dt) # simulate on one machine
def on_draw(self, g):
... # draw on every machine
The other hooks are on_init, on_create (GL is live), on_gui(panel),
and on_keys(keys). A registered Parameter syncs over OSC and appears in
the control panel. Every example has this form: a model module in
vectorized numpy, a state.py with the dtype, and an app.py whose
main() calls tau.run.
Registered functions
A runtime from tau.runtime() takes its hooks as functions:
rt = tau.runtime(state_type=my_dtype)
@rt.animate
def animate(dt):
...
@rt.draw
def draw(g):
...
rt.run()
A second registration replaces the first, even while the loop runs. The loop and the network connections do not stop when the content changes. With no draw hook the output is black, so a session can start empty and get content later. Live coding works this way.
Your own loop
rt.run() is a plain loop. A program with its own loop calls the pieces
instead:
rt.open() # election, domains, window
while running:
rt.step(dt) # simulate and replicate
rt.poll(dt) # domain upkeep
rt.render() # draw one frame
rt.shutdown()
This form suits a notebook, or a larger program that uses Tau as a library.
A bare scene
The renderers call three methods on the object they draw: nav(),
lens(), and on_draw(g). tau.Scene names this protocol. Any object
with the three methods renders through the capture, warp/blend, and stereo
pipeline. Content that was not written against Tau enters here: wrap it in
the three methods, or give its per-frame arrays to the retained primitives
(instanced meshes, points, lines, ribbons). State replication and
parameters work beside either route.
The upload rule
on_animate(dt) runs once per frame. on_draw(g) runs once per projector
per eye, so it must only issue draw calls. Build and upload geometry in
on_animate. Content that uploads in on_draw looks correct in a window
and renders differently on each projector. The checker below catches this.
Working with a coding agent
AGENTS.md carries the conventions and the reasons for them,
written for an agent as much as for a person. An agent that works in a
clone reads it from the repository root without being asked. The checker's
--json output is for an authoring loop.
Stereo 3D
A calibrated renderer captures both eyes and presents them through a
quad-buffer framebuffer. If the driver has no stereo framebuffer, the
renderer warns and runs mono. --mono disables stereo.
The lens sets the depth. lens().focal_length(v) places the convergence
distance. Content at that distance sits at the screen surface. Nearer
content floats inside the sphere, and farther content recedes.
lens().eye_sep(v) scales the disparity. Each example places its
convergence where its content lives.
The /tau/stereo parameter is a checkbox in the control panel. It switches
the cluster between stereo and mono while the app runs, and mono also
halves the capture cost. A vertex shader gets the displacement when it
calls stereo_displace(...). The runtime inserts the correct variant for
the render path at compile time, and g.apply_stereo(prog) sets the
uniforms.
At home, the anaglyph view shows the same disparity through red/cyan
glasses. examples.calibration draws a depth ladder dead ahead. The
graticule sits at the convergence distance. An orange ring at half that
distance must float inside the sphere, and a violet ring at twice it must
sit beyond. Flat rings mean stereo is dead. Swapped depths mean crossed
eyes.
Control panel
On the simulator, every app gets a second window: a view selector, a
widget for each registered parameter, the stereo toggle, the pattern
selector, and the cluster roster. Override on_gui(panel) to add custom
UI. Render nodes never open one, and --no-gui disables it.
Shaders
The renderer supplies tau_ModelViewMatrix, tau_ProjectionMatrix, and
tau_ViewMatrix. A shader declares whichever it uses. Shaders loaded
through ShaderManager reload on file change while the app runs.
Checking an app
.venv/bin/python -m tau.check apps.myapp # or examples.boids, or a bare name
This runs the gates that run at home: shader compilation at the
#version 410 ceiling, draw purity, uploads misplaced in on_draw,
stereo, state size and wire rate, and headless determinism. The checker
phrases each failure as the change that fixes it. --json emits the
report for tooling. The app supplies one thing, a smoke() function in its app.py:
def smoke():
return MyApp(n=64, seed=1, headless=True, fps=0.0)
The test suite (python -m pytest) checks the core the same way. It
includes purity for every example, a pixel-for-pixel warp oracle, a
two-process rehearsal of election and transport, and the stereo gates.
bash scripts/test-py310.sh repeats the suite on the renderers'
interpreter. python -m tests.bench_sphere reports timing costs, and
--save / --compare bracket a change. Subnet broadcast, driver
differences, the spanned X screen, and quad-buffer presentation are
checked on site.
No check covers whether content reads from inside the AlloSphere.
Content can pass everything and still be composed for a rectangle. Look at
it in --view equirect and --view pov.
Running in the AlloSphere
Stage the work on the shared /alloshare mount. Build the venv there
once, from a renderer: bash deploy/build_venv.sh. Then start the same
app on every machine by hand:
.venv/bin/python -m examples.<name>.app # identical on every machine
or start the node agents once and switch content from the simulator:
bash deploy/launch_sphere.sh start
.venv/bin/python -m tau.launcher
Role and renderer resolve from the hostname and the calibration data. The
primary host (ar01, or TAU_PRIMARY_HOST) simulates and sends state. A
renderer with a calibration manifest applies warp, blend, and stereo, and
runs fullscreen. --sim makes any machine the primary.
State ships as full snapshots over UDP, latest-wins. Point TAU_CACHE_DIR
at a shared path and every node loads one copy of precomputed data.
Every node sends a heartbeat once a second. The control panel shows the
roster, and python -m tau.heartbeat prints the same table. Two
simultaneous primaries appear on both.
The /tau/pattern parameter (control panel, or keys 0–5 in
examples.calibration) switches every renderer into a
projector-identification pattern without stopping the content. 0 returns
to normal. Degraded states, such as missing calibration or missing state,
appear on the output itself.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tau_av-0.2.0.tar.gz.
File metadata
- Download URL: tau_av-0.2.0.tar.gz
- Upload date:
- Size: 268.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c1b071ab3db481ee93ef0f9f8f1ec0c2921f12b4da46a1d53f572a53137bb1a
|
|
| MD5 |
fb4ac8f65bb749d5901b749daa7df60b
|
|
| BLAKE2b-256 |
8738e7b5e0b12cd9332687b5353c91b7c214c3d0ebee685672b38024c8bff3f8
|
File details
Details for the file tau_av-0.2.0-py3-none-any.whl.
File metadata
- Download URL: tau_av-0.2.0-py3-none-any.whl
- Upload date:
- Size: 134.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.14.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91244b30f936466ee4b3eae35675d274cfbeaab653a3327834ab5fc60275e473
|
|
| MD5 |
36fa5769440040674a3ff1675a5ca09e
|
|
| BLAKE2b-256 |
e9b67274e6f927ad21c697a60342ecd75e6c628d19fe2a177c44005a2828c09a
|