The player is the one tile that both reads and publishes, so it has two bindings: it shows a `record` describing what is playing — title, artist, album, status, and position and duration in seconds — and publishes transport words back to one `str` message (`toggle`, `next`, `prev`, `seek:<seconds>`). Those are a streamer's own vocabulary rather than this app's, which is what lets one tile drive whatever is on the other end. The position counts forward in the browser between readings, so the bar moves at one second while the device behind it is polled at whatever rate suits it; every reading that arrives is taken as the truth and the count restarts there. That is also why this is one record rather than five messages — a tile drawn from five would redraw itself five times, and show a new title against the old duration in between. Being both is why `INPUT_WIDGETS` does not gain it: what that set means is "the message this widget publishes is its only binding", which is exactly what a player is not. Its reading is checked the usual way and its `target` separately. The fader beside it needed nothing new. `ui/core` has had `orientation` on the slider all along and all three looks draw it; only the widget never passed it, so a volume control — the one thing reached for without looking, where up is louder — could not be a column. Now it can, and the tile's height is the track.
Fluksio
Fluksio is a node-based automation software that brings trust and reliability to your flow. It just works and looks good. Get started by running
pip install fluksio
fluksio serve
and you're ready to go.
For data science
You can turn your existing data science project into a flow by decorating your functions with @node ...
# myresearch/train.py
import fluksio
from fluksio import Port, node
@node(
requires=["dataset", Port("lr", "float")],
provides=[Port("loss", "float", stream=True), Port("weights", "artifact")],
device="gpu", device_policy="prefer",
)
def fit(dataset, lr, epochs=25):
for epoch in range(epochs):
loss = step(...)
yield {"loss": loss} # published as it happens, kept as a series
return {"weights": fluksio.save_artifact("weights.pt")}
... and passing them to a Flow:
# myresearch/pipeline.py
from fluksio import Flow, Port
from myresearch.data import prepare
from myresearch.evaluate import evaluate
from myresearch.train import fit
train = Flow("train", nodes=[prepare, fit, evaluate],
inputs=[Port("lr", "float", initial=0.01)], outputs=["score"])
Fluksio will automatically infer the order of nodes based on the inputs and outputs you defined. When everything is set, you can launch your first run as follows:
fluksio run train --lr 0.05 --wait
Checkout our documentation for more infos.
Some other features
- Flows: typed messages between nodes, wired by name, edited on a canvas or declared in code. Every change is a commit in a git repository you own.
- Runs: an experiment and a CI-style job are the same entity. Parameters, seed, result, per-node timings, artifacts and the commit it ran at.
- Dashboards: charts and controls bound to the same messages the flows carry, with no separate metrics pipeline.
- Remote workers:
pip install fluksio-workeron the GPU box; it dials out over one websocket, so nothing there has to be reachable.
Fluksio can also be used for facility automation. Visit us on fluksio.com or go straight to our documentation.
License
Copyright (C) 2026 Melvin Strobl - GNU Affero General Public License v3.0 or later. Running a modified version over a network obliges you to offer its users the corresponding source (AGPL §13).