When writing a modgui skin for an LV2 plugin that exposes its parameters via patch:writable (the atom/patch protocol) rather than traditional lv2:ControlPort ports, using the documented mod-role="input-control-port" in icon.html causes knobs to render correctly but not respond to interaction. There is no error or warning — the knobs simply do nothing when dragged.
Root cause
In modgui.js, assignControlFunctionality populates self.controls from effect.ports.control.input:
self.controls = self.makePortIndexes(effect.ports.control.input)
For patch:writable plugins this list is empty (only internal ports like enabled and freeWheeling appear there, both with
pprop:notOnGUI). The binding code then looks up the knob's mod-port-symbol in self.controls, finds nothing, and silently skips the entire binding:
var port = self.controls[symbol]
if (port) {
// bind the knob — never reached for patch:writable plugins
}
Affected plugins
Any LV2 plugin built with JUCE uses patch:writable exclusively for all parameters. This includes the entire Surge XT effect suite and any other plugin exported via JUCE's LV2 support. The pattern is increasingly common.
Workaround
Use mod-role="input-parameter" with mod-parameter-uri set to the full parameter URI instead:
This is undocumented in the SDK and not mentioned anywhere in the modgui skin examples or wiki. A developer following the standard
examples will produce broken skins for this class of plugin with no indication of what went wrong.
Suggested fix
Either of these would resolve the issue:
- When input-control-port + mod-port-symbol finds no match in effect.ports.control.input, emit a console.warn identifying the
unresolved symbol. This costs nothing and would immediately surface the problem.
- As a deeper fix, fall back to checking effect.parameters for a parameter whose URI ends with :symbol. If found, bind using the
lv2PatchSet path. This would make input-control-port work transparently for both parameter types.
When writing a modgui skin for an LV2 plugin that exposes its parameters via patch:writable (the atom/patch protocol) rather than traditional lv2:ControlPort ports, using the documented mod-role="input-control-port" in icon.html causes knobs to render correctly but not respond to interaction. There is no error or warning — the knobs simply do nothing when dragged.
Root cause
In modgui.js, assignControlFunctionality populates self.controls from effect.ports.control.input:
self.controls = self.makePortIndexes(effect.ports.control.input)
For patch:writable plugins this list is empty (only internal ports like enabled and freeWheeling appear there, both with
pprop:notOnGUI). The binding code then looks up the knob's mod-port-symbol in self.controls, finds nothing, and silently skips the entire binding:
var port = self.controls[symbol]
if (port) {
// bind the knob — never reached for patch:writable plugins
}
Affected plugins
Any LV2 plugin built with JUCE uses patch:writable exclusively for all parameters. This includes the entire Surge XT effect suite and any other plugin exported via JUCE's LV2 support. The pattern is increasingly common.
Workaround
Use mod-role="input-parameter" with mod-parameter-uri set to the full parameter URI instead:
This is undocumented in the SDK and not mentioned anywhere in the modgui skin examples or wiki. A developer following the standard
examples will produce broken skins for this class of plugin with no indication of what went wrong.
Suggested fix
Either of these would resolve the issue:
unresolved symbol. This costs nothing and would immediately surface the problem.
lv2PatchSet path. This would make input-control-port work transparently for both parameter types.