ScriptNode Feedback After a Month of Heavy Use
-
I've been living and breathing ScriptNode for a month now, day in, day out. I used it here and there over the past few months but it was for small networks and modulators, usually no more than 10 nodes.
It's ridiculously easy to get most DSP prototypes working in Scriptnode. If you add Faust to the mix, it really is a superweapon ready for the future. Global cables as well as external slots make it easy to communicate the data out to the UI.
I'm currently working with massive, interconnected networks with lots of parameters. I think I loaded every node at least once.
For some context: I have a decent amount of experience using Unreal Engine Blueprint and writing complex animation blueprints and state machines in Unreal (putting together designer-friendly AAA-level non-replicated locomotion systems and character blueprints up until UE5.2).
I've been vocal about ScriptNode lately in various threads and have posted numerous bug reports as well. I figured collecting my feedback in this thread will make it easier to discuss.
Issues
There's a number of issues lurking, many of which have been only made apparent in my attempts to scale this thing to megacity-levels (200+ nodes and counting).
Unique IDs
The biggest design flaw in ScriptNode is that connections are tied to IDs, which are user-facing and user-editable, and the repercussions of messing with this are not audible until you reload the network (e.g. close and re-open HISE).
I've elaborated on this here. https://forum.hise.audio/topic/9835/scriptnode-prevent-renaming-a-node-id-when-attempting-to-use-one-that-already-existsThe magnitude of this issue is most apparent when trying to copy/paste nodes which have active connections (it's impossible): https://github.com/christophhart/HISE/issues/546
Suggestions:
- unique IDs for nodes in the back, just a name property for the front. Notes spawn with the default name property without the number, like they currently do.
- if a name of the node is changed from the default name, hovering the node name should show the original node name (the factory path property from right click)
Navigation
I haven't found a way to navigate around a network freely other than with a mouse or a touch pad. It becomes problematic when you want to plug a, say, peak node into whatever parameter of another node two screens away. If you don't have an MX Master, which has a vertical and a horizontal scroll wheel, or a similar peripheral, and the network is too big even for zooming out, your only choice is to exit HISE, edit the XML and reload the project.
Suggestions:
- double clicking on an output pin or parameter enters plug mode: you can now freely scroll by clicking on the scroll sliders or using two fingers on a touchpad
- be able to use arrow keys to scroll the screen instead of jump around node selection
- moving the mouse while holding the drag pin to the edge of the screen scrolls the screen, like in a CRPG/Strategy/MOBA game.
Labeling
When working on a big anything, keeping things organised is paramount. Node comments in a vertical layout are fine. But if you switch to a horizontal layout, they now unnecessarily add to the width by staying on the side, when they could be moved to the top or bottom.
https://forum.hise.audio/topic/9816/put-node-comments-above-below-nodes-when-the-container-is-laid-out-horizontallyAlso, once the ID issue is sorted out and the name property becomes just a name, right clicking a node should automatically enable the editing of the node property field so you can immediately type a desired name.
Connection Clarity
Once things get bigger than your screen, and as they grow, it becomes ridiculously hard to keep track of connections.
This has been briefly touched on here https://forum.hise.audio/topic/9819/scriptnode-highlight-incoming-cable-on-hover-when-cables-are-hidden but there's still a long way to go.- If you right click a source (parameter or output pin), a target will be displayed
- If you right click a target, a source will be displayed only if it's a parameter, not if it's a node (e.g. peak)
- After the feature in the link above was added, the source will now be displayed on hover if you hover a target, but still nothing if you right click it
- There's no mention of the source or target's parent nodes
- The names are direct node IDs, which ties in to the ID issue mentioned above, so you're discouraged from changing the node names, in fear of accidentally ending up with two nodes that share the same name/ID
- If you collapse a node, connections to any of its parameters or child nodes disappear both visually and in the right-click connections tab for the source parameter
Suggestions:
- The new feature that shows a source should always be on
- Remove the tooltip when hovering a parameter which just repeats the visible node ID and parameter ID, that just obscures the new source tooltip
- Show full target hierarchy both on hover and on right click menus as outlined here https://forum.hise.audio/post/81634
- Fix missing source connections from node outputs when right clicking parameters
- Show cables running to and from collapsed nodes; show source/target node names in the connection list
- Have a way to click on a node name in the connection list in parameter properties that will move the viewport to that node
Wishlist
Here are the things that I find missing at the moment.
Logic operators and Clamping
Basic < > = operators, as well as unscaled input clamping. Discussed here. https://forum.hise.audio/topic/9844/scriptnode-add-to-control-logic_op/6
Variables
Having variables in scriptnode that can be saved or read. Here's an example from Unreal's Blueprint: https://youtu.be/oKGJECvQJA8?t=88
Suggestion:
- variable creation window, maybe like the parameter toolbar; variable needs an identifier and a default value
- get node (dropdown menu that lets you select the variable to get, output pin)
- set node (dropdown menu that lets you select the variable to set, parameter for writing, writes on change)
The get node avoids the need for a cable mess since you can write to a variable with set, and then just load as many get nodes as needed.
Wrapping Nodes into External Files
Just like you can wrap nodes into a chain, and like you can move selected code into an external file, you should be able to move the nodes to an external file, which will then be available in the Project tab in Add Node, automatically replace the moved nodes and connections. Because DspNetworks need defined parameters if you need input, perhaps this moving into external files should only be possible for chain nodes, as you can add Parameters to it.
Cable Outputs for Embedded Project Nodes
If you create a network, it can always be added as a node to any other network from the Project tab of the Add Node window. Any parameters of the main container are also visible here.
It would be great to also have output pins for any (scaled or unscaled) peak node from within the network, with usual text ("Drag to Modulation Target") being replaced with the name of the peak node. This is the equivalent of adding outputs to a function in Unreal Blueprint; whenever you add that function to any blueprint, it has the configured input/output pins. Scriptnode currently has inputs (parameters), but not outputs like this.
Extended Expr Nodes
Math and Cable.expr nodes are great, I just wish they had more inputs for algos that need more than one input but are small enough that they don't warrant a SNEX file, which limits visibility
SNEX Docs
I gather SNEX is the place for any complex algorithms because it supports variables and is blazing fast, but at this point I have no clue how to get a sound out of it; the examples in the docs either don't compile or crash HISE.
-
@aaronventure Thank you for your hard work and ideas. I'll add my $0.02. Perhaps there is a smoother way to handle creating one type of module for writing ScriptNode, and then a different one for running them. Perhaps a wrapper that faces the user,, that appears to be a single module. Under the hood, it's still two different nodes.
-
Wow Aaron thanks for the in-depth report. When I'm done with the multipage port into HISE, I'll spend some time in scriptnode and implement some of the suggestions.
Variables are a very good idea - maybe we'll call them
local_cable
as the workflow can be exactly like with global_cables but with them being scoped to the network only. This should work well within the concept of code generation and it will definitely help with the UI mess that many cables can generate. Making the ID a constant string and adding a newTitle
property is also in the list of confirmed feature suggestions that await implementation.There are a few things which are already in there, but since you've spend so much time in scriptnode and didn't find them I guess I did a terrific job hiding them from the user.
- You can already "move" chains to an external file. Click on the package icon and Choose Wrap into DSP Network. The UX is all over the place and you need to press resave after performing this step, then unload and reload the network, but the basic functionality is exactly what you suggested - it even retains the parameter connections.
- cable outputs are available, although only one :). Just use the public_mod node
- not sure about the extension of the expr node. I have to draw the line between these and the SNEX nodes somewhere and if I give in we'll be moving the goal post here until the expr node is as powerful as the SNEX node. The underlying problem is rather that the SNEX nodes are a bit too clunky to use so people avoid them.
- Can you link to the examples that crash? All snippets from the browser tagged with SNEX work fine here.
- Regarding the UX of navigation and organizing, another approach is to nudge people into breaking down their big networks into smaller, more manageable subnetworks. This doesn't mean that the suggestions are bad or that I won't spend any time improving the workflow of scriptnode, but I usually took it as a good hint that a code / module reorganization is due when the editing became too painful. So for example all your suggestions for improving the cable appearance will be rendered unnecessary once there is a variable system that you can use to send / receive values from different places because that will be the preferred way of connecting things that are distant from each other.
-
@Christoph-Hart said in ScriptNode Feedback After a Month of Heavy Use:
Wow Aaron thanks for the in-depth report. When I'm done with the multipage port into HISE, I'll spend some time in scriptnode and implement some of the suggestions.
Nice, good to hear that.
@Christoph-Hart said in ScriptNode Feedback After a Month of Heavy Use:
Variables are a very good idea - maybe we'll call them local_cable as the workflow can be exactly like with global_cables but with them being scoped to the network only. This should work well within the concept of code generation and it will definitely help with the UI mess that many cables can generate. Making the ID a constant string and adding a new Title property is also in the list of confirmed feature suggestions that await implementation.
Sounds good. But the local_cable name is terrible. It will confuse people on how it is different than the send/receive node, and the name cable doesn't imply that it can store a value or have a custom identifier. I get that implementation is similar, but just name it local_variable or something clean like that.
@Christoph-Hart said in ScriptNode Feedback After a Month of Heavy Use:
You can already "move" chains to an external file. Click on the package icon and Choose Wrap into DSP Network. The UX is all over the place and you need to press resave after performing this step, then unload and reload the network, but the basic functionality is exactly what you suggested - it even retains the parameter connections.
Huh. Alright, cool. Would be great to do have these steps be done automatically although I understand the hesitation as the saving should be a manual action. Perhaps a prompt? Do you want to resave and reload? If no, it says what should be done manually.
@Christoph-Hart said in ScriptNode Feedback After a Month of Heavy Use:
not sure about the extension of the expr node. I have to draw the line between these and the SNEX nodes somewhere and if I give in we'll be moving the goal post here until the expr node is as powerful as the SNEX node. The underlying problem is rather that the SNEX nodes are a bit too clunky to use so people avoid them.
Ah, I don't think it'll go that far. I haven't seen any other requests for it.
So maybe a plus button like for the xfader node that lets you add up to 8 parameters. By default it looks clean, and inputs can be added if necessary. Following the ID changes, these should have a consistent unique ID for incoming connections but the SNEX code in the expression should refer to the name we assign to them.
There really is no space on the expr node for stuff beyond this, it's just a single expression. but sometimes you want to plug multiple things together and do a slightly more complex math operation or use the math api, while retaining the visibility. Anything requiring two or more lines of code should be the job of a SNEX file.
@Christoph-Hart said in ScriptNode Feedback After a Month of Heavy Use:
Regarding the UX of navigation and organizing, another approach is to nudge people into breaking down their big networks into smaller, more manageable subnetworks. This doesn't mean that the suggestions are bad or that I won't spend any time improving the workflow of scriptnode, but I usually took it as a good hint that a code / module reorganization is due when the editing became too painful. So for example all your suggestions for improving the cable appearance will be rendered unnecessary once there is a variable system that you can use to send / receive values from different places because that will be the preferred way of connecting things that are distant from each other.
Alright, I'll be happy to test out that hypothesis. I still think that showing relative hierarchy for source/target is a good idea. mul7? Where the fuck is that? I can search for it in the Node List, but it won't tell me very clearly where it is (it'll filter out its parents too so you can't see the hierarchy) and clicking on it will collapse every other node, which takes a bit of time with a big network, and when I click back on the root container to get my view back, it uncollapses all the nodes, some of which I prefer having always collapsed (clear nodes, abs nodes etc.; this seems like another pain that becomes apparent when dealing with big networks). Instead, the connection property'll just keep mentioning the parent nodes until it gets to a parent node they share and you instantly know where it is.
I get the modularisation and I agree, but not with the current state of ScriptNode. In Unreal Blueprint, as soon as I had more than 6 nodes together, they would go off into a function. BP lets you clearly define inputs and outputs for functions, as well as their types. For scriptnode the only relevant thing would be whether the output is scaled or unscaled, which could be defined with the type of peak node used; btw the new unscaled peak node reduced my node count by at least 60-70 so far.
Because if I have a modchain, I might want to take more than one output from it. E.g. some of its PMA outputs can easily be used elsewhere.
I know this node megacity of mine is an outlier and the idea with ScriptNode was to be making smaller DSP algorithms and combining them together in a chain, but who says you can't push things further? It seems to be working just fine performance wise (the UI gets a bit sluggish but that's expected and collapsing nodes helps), it's just that the UX issues pop up when it gets pushed this far, but that's just low hanging fruit when it comes to making ScriptNode run smoothly with bigger projects.
It does remind me of the evolution of Unreal Blueprint throughout the years. It started like a quick and dirty replacement for smaller algorithms so you can code live and iterate fast, and evolved into a workflow that lets you make an entire game without writing any C++ at all (e.g. Mortal Shell).
-
@Christoph-Hart said in ScriptNode Feedback After a Month of Heavy Use:
Can you link to the examples that crash? All snippets from the browser tagged with SNEX work fine here.
I meant the one in the HISE Docs webpage. The snippet from the browser opens fine. Still getting used to that being a thing!
-
@Christoph-Hart Alright, having looked at the SNEX example in the snippet browser, some things are still not clear:
- how to set channel count to 1 (so it can be used in a modchain)
- how to send signal to all channels instead of one at the time
- how to refer to a table that was added to the SNEX node
If you can make a snippet that covers basic modchain usage of SNEX with some simple math operation, e.g. has an input parameter for velocity and outputs a 0-1 signal based on input velocity and a curve on a table, that would be great.
-
@aaronventure said in ScriptNode Feedback After a Month of Heavy Use:
I meant the one in the HISE Docs webpage.
You mean that thing?
https://docs.hise.dev/scriptnode/manual/snex.html
Yeah, that's on my TODO list of things to get rid of and completely rewrite as it is completely outdated.
how to set channel count to 1 (so it can be used in a modchain)
Just drop it in a modchain, it will call the process functions with a templated channel count of 1. Might cause a compile error though :)
how to send signal to all channels instead of one at the time
Can you elaborate?
how to refer to a table that was added to the SNEX node
grab a reference in the
setExternalData()
callback that you then can access. You need to take care of proper thread synchronization though so that accessing the data while you're changing it won't blow up the system. This sentence will not make too much sense for anybody else than me, so:If you can make a snippet that covers basic modchain usage of SNEX with some simple math operation, e.g. has an input parameter for velocity and outputs a 0-1 signal based on input velocity and a curve on a table, that would be great.
Yup, good idea, will do it right away.
-
@Christoph-Hart said in ScriptNode Feedback After a Month of Heavy Use:
Can you elaborate?
@Christoph-Hart said in ScriptNode Feedback After a Month of Heavy Use:
Just drop it in a modchain, it will call the process functions with a templated channel count of 1. Might cause a compile error though :)
-
@aaronventure ah ok, so to iterate over each sample of each channel you simply have to use a nested loop:
// iterate all channels for(auto& ch: data) { // create an from the iterator object ch for(auto& s: data.toChannelData(ch)) { s = Math.random(); } }
The magic spell in this case is probably the
toChannelData()
method that will turnch
into a properdyn<float>
block.HiseSnippet 1609.3oc2X80aaaCDWxIJY1sqqEsaOqmJb.BbSZ65FV5Zahi8pwVbLpRC5aArRzwDlhTPjJwdC888wZej52fs6DkrjrcSa75FvlMhk3cG4c2u6OjLzIZpHfFblRvhhnZKK60FDK8oJkL1xt9ISinV12zwapPOp8HBSX06PK6a4bDQoowtFRGLMhnTz.Ka609Ijfc80sR+79me.gSD9zBRVVmJY9zegExzETG7helw4cIAzSXgkj9wunmuTzVxkIf8rlyNVQD+wjyo8InX0brr2nS.SKi8zDMUAxbfLXp2H4kBi7mxTr2xo3fcs7fExP1p8HFOXlup.Oe8Rd9ZFO+dNGwBXynWf.2NkgawLJiA10tJSZ2qgIYWxjV2XR2wwyOlEoK3f1yMb5If.xPB.0kMEirV1+gSaIHfP2JjLl1MFFLaBMexN6rsK7yV6MLQ3qYRgqTzWpoGKZtUieqQ8Fuqg67rFNbo7P0DK4bZ7RYiQ23qZhMEIgukFus6EDdBclff6WES23SCS8MdcIAkhdBl93HZ13tRd.hU36KFArxfM3sW26PhlfAkLZfbQzXMCMG6CoW.Y0lPTcmCopwZYDjWuP7CxbjAIbhtZ5DV2jw.viJwPLPITL8zx0UWibrctxbrOUS7NNCXZ+QK2FqsDaDPp+IrwrJyuzoyvgTecgAttS22rpkg6bsKCajUFB5DU9MyF518M6tzBve2oHYOJlFQhomHGvISapHgQb5q.aba22xk9i8X+JcwJjHiIb.JQS+QDgfxUqRgzFelZNY8JYhlIN+HhNlMwx1oeRnGzl1m1Ny5.Z10vZHy3cvwXdgGrmS5f+D9jwbWbrcFycyYVp7qOUeoLdbZ3H6cK6MMXuJEjOa3Dv7owJL4zdSmcZAes1mykW1VFFwxRagfRJsAR9znQRAyGIYjH2z2OTl.ZMy9eIQcBgww7buDEz4H3XgGHb59ZXdVeY.7lSWhO.rSGPziv5Er2CjbRia4OKIshsVjmfHLtH4azYvIvlBQmOcat0P9oaSkoMSaeEKfZV9JZ+qKo8BQJmX9wTWotalB.bDTdWy3F8Tmhb8IbqSwDLDbrxMQ6Rl3lNgIbMy5CBNF1qpoMSm0Joya4HU9v4IfeAIqp5uwgHH7oJZqJxrx5GSb3zI46MbHSEAU0Gj.cmL8NpPAwO3.WSxSv6.0nAv9OoSGWcqAjX3nM5zIaUxm9pJ9zt+G1o9vC9ft6i9+QLrgSnLXIUq2qb4PtDqV2gZU6NHnSNSfTpnuaC5Kl1pf60RWqeksFp6zlCqUuf79Ba5.aNMd57nryIDXaFDf73PCp3Av45wQ6mDvjcYFVvyLTb9XRULt3TImPhOmpmC22vIhRFWECtgACRYbMRbp37KUueNSlx1E7JJeVwtzyZN6yoj3pHyMcBgmsLb9Dgl65ngcNG6FRcA7.Ria0BLhJXUY6FL07QPz4HlHy5JN63QjIyQ68O2SSivCGUVNuwzKMVeYIM9lYAVXo+6ifGHkiCIoGFY0tVv+FGcJj3GKOy2bZPz49hTJPItH8ty0gKwCic205h4QnP3tsm46WcoVXhObUm3iV0I93Uche6pNwmrpS76V0I98e7Ih+qG1OQKCMGnEJVFzwzuvti.6plVsZQyZkXtBBlqNiBz3OqEqs8PFdZ+Z3Cikb2hsMdPZq6VixuGsUsWulilBM3fhb2mxDZ29m9LWkNNwW6lJLdQCu9cdyY8O9vNMSIs0dvcOp+fG3h27lQ3LE0UOhlekFnPvENUsuxcDMl1n9ERVP9cjZNv7zKkeDbgm5v5WGtaSdkh6OBjaUh.nr2kouWQUT87pJhEQ4vVsk0VLJXyrEuX9YW2IcETrygybjMoBHPCk1HrkKKFGvx8m4l4FoTaNG266F.ukougx3lDHbdeW+Q+PNiTNkXoLbZokY9ItTv8.2Bk0HbcDKNBacGSDAxvlHtWGcF7uqvmHJ2gXGwEbNL91tpmzEEroJhHd5Ptjn2FDnr2rL0bTuC64Ru.xepDhA+HfSeIjMzA40b1a22ktPn30o4LLUwMc0RWHnkDkpB5DncN5Knkjs9.2NYjMfkTnztkIAF91tnSxvsfWPmd4IOy1uXoHzfLDBz2rcVZFHSfBQ2Kptnn3Uyce3dMd2dMrr9K.PTIL7B
Load up this snippet, then inspect the
numChannels
data member while you drag around the node in various places. It's guaranteed that theprepare
callback will always be executed before theprocess()
callbacks are coming in, so you can use that information too for figuring out channel-specific stuff.I also noticed the error with the channel count when I started writing the example :)
However it goes away if you save and reload the network - I think the problem is that if you create a new SNEX file and compile it the first time it assumes stereo processing, but in subsequent compilations it picks up the actual channel count correctly. -
@aaronventure Not to derail your current conversation but but you make some excellent points so kudos to you for putting it altogether in a single post. Coming from more of a MAX MSP background I am definitely seeing Scriptnode as a robust DSP alternative, and I think it is part of what is drawing so many users to HISE.
I have been messing around in Scriptnode for over a year now and I have noticed most of the issues you brought attention to. I think the docs could use a healthy touchup. I personally feel a few tutorial videos on making Scriptnode networks would put an end to half of the forum posts we see on the topic. My personal feeling is we need a D. Healey but for Scriptnode. to come out of the woodworks.
There are a lot of functions that are hidden in Scriptnode already that users aren't familiar with that greatly increase workflow (wrapping DSP networks, the importance of different container types, show/hide cables, copy range to source, loading DSP Graphs, etc.). It took me a while to figure out these things were there.
I actually agree with Christoph about modularization, as it reminds me more of how MAX MSP operates. It really isn't the cleanest, but encapsulating certain aspects of DSP and recycling them across networks is a more ideal solution. If there were a simpler/clearer way to share these across projects without having to drag network files across projects, that would be great.
One feature I personally have been eagerly waiting for is a buffer inside Scriptnode which opens up a can of DSP worms. I know there is the Recorder Demo Snex project, which is perfect, except that it still doesn't compile
Other issues I noticed is embedding RNBO .dll in Scriptnode requires you to resave the network each time before compiling .
The IDs are the number one issue so glad to see that is being addressed. I have had other issues that I can't recall that I will post here after I give it more thought.
Also, sorry to derail the conversation further, but how do you add comments/labels inside Scriptnode? I never figured this out but I have a few projects I intend to share soon to help users cut their teeth with Scriptnode, so I want to be able to explain the signal chain.
-
@HISEnberg said in ScriptNode Feedback After a Month of Heavy Use:
, sorry to derail the conversation further, but how do you add comments/labels inside Scriptnode? I never figured this out but I have a few projects I intend to share soon to help users cut their teeth with Scriptnode, so I want to be able to explain the signal chain.
Right click any node and type it in
-
@aaronventure Wow thanks, it was staring me right in the face
-
Thanks for the snippet.
@Christoph-Hart said in ScriptNode Feedback After a Month of Heavy Use:
// iterate all channels
for(auto& ch: data)
{
// create an from the iterator object ch
for(auto& s: data.toChannelData(ch))
{
s = Math.random();
}
}Do you think it's a good idea to put this structure in the base template? I can't imagine a scenario where I wouldn't want stereo linking without offering a stereo linked version as well.
-
@Christoph-Hart said in ScriptNode Feedback After a Month of Heavy Use:
You can already "move" chains to an external file. Click on the package icon and Choose Wrap into DSP Network. The UX is all over the place and you need to press resave after performing this step, then unload and reload the network, but the basic functionality is exactly what you suggested - it even retains the parameter connections.
I think part of the issue of this being hidden is that it's only visible in the package icon in the main toolbar, and not if you right click the node.
-
@HISEnberg Yes! I would love a buffer! Insane granular experiments would ensue