Skip to content

Flows


In the previous page, we learned that a voice is a message that carries information between nodes during playback. But how does the voice know which nodes to visit, and in what order? That's what flows are for.

A flow is a pipeline of nodes that a voice travels through, one stage at a time. When a note is pressed, the framework creates a voice and sends it into a flow. The voice visits each node in the flow's sequence, and each node gets a chance to read the voice, modify it, or act on it.

Think of it like a conveyor belt: the voice is the object on the belt, the flow defines the belt's path, and each node is a station along the way.

Declaring Flows


Flows are declared inside a special callback called cb Flows, which runs during FirstLoad (the very first time the instrument loads). This means flows are set up once and remain in place for the lifetime of the instrument.

Here's the basic pattern:

node MyPlaybackLogic:
    cb Flows:
        define FLOWS += my_flow

        ivls.register_node(my_flow, MyModifier)
        ivls.register_node(my_flow, MyPlayEvent)
end node

Let's break this down:

define FLOWS += registers the names of your flows. This is similar to how IVLS_NODES registers node names -- it tells the framework that these flow variables exist. Each flow name gets assigned an index automatically.

ivls.register_node() adds a node to the flow's pipeline. Nodes are visited in the order you register them. In the example above, a voice entering my_flow would visit MyModifier first, then MyPlayEvent.

If you need to register multiple flow names, separate them with , ...:

define FLOWS += my_first_flow, ...
                my_second_flow

A Minimal Flow


The simplest useful flow has just one or two nodes and ends with a PlayEvent -- the node that actually triggers a sound in Kontakt. Here's what that looks like:

node MyPlaybackLogic:
    cb Flows:
        define FLOWS += my_flow

        ivls.register_node(my_flow, MyPlayEvent)
end node

This is the shortest possible flow: a voice enters, and MyPlayEvent fires the sound immediately. In practice, you'll almost always have at least one node before PlayEvent that modifies the voice in some way -- adjusting its note, velocity, or choosing which sample groups to play. We'll cover PlayEvent in detail on the next page.

A slightly more realistic flow might look like this:

node MyPlaybackLogic:
    cb Flows:
        define FLOWS += my_flow

        ivls.register_node(my_flow, MyNoteModifier)
        ivls.register_node(my_flow, Stl.Pedals)
        ivls.register_node(my_flow, MyPlayEvent)
end node

Here, the voice first passes through MyNoteModifier (which might transpose the note or adjust the velocity), then through Stl.Pedals (which handles sustain pedal behavior), and finally arrives at MyPlayEvent to produce sound.

Flow Naming Conventions


In production instruments, flow names typically use a prefix that identifies the product, followed by a descriptive name. For example:

define FLOWS += tky.flows.start, ...
                tky.flows.play_sound

define FLOWS += phr.play_flow, ...
                phr.select_flow

This prevents name collisions and makes it clear which product each flow belongs to.

register_move


In addition to ivls.register_node(), you can use ivls.register_move() to redirect a voice from one flow into another. When a voice reaches a move entry, it automatically continues into the target flow:

ivls.register_move(my_flow, my_other_flow)

This is useful for composing flows out of shared sub-flows, so you don't have to duplicate the same sequence of nodes in multiple places. We'll see more advanced flow routing patterns in a later page.


At this point you understand the basic shape of IVLS playback: voices travel through flows, visiting nodes in sequence. The next step is learning how to build a PlayEvent node that actually produces sound.