Noisecraft - anyone heard of it?

I should qualify my VP expectations: I spend a lot of time typing on the keyboard when I use Node-RED, no question. So yes, I still do a lot of textual coding and yes, that won't disappear anytime soon (perhaps with a Neuromancer-style direct interface we'll get rid of the keyboard).

But I'm firmly a fan of the the old adage a picture is worth a thousand words - somewhere between all text, no visuals and all visuals, no text is the sweet spot. Where? I don't know. I definitely hope that all that we have learned from textual programming doesn't fall by the wayside - as it happening with AI.

1 Like

Argh, I can import all I want, it just works :frowning: - I have had escaped-stuff-problems before in other flows but not in this one - it just works. Using Firefox on Mac ... and those encoding bugs are just rabbit holes (looking at you geolib and the ° symbol).

On the other hand, I wonder why it would do that but not with a normal function node? Perhaps I did code something incorrectly somehow ... I just copied it from the github diff window, nothing special. I'll have an investigate ... thanks for the heads up :+1:

I just want to be able to hit the inject button and have things happen in my editor :slight_smile: Thanks for the link, I was wondering how to get a user-made header onto Node-RED.

Thank you, much appreciated just as much as I enjoyed finding and using your SVG node :+1: Ended up multi-coloured blinking SVGs!

Btw thinking about how to change the code ... first step is width of nodes, the code assumes 200px for all nodes.

1 Like

It might be useful if some settings were (somehow) easily adjustable, because they are user dependent. For example I like to have a smaller distance horizontally between my nodes, while other users like to have them separated widely...

Not sure if this is somehow related to the Node-RED lint tool settings. Never had time to play with it. Just thinking out loud now...

I'm using document.getElementById(id).getBBox().width to get the width of the node, then there needs to be spacer that can/should be adjustable but to avoid overlap the width of the node needs to be known. Also it's the width of the parent node, which is another issue on its own ... :slight_smile:

I also had a look at the flow data for the client-code flow, there is no > in there (for the client code code) - if you edit the node, is there > in the Node-RED editor window?

Yes, a plug in can register actions to be available from the command palette which in turn can be bound to shortcuts

Hey Steve,
That is good to know. Never used it.

So Gregorius could register a auto-layout action via a plugin, so we can call it that way. And if he wants to do auto-layout by injecting a message, he can simply do that with a function node (by executing that action via Js code). Or if he wants he can build a custom node that is dedicated to executing actions, e.g. that he can select an action from his config screen perhaps...

@gregorius: Perhaps if you create a new discussion about auto layouting, you might get some more feedback compared to a discussion about Noisecraft :yum:

3 Likes

I figure I should say thanks here @gregorius. You are really opening up some new ideas and challenging the status-quo. Good for all of us who've been around Node-RED for a long time. :grin:

We still might not always agree, but it is healthy to have the debate regardless.

3 Likes

Thank you all for helping :slight_smile: And above all, thank you all for making Node-RED such a joy to work with :+1:

Much appreciated :blush: and I hope I can continue to be a bit off-the-wall!

3 Likes

For those interested, this flow (new node at the top left) does layouting using elkjs - just a first attempt and the options need finetuning, but it demonstrates how it could be done ... or how it could be done better :slight_smile:

@BartButenaers can you try again please, I think I've found the encoding bug - it seems that the base64 node decodes to string('binary') and it should be string('utf8') (in my case) - the flows are now coming out properly.

@gregorius,
Thanks for the follow up of my issue!!
Unfortunately I still have the errors in my function node, when I import the flow from your previous post.
I see that the escaped characters are already available when I click "copy flow to clipboard" on FlowHub:

image

Or do you need to regenerate all the flows on FlowHub?

Perfect! I can reproduce that using Firefox & Opera ... ok, I'll have a look

Cheers!

1 Like

@BartButenaers thanks for the help, should be fixed with innerText instead of innerHTML - doh, bug was 40cm from screen!

Nice catch!! The flow now loads without syntax errors. Much better!

But none of the algorithms seem to work for me, i.e. the selected node(s) are not rearranged. Instead I see these errors in the Debug panel: ""Missing return node information""

And when I use the subflow to obtain elkjs configuration settings, I first get "timeout" messages and tthen again the ""Missing return node information"" message.

I thought I saw some error passing by in my developer tools, about the elk.js minified file that could not be loaded. But I forgot to make a screenshot, and now I don't get that error anymore.

My time is up for today...

The only thing I can think of is that you selected a group of nodes and not a single node. I noticed that through the grouping of groups of nodes - it's a lot of clicking to get to a node. I added a notification if that should be the case so it's clear that a node wasn't selected.

The timeout message still appears in the debug panel, that's because the link-call (the corresponding link-in never returns) times out when a notification is shown.

