Patch a living system.

Brain, body, sound and text, read and answered while they happen. goofi is a node graph for building real-time experiences. Every node owns a thread and decides when to run, so a patch is a set of parts that are all alive at once.

from a fresh clone to a running server
cargo run -p goofi-init   # once per clone
cargo run                 # builds the app if stale, prints the URL
LslIn py
out
Psd rs
input
out
EegPowerBands py
psd
power
SignalIn rs
input
out
AudioOut rs
input

Five nodes goofi ships, turning a live EEG stream into sound: the stream, its spectrum, its band powers, and the audio those powers drive. The cable into SignalIn is where the patch crosses from the signal plane to the audio one.

Four engines, one graph.

What goes into a goofi patch, which engine takes it, and what comes outLive input on the left, each coloured by the engine it binds to: EEG and physiology and sensor streams to the signal engine, audio and MIDI to the audio engine, camera and video to the graphics engine. The four engines sit together in the centre, the fourth still to come. Output on the right, each coloured by the engine that makes it: generative visuals from graphics, sound and music from audio, measures and text from signal, devices and motion from the fourth engine.EEG & physiologySensors & streamsAudio & MIDICamera & videoGenerative visualsSound & musicMeasures & textDevices & motionINPUTSOUTPUTSsignalaudiographicsto comegoofi

A node runs on an engine, and its colour says which. They share one graph and one node interface, so a cable crosses between them: a band power off the signal engine can set a filter cutoff on the audio one.

Signal signal:

Frames of array data at whatever rate the source runs. LSL, MIDI and OSC in and out, spectra and band powers, complexity measures, and the arithmetic to select, reshape, combine and route whatever comes out of them.

50 nodes
Audio audio:

The same node interface at audio rate. Live in and out, MIDI, oscillators, filters, envelopes and feedback, plus any VST3 plugin on the machine hosted as a node.

17 nodes
Graphics

Frames of pixels, for camera in and generated visuals out. The viewers already draw bitmaps; the engine that fills them is the next one to land.

to come
A fourth

Reserved, and not yet named. Its colour is chosen so a patch can already tell it apart from the three that exist.

to come

What travels on the cable is a second axis: four data types, ARRAY, AUDIO, STRING and TABLE, each with its own ink on a node's pins. 6 viewers draw them, line, image, trajectory, topomap, string, table, and each binds to one output slot and redraws as frames arrive.

A cyber-ecosystem.

There is no global tick. Every node owns a thread and schedules itself, and frames travel node to node over shared memory, so a kHz EEG stream and an audio chain sit in the same patch without either holding the other up.

A node you add is another inhabitant: it reads what reaches it and decides for itself how often to run. Several people can work inside one patch at the same time, and undo stays each person's own.

One programmatic interface

An agent can build your patch.

Everything goofi can do, it does through one vocabulary of 57 ops. The editor, the CLI, a test and an agent are all transports over that one entry point, and anything the editor can do that the ops cannot is treated as a bug.

/mcp exposes that same vocabulary to an agent over a different wire. Describe the patch you want, let the agent build it, then take the mouse back.

The full op vocabulary →

the same ops, from a shell
$ goofi node add signal:Psd
$ goofi link add lslin.out psd.data
$ goofi layout panel edit p2 --type viewer
Four transports over one op vocabularyThe editor, the command line, an agent over the MCP endpoint and a test all reach goofi through the same entry point: one vocabulary of 57 ops. Past that door the manager applies the change to the patch, and the resulting delta is broadcast back to every client on one return path.The editorThe CLIAn agentA testa browser tabgoofi node add …over /mcpthe same ops57 OPS, ONE VOCABULARYthe managerone owner of the documentthe patchthe delta, broadcast back to every client

Extending it

One file is a node.

Drop a Python or Rust file in a folder and it is in the palette. Where it runs is decided by a probe that imports it and picks the tier its imports allow. goofi brings its own Python interpreters, and a parameter can be a Python expression.

How to write a node →

nodes_signal/smooth.py
import goofi
import numpy as np


class Smooth(goofi.Node):
    """Rolling mean over the last axis."""

    INPUTS = {"data": goofi.InputSlot(goofi.DataType.ARRAY, required=True)}
    OUTPUTS = {"out": goofi.DataType.ARRAY}
    PARAMS = {"smoothing": {"window": goofi.IntParam(8, 1, 512, doc="Samples in the mean.")}}

    def process(self, data):
        w = self.params.smoothing.window
        kernel = np.ones(w, dtype=np.float32) / w
        return np.apply_along_axis(lambda v: np.convolve(v, kernel, mode="same"), -1, data.data)

One artifact.

goofi is a single binary with the app compiled into it. It is the server, and it is the CLI client of a running server. It serves the app, prints the URL, and leaves opening a browser to you.

67nodes shipped
57ops in the vocabulary
47+20Rust + Python
6viewer kinds

Every number on this page is read from goofi 3.1.0 describing itself.