Thanks @Kartik Agaram I’ve looked at FRP in the past, in relation to Tidal Cycles which was heavily influenced by it, IIRC. I’ll take another look.
In my current node implementation (for the demos you've seen before), I have 2 distinct types of 'pin'
• Simple data: (bool, int, double, etc.) Currently these are always 'inputs'; but I don't technically have a limitation on them being outputs too.
• Flow Data - a generic representation of data flowing around the graph.
Flow data is effectively vectors of simple data types. For example, 2 audio waves would be represented as:
â—¦ [float, float, float....]
â—¦ [float, float, float...]
In my graph, this means that playing a 3-note chord on an input instrument (for the audio case), may well result in several types of flow data running around the graph, typically containing 3 arrays of data, each array being a single 'frame' of audio; say 512 samples. Important point - this is output/input on a single 'flow' pin, and contains 3 unique 'channels' of 512 float samples:
FlowData
{
map<int, vector<float>> channels;
}
It is up to the application to determine what 'Flow' data actually means; but the important point is that the graph is evaluated based on finding the output flow and walking back into the graph to satisfy the data flow through it; by finding dependencies and walking back through the graph to fulfil them before computing each node.
The tricky part is something like an 'Adder' node. Suppose it wants to take in 2 numbers and produce a result. In my system, currently, that means that a node outputting a number must convert it to a 1-dimension flow data to output it. Then the Adder node can read the flow data and extract the number. This means that you could effectively add 2 arrays of numbers in one compute step in the adder; or multiple entries. You could also, in theory route 2 seperate flow sources to the same target and multiplex them on arrival.
But I'm still thinking about if it is appropriate to enforce this transform of scalar values to flow data when outputting from a node, and how this feels to the user. There is a certain uniformity I like if all data flowing through the graph is represented the same way; but then what if you output from a node 3 streams of numbers in the flow data, but the user hooks that up to a single input number in a node. Which to pick? Or is that just an illegal operation? Is a single-entry flow data valid, but a multi entry not? Essentially the implicit transforms from simple to flow look like:
[Scalar -> FlowData[Scalar Array, size 1]] Example: output=1.0 input=vector<float>{1.0}
[FlowData[..] -> Scalar] (only if flow data is dimension 1?) Example: output=vector<float>{1.0} input=1.0
[FlowData, FlowData -> FlowData] Example: input=2 * flow data, output=muxed flow data.
There are technical reasons why I like this design, and flexibility reasons; it accomodates the parallel data flow model which works well for audio/graphics. It is easy to evaluate the graph, and for the user to reason the direction of evaluation. What it gives up is the simple ability to wire simple scalar values to simple scalar values directly; that operation is allowed by under the covers it is effectively a conversion.
In the UI/demo I am trying to hint at this by having flow data pins sit on the outside of nodes, and conversion from flow data to number be shown as an arrow inside the node next to the number in question. As well as having arrows for flow data direction.