@gregorius,
After updating your introspection node, it worked. Not sure if that was the root cause. Perhaps something about my system, or me doing something stupid wrong.
Anyway you can forget about it!

1 Like

Some great discussion here about VPL :slight_smile: a minority of people deeply understand assembly while the majority of programmers only understand the text-based languages built on top of it. I think the same could be argued for text-based languages and VPL with programmers and non-technical users.

As has been said, it seems like VPL might not reach general purpose any time soon, but a common textual standard for representing applications could be interesting step in the right direction…

Dunno what it is, must be because it's friday - found another two interesting products that should be here:

  • NoFlo Flow-Based Programming for JavaScript
  • and from them came flowhub.io

both of which I found via Flow Based Programming by J Paul Morrison - spoiler: we're doing nothing new here!

1 Like

Actually, Node-RED IS at least slightly different and is, in my opinionated opinion anyway :slight_smile: much easier to use than the other tools. And it is also fully open source which I don't think NoFlo is?

But no, Node-RED was far from being the first flow-based programming tool, that's for sure. But it is arguably one of the most successful. And certainly one of the most approachable ones.


OK, maybe NoFlow is now open, seems to have moved on a bit. But still a lot more complex than Node-RED - and I think they really need a UX designer to help them!

This is their example for a click counter:

I think that the equivalent in Node-RED would probably be a bit simpler. :wink:

This is an thought that's being going around my head in the last couple of weeks: every language is extremely flexible and can do anything. What all languages require are abstractions and reuse: libraries, functions, methods, objects ... whatever. If a language lets you build up your toolbox of abstractions, then you can do anything with very little effort, because you begin to use those abstractions.

I think Node-RED has nailed that by having subflows, flows and nodes as abstraction layers. We all start out building complex flows, then create subflows to simplify flows and eventually we create nodes to simplify subflows (if we look at it hierarchically). (another example is using function nodes where a switch or change node would do the same ...)

That NoFlo flow that you show may be complex however if it's quickly possible to abstract parts of that flow away, then NoFlo becomes useful. Remember, its difficult to create a general language that is always simple, that's why there are frameworks - abstractions for specific tasks.

Personally I have began creating flows that create flows and nodes - yet another level of abstraction. To prove the point:

[{"id":"6030f1e36ecbc405","type":"function","z":"73b4aedc.e9602","name":"generate nodes for 3d ticker wall","func":"/* How big should the display be? */\nconst nrCols = msg.nrCols;\nconst nrRows = msg.nrRows;\n\n/*\n  Matrial ids: these are taken from using \"Export Nodes\" and reading the json generated.\n*/\nvar materials = [\n    [\n        \"17350478e877bc09\", // red\n        \"e7298bcff0d93b16\", // white\n    ],\n    [\n        \"e7298bcff0d93b16\", // white\n        \"17350478e877bc09\", // red\n    ],\n]\n\nvar junctionToAll = {\n    \"id\": RED.util.generateId(),\n    \"type\": \"junction\",\n    \"x\": 0,\n    \"y\": 0,\n    \"wires\": [\n        [\n        ]\n    ]\n}\n\nvar colRouter = {\n    \"id\": RED.util.generateId(),\n    \"type\": \"switch\",\n    \"name\": \"col switch\",\n    \"property\": \"col\",\n    \"propertyType\": \"msg\",\n    \"rules\": Array.from({ length: nrCols }, () => { return { \"t\": \"eq\", \"v\": \"1\", \"vt\": \"num\" }}),\n    \"checkall\": \"false\",\n    \"repair\": false,\n    \"outputs\": nrCols,\n    \"x\": 0,\n    \"y\": -600,\n    \"wires\": Array.from({ length: nrCols }, () => { return []}),\n}\n\n// set correct values\ncolRouter.rules.forEach((i, d) => i.v = (d + 1).toString())\n\nvar rowRouters = [];\nfor ( var y = 0; y < nrCols; y++) {\n    var rowRouter = {\n        \"id\": RED.util.generateId(),\n        \"type\": \"switch\",\n        \"name\": \"row switch for col \" + (y+1),\n        \"property\": \"row\",\n        \"propertyType\": \"msg\",\n        \"rules\": Array.from({ length: nrRows }, () => { return { \"t\": \"eq\", \"v\": \"1\", \"vt\": \"num\" }}),\n        \"checkall\": \"false\",\n        \"repair\": false,\n        \"outputs\": nrRows,\n        \"x\": 200*y,\n        \"y\": -500,  \n        \"wires\": Array.from({ length: nrRows }, () => { return []})\n    }\n\n    colRouter.wires[y].push(rowRouter.id)\n    rowRouter.rules.forEach((i, d) => i.v = (d + 1).toString())\n    rowRouters.push(rowRouter)\n}\n\nvar valueScaler = {\n    \"id\": \"WILLBEPREPLACDE\",\n    \"type\": \"change\",\n    \"name\": \"Set Z scale\",\n    \"rules\": [\n        {\n            \"t\": \"set\",\n            \"p\": \"payload\",\n            \"pt\": \"msg\",\n            \"to\": \"{\\t    \\\"type\\\": \\\"scale\\\",\\t    \\\"relative\\\": false,\\t    \\\"values\\\": {\\t      \\\"x\\\":1,\\t      \\\"y\\\":1,\\t      \\\"z\\\":$$.payload\\t    },\\t    \\\"pivot\\\": {\\t        \\\"x\\\": null,\\t        \\\"y\\\": null,\\t        \\\"z\\\": null\\t    }\\t}\",\n            \"tot\": \"jsonata\"\n        }\n    ],\n    \"action\": \"\",\n    \"property\": \"\",\n    \"from\": \"\",\n    \"to\": \"\",\n    \"reg\": false,\n    \"x\": 0,\n    \"y\": 0,\n    \"wires\": [\n        [\n\n        ]\n    ]\n}\n\nvar baseNode = {\n    \"id\": \"WILLBEREPLACED\",\n    \"type\": \"box\",\n    \"scene\": \"c0d1433da002f3c6\",\n    \"material\": \"WILLBEREPLACED\",\n    \"name_conf_a\": \"WILLBEREPLACED\",\n    \"size_conf_n\": 1,\n    \"height_conf_n\": \"\",\n    \"width_conf_n\": \"\",\n    \"depth_conf_n\": \"\",\n    \"updatable_conf_b\": false,\n    \"sideOrientation_conf_a\": \"1\",\n    \"pos_x\": \"WILLBEREPLACED\",\n    \"pos_y\": \"WILLBEREPLACD\",\n    \"pos_z\": 0,\n    \"scale_x\": \"1\",\n    \"scale_y\": \"1\",\n    \"scale_z\": \"0.25\",\n    \"rot_x\": 0,\n    \"rot_y\": 0,\n    \"rot_z\": 0,\n    \"x\": 0,\n    \"y\": 0,\n    \"wires\": [\n        []\n    ]\n}\nvar nodes = []\n\nfor (var x = 1; x < (nrCols + 1); x++) {\n    for (var y = 1; y < (nrRows + 1); y++) {\n        var nde = { ...baseNode };\n        nde.x = 180 * x;\n        nde.y = (60 * nrRows) + (50 * y);\n        nde[\"name_conf_a\"] = \"pixel_\" + x + \"_x_\" + y\n        nde.pos_x = x;\n        nde.pos_y = y;\n        nde.material = materials[x % 2][y % 2]\n        nde.id = RED.util.generateId()\n\n        var scaler = { ...valueScaler }\n        scaler.id = RED.util.generateId()\n        scaler.x = 180 * x;\n        scaler.y = 50 * y;\n        scaler.wires = [[nde.id]]\n\n        rowRouters[x-1].wires[y-1].push(scaler.id)\n        junctionToAll.wires[0].push(nde.id)\n\n        nodes.push(scaler)\n        nodes.push(nde)\n    }\n}\n\nvar ary = [\n    junctionToAll,\n    colRouter\n];\n\n// @ts-ignore\nmsg.payload = nodes.concat(ary).concat(rowRouters);\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":394.1426086425781,"y":138.57132053375244,"wires":[["e522af90c2ebba47","b9e6005780e0386f"]]},{"id":"3b6cf5336219131e","type":"inject","z":"73b4aedc.e9602","name":"number of: columns and rows","props":[{"p":"nrCols","v":"11","vt":"num"},{"p":"nrRows","v":"5","vt":"num"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":170.42828369140625,"y":86.71411323547363,"wires":[["6030f1e36ecbc405"]]},{"id":"e522af90c2ebba47","type":"debug","z":"73b4aedc.e9602","name":"debug 6","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":710.5711097717285,"y":77.99979782104492,"wires":[]},{"id":"b9e6005780e0386f","type":"json","z":"73b4aedc.e9602","name":"","property":"payload","action":"str","pretty":false,"x":625.8567047119141,"y":175.2856330871582,"wires":[["740780e56a0cf863"]]}]

This flow will create a bunch of nodes for creating a 3d ticker wall using babylonjs in Node-RED. With that flow, I say how many rows and how many columns, and the flow will give me a flows.json that I can import that will create the corresponding Node-RED nodes. It's like using eval in JS whereby the string being evaluated was generated by the same codebase doing the eval.

If a language can do that, then it's got the abstraction levels/layers just right :wink:

1 Like