What Are Bouncy Links and How Do They Trigger Reaction Chains in Nallely-MIDI
Bouncy links are specialized parameter connections in nallely-midi that automatically re-trigger every other link sharing the same source, creating reaction chains where a single control change cascades through multiple destinations simultaneously.
Bouncy links provide a powerful mechanism for building reactive MIDI device graphs in the dr-schlange/nallely-midi framework. By marking a parameter connection as bouncy, developers enable automatic propagation of changes across all related links without manual rebinding. This architecture simplifies complex modulation scenarios where one source parameter must drive multiple destinations in unison.
Understanding Bouncy Links and Reaction Chains
A bouncy link is a connection between two parameters (source → destination) that, when triggered, automatically invokes every other link that shares the same source path. This creates a reaction chain: a cascading update where a single change on the source parameter propagates through all linked destinations, allowing one control movement to affect many parts of a device graph simultaneously.
Unlike standard static bindings, bouncy links enable dynamic fan-out behavior without requiring explicit individual associations for every destination.
How Bouncy Links Work Under the Hood
The implementation spans four core files in the nallely-midi codebase, each handling a specific aspect of the bouncy link lifecycle.
The Link Class and Bouncy Flag
In nallely/core/links.py, the Link class stores the connection metadata and a boolean bouncy flag initialized in Link.__init__ (lines 48-56). When Link.trigger executes (lines 94-109), it first applies its own callback to update the destination parameter. If self.bouncy is True, it then invokes self.dest.device.bounce_link(self.dest, value, ctx) to initiate the reaction chain.
The bounce_link Method
Both hardware and virtual devices implement the bounce_link method to scan for related connections. In nallely/core/midi_device.py (lines 74-79) and nallely/core/virtual_device.py (lines 58-63), the method accepts the source parameter, value, and context, then iterates through the device's links_registry to find all links sharing the same src_path. Each matching link receives a recursive trigger call, continuing the chain.
The Public API
The TrevorAPI class in nallely/trevor/trevor_api.py provides make_link_bouncy (lines 101-110), a public interface that toggles the bouncy flag on existing links. This allows developers to convert standard parameter associations into bouncy connections at runtime without directly manipulating Link objects.
Step-by-Step Execution Flow
When a bouncy link fires, the following sequence occurs:
- Source change detection →
Link.triggerreceives the new value and context. - Local update → The link's callback updates its immediate destination parameter.
- Bounce invocation → Because
self.bouncyisTrue, the link callsdest.device.bounce_link. - Registry scan →
bounce_linklooks up all links registered with the same source path. - Chain propagation → Each matching link is re-triggered, potentially activating additional bouncy links and continuing the cascade.
Practical Implementation Examples
Creating a Bouncy Link via the API
from nallely.trevor import TrevorAPI
api = TrevorAPI()
# Create a standard link between a synth parameter and a panel indicator
api.associate_parameters(
from_parameter="Synth::filter::cutoff",
to_parameter="Panel::led::filter_indicator"
)
# Convert the link to bouncy to enable reaction chains
api.make_link_bouncy(
from_parameter="Synth::filter::cutoff",
to_parameter="Panel::led::filter_indicator",
bouncy=True
)
When the cutoff knob moves, the LED updates and any other links using Synth::filter::cutoff as their source automatically trigger.
Internal Trigger Flow
The Link.trigger method in nallely/core/links.py implements the bouncy logic:
def trigger(self, value, ctx):
# Apply the link's own callback first
self.callback(value, ctx)
# If bouncy, ask the destination device to rebroadcast
if self.bouncy:
self.dest.device.bounce_link(self.dest, value, ctx)
The device's bounce_link method then handles the cascade:
def bounce_link(self, from_, value, ctx):
src_path = from_.repr()
for (src, _), link in list(self.links_registry.items()):
if src == src_path: # Same source as the original link?
link.trigger(value, ctx) # Re-trigger to continue the chain
Multi-Parameter Reaction Chain Example
Consider three links sharing the modwheel source:
Link A: Synth::modwheel -> ModDepth (bouncy)
Link B: Synth::modwheel -> LFO::rate
Link C: Synth::modwheel -> Env::attack
When the modwheel changes:
- Link A updates
ModDepthand callsbounce_linkbecause it is bouncy. - The device finds Link B and Link C sharing the same
Synth::modwheelsource. - Both links trigger simultaneously, updating the LFO rate and envelope attack.
All three parameters react to a single control movement, demonstrating the reaction chain pattern.
Summary
- Bouncy links in nallely-midi are parameter connections marked with a
bouncy=Trueflag that automatically propagate changes to all other links sharing the same source. - The reaction chain mechanism relies on
Link.triggerchecking the bouncy flag and callingbounce_link, which scans the device's link registry for matches. - Both MIDI devices (
nallely/core/midi_device.py) and virtual devices (nallely/core/virtual_device.py) implement thebounce_linkmethod to support hardware and software synths. - The
TrevorAPI.make_link_bouncymethod provides a clean public interface for enabling this behavior without directLinkmanipulation. - This architecture enables complex, single-source-to-multi-destination modulation graphs with minimal configuration code.
Frequently Asked Questions
What is the difference between a standard link and a bouncy link?
A standard link creates a one-to-one connection between a source and destination parameter. A bouncy link extends this by automatically triggering all other links that share the same source path after executing its own update, creating a one-to-many propagation pattern known as a reaction chain.
Can bouncy links create infinite loops?
The nallely-midi implementation prevents infinite recursion because bounce_link only triggers links that share the exact same source path as the original triggering link. Since links are unidirectional (source → destination) and the bounce operation specifically targets co-source links rather than following destination-to-source cycles, the chain naturally terminates after all matching links fire once.
How do I disable the bouncy behavior on an existing link?
Call TrevorAPI.make_link_bouncy() with bouncy=False for the specific source-destination pair. This sets the Link.bouncy attribute to False, causing future triggers to execute only the local callback without invoking bounce_link or propagating to other connections.
Do bouncy links work with both hardware MIDI devices and virtual software synths?
Yes. The bounce_link method is implemented in both MidiDevice (nallely/core/midi_device.py) and VirtualDevice (nallely/core/virtual_device.py), ensuring consistent reaction chain behavior across hardware controllers and virtual instruments within the nallely-midi framework.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →