# Standalone visual comparison of flows - in browser

**URL:** <https://discourse.nodered.org/t/standalone-visual-comparison-of-flows-in-browser/80021>\
**Category:** Share Your Projects\
**Created:** [23 July 2023 12:30 UTC](https://discourse.nodered.org/t/standalone-visual-comparison-of-flows-in-browser/80021 "2023-07-23T12:30:56Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [23 July 2023 12:30 UTC](https://discourse.nodered.org/t/standalone-visual-comparison-of-flows-in-browser/80021/1 "2023-07-23T12:30:56Z")

</div>

Hi There!

I've created a little tool for comparing flows visually, for [example](https://cdn.openmindmap.org/compare?flow1=W3siaWQiOiJhNTAyZmQ0NzE5ZjQzZTU2IiwidHlwZSI6Imdyb3VwIiwieiI6IjM1ODY5MjJjYjcwOGEyOWMiLCJuYW1lIjoicmV0cmlldmUgc3BlY2lmaWMgY29tbWl0IGRldGFpbHMiLCJzdHlsZSI6eyJsYWJlbCI6dHJ1ZX0sIm5vZGVzIjpbIjc2ZmQ1MDZhOWU3YTFlYTYiLCJiNzdkYjE3ZjY0Nzc4NWQ2IiwiZDU0OGQ5NTlkMWM5NjA4NSIsImM2M2E4NjhiY2ZmODc1NDQiXSwieCI6Nzk4LCJ5Ijo1NzQsInciOjQ2NiwiaCI6NDg5fSx7ImlkIjoiNzZmZDUwNmE5ZTdhMWVhNiIsInR5cGUiOiJmdW5jdGlvbiIsInoiOiIzNTg2OTIyY2I3MDhhMjljIiwiZyI6ImE1MDJmZDQ3MTlmNDNlNTYiLCJuYW1lIjoiaGFuZGxlIHRoZSBxdWVyeSAnYycgLSBjb21taXQgbGlzdCIsImZ1bmMiOiJtc2cuc2hhc1RvT2J0YWluID0gbXNnLnJlcS5xdWVyeS5jLnNwbGl0KCcsJyk7XG5cbm1zZy5wYXlsb2FkID0gbXNnLnNoYXNUb09idGFpbi5wb3AoKTtcblxucmV0dXJuIG1zZzsiLCJvdXRwdXRzIjoxLCJub2VyciI6MCwiaW5pdGlhbGl6ZSI6IiIsImZpbmFsaXplIjoiIiwibGlicyI6W10sIngiOjk2NCwieSI6NjE1LCJ3aXJlcyI6W1siYjc3ZGIxN2Y2NDc3ODVkNiJdXX0seyJpZCI6ImI3N2RiMTdmNjQ3Nzg1ZDYiLCJ0eXBlIjoibGluayBjYWxsIiwieiI6IjM1ODY5MjJjYjcwOGEyOWMiLCJnIjoiYTUwMmZkNDcxOWY0M2U1NiIsIm5hbWUiOiIiLCJsaW5rcyI6WyJkYmRlYTUzOTY2ZTcyMDMxIl0sImxpbmtUeXBlIjoic3RhdGljIiwidGltZW91dCI6IjMwIiwieCI6MTA0MywieSI6ODc2LCJ3aXJlcyI6W1siZDU0OGQ5NTlkMWM5NjA4NSJdXX0seyJpZCI6ImQ1NDhkOTU5ZDFjOTYwODUiLCJ0eXBlIjoiZnVuY3Rpb24iLCJ6IjoiMzU4NjkyMmNiNzA4YTI5YyIsImciOiJhNTAyZmQ0NzE5ZjQzZTU2IiwibmFtZSI6InB1c2ggY29tbWl0IG9udG8gc3RhY2sgYW5kIHNldCBuZXh0IGNvbW1pdCBzaGEiLCJmdW5jIjoibXNnLmNvbW1pdHMucHVzaCh7Li4ubXNnLnBheWxvYWR9KVxuXG5tc2cucGF5bG9hZCA9IG1zZy5zaGFzVG9PYnRhaW4ucG9wKClcblxuXG5yZXR1cm4gbXNnOyIsIm91dHB1dHMiOjEsIm5vZXJyIjowLCJpbml0aWFsaXplIjoiIiwiZmluYWxpemUiOiIiLCJsaWJzIjpbXSwieCI6MTA0OCwieSI6Njk4LCJ3aXJlcyI6W1siYzYzYTg2OGJjZmY4NzU0NCJdXX0seyJpZCI6ImM2M2E4NjhiY2ZmODc1NDQiLCJ0eXBlIjoic3dpdGNoIiwieiI6IjM1ODY5MjJjYjcwOGEyOWMiLCJnIjoiYTUwMmZkNDcxOWY0M2U1NiIsIm5hbWUiOiJpcyBsb29wIGRvbmU/IiwicHJvcGVydHkiOiJwYXlsb2FkIiwicHJvcGVydHlUeXBlIjoibXNnIiwicnVsZXMiOlt7InQiOiJudWxsIn0seyJ0IjoiZWxzZSJ9XSwiY2hlY2thbGwiOiJmYWxzZSIsInJlcGFpciI6ZmFsc2UsIm91dHB1dHMiOjIsIngiOjEwNTMsInkiOjEwMjIsIndpcmVzIjpbWyI3N2RhMDNjYWVlZmExMTUxIl0sWyJiNzdkYjE3ZjY0Nzc4NWQ2Il1dfV0=&flow2=W3siaWQiOiJhNTAyZmQ0NzE5ZjQzZTU2IiwidHlwZSI6Imdyb3VwIiwieiI6IjM1ODY5MjJjYjcwOGEyOWMiLCJuYW1lIjoicmV0cmlldmUgc3BlY2lmaWMgY29tbWl0IGRldGFpbHMiLCJzdHlsZSI6eyJsYWJlbCI6dHJ1ZX0sIm5vZGVzIjpbIjc2ZmQ1MDZhOWU3YTFlYTYiLCJiNzdkYjE3ZjY0Nzc4NWQ2IiwiZDU0OGQ5NTlkMWM5NjA4NSIsImM2M2E4NjhiY2ZmODc1NDQiXSwieCI6Nzk4LCJ5Ijo1NzQsInciOjM3MSwiaCI6NDg5fSx7ImlkIjoiNzZmZDUwNmE5ZTdhMWVhNiIsInR5cGUiOiJmdW5jdGlvbiIsInoiOiIzNTg2OTIyY2I3MDhhMjljIiwiZyI6ImE1MDJmZDQ3MTlmNDNlNTYiLCJuYW1lIjoiY2hhbmdlZCBzb21ldGhpbmciLCJmdW5jIjoibXNnLnNoYXNUb09idGFpbiA9IG1zZy5yZXEucXVlcnkuYy5zcGxpdCgnLCcpO1xuXG5tc2cucGF5bG9hZCA9IG1zZy5zaGFzVG9PYnRhaW4ucG9wKCk7XG5cbnJldHVybiBtc2c7Iiwib3V0cHV0cyI6MSwibm9lcnIiOjAsImluaXRpYWxpemUiOiIiLCJmaW5hbGl6ZSI6IiIsImxpYnMiOltdLCJ4Ijo5MjQsInkiOjYxNSwid2lyZXMiOltbImI3N2RiMTdmNjQ3Nzg1ZDYiXV19LHsiaWQiOiJiNzdkYjE3ZjY0Nzc4NWQ2IiwidHlwZSI6ImxpbmsgY2FsbCIsInoiOiIzNTg2OTIyY2I3MDhhMjljIiwiZyI6ImE1MDJmZDQ3MTlmNDNlNTYiLCJuYW1lIjoiYW5kIG1vdmUgbW9yZSBjaGFuZ2UiLCJsaW5rcyI6WyJkYmRlYTUzOTY2ZTcyMDMxIl0sImxpbmtUeXBlIjoic3RhdGljIiwidGltZW91dCI6IjMwIiwieCI6MTAzMywieSI6ODc2LCJ3aXJlcyI6W1siZDU0OGQ5NTlkMWM5NjA4NSJdXX0seyJpZCI6ImQ1NDhkOTU5ZDFjOTYwODUiLCJ0eXBlIjoiZnVuY3Rpb24iLCJ6IjoiMzU4NjkyMmNiNzA4YTI5YyIsImciOiJhNTAyZmQ0NzE5ZjQzZTU2IiwibmFtZSI6ImNoYW5nZWQgc29tZSBtb3JlIiwiZnVuYyI6Im1zZy5jb21taXRzLnB1c2goey4uLm1zZy5wYXlsb2FkfSlcblxubXNnLnBheWxvYWQgPSBtc2cuc2hhc1RvT2J0YWluLnBvcCgpXG5cblxucmV0dXJuIG1zZzsiLCJvdXRwdXRzIjoxLCJub2VyciI6MCwiaW5pdGlhbGl6ZSI6IiIsImZpbmFsaXplIjoiIiwibGlicyI6W10sIngiOjk1OCwieSI6Njk4LCJ3aXJlcyI6W1siYzYzYTg2OGJjZmY4NzU0NCJdXX0seyJpZCI6ImM2M2E4NjhiY2ZmODc1NDQiLCJ0eXBlIjoic3dpdGNoIiwieiI6IjM1ODY5MjJjYjcwOGEyOWMiLCJnIjoiYTUwMmZkNDcxOWY0M2U1NiIsIm5hbWUiOiJpcyBsb29wIGRvbmU/IiwicHJvcGVydHkiOiJwYXlsb2FkIiwicHJvcGVydHlUeXBlIjoibXNnIiwicnVsZXMiOlt7InQiOiJudWxsIn0seyJ0IjoiZWxzZSJ9XSwiY2hlY2thbGwiOiJmYWxzZSIsInJlcGFpciI6ZmFsc2UsIm91dHB1dHMiOjIsIngiOjEwNTMsInkiOjEwMjIsIndpcmVzIjpbWyI3N2RhMDNjYWVlZmExMTUxIl0sWyJiNzdkYjE3ZjY0Nzc4NWQ2Il1dfV0=)

To create your own visualisations, go to [Comparing Flows](https://cdn.openmindmap.org/compare) and paste your two flows into the textboxes.

You can create link with the flows BUT most urls become too long and the server complains - your mileage will vary (the above link is under that limit).

The tool has various colours for nodes: black/grey means a node was removed, green it was added, yellowish/green means it was moved and purple means it was changed - clicking on the nodes sometimes brings up a diff popup with the changes (depending on the what happened to the node). Use the slider to transition between the two versions of the flow. If it's a large flow.json file, the the selector will be filled with the various flow tabs. As seen over [here](https://backup.openmindmap.org/?v2=69bcd0c1a3c5c6f0694e7f476482bb11beb1e9e5&v1=270448f680cbded2b52fa0d36f9c12b1af55724c&flid=3586922cb708a29c).

Various bugs exist but it's up to the user to find them! 🙂 Please provide feedback and suggestions, this is just a proof of concept implementation!

The tool is completely running in the browser, no server involved. Permissions is given to steal, copy and modify BUT don't do any evil. Attribution is also always a kind gesture.

Cheers!

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [23 July 2023 12:47 UTC](https://discourse.nodered.org/t/standalone-visual-comparison-of-flows-in-browser/80021/2 "2023-07-23T12:47:39Z")

</div>

Great stuff - love seeing your progress.

Now you have this in a re-usable form outside of node-red, it would be great if it were an NPM module that others could import. Bonus points for a CDN.

And I havent forgotten about seeing if I can populate the node catalog with icon and colour info (just havent got around to it)

--

PS: apart from visualising the node, text and position, is the diff supposed to highlight things like changed IDs or props?

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [23 July 2023 14:52 UTC](https://discourse.nodered.org/t/standalone-visual-comparison-of-flows-in-browser/80021/3 "2023-07-23T14:52:44Z")

</div>

> [@Steve-Mcl](#):
>
> Great stuff - love seeing your progress.

Thanks Steve, it's been fun work using Node-RED for doing this.

> [@Steve-Mcl](#):
>
> Now you have this in a re-usable form outside of node-red, it would be great if it were an NPM module that others could import.

Can you point me in the direction of an example NPM package that does what you describe? I'm don't have many NPM experiences and I certainly don't know the best practices when it comes to creating packages.

> [@Steve-Mcl](#):
>
> Bonus points for a CDN.

That's just GitHub - [GitHub - gorenje/cdn.openmindmap.org: image content and discussions for blog.openmindmap.org](https://github.com/gorenje/cdn.openmindmap.org) - that was my [GitHub flow](https://discourse.nodered.org/t/github-commit-via-api-flow/79899), i.e. the reason I built it - so that I could get a cdn for free! 😉

> [@Steve-Mcl](#):
>
> PS: apart from visualising the node, text and position, is the diff supposed to highlight things like changed IDs or props?

IDs would become additions/deletions, props are compared and become changed node i.e. purple in colour. I use the [same comparison code](https://github.com/gorenje/cdn.openmindmap.org/blob/7dfcc123f25d7b992b10f6a3c97ccb3114c31ddb/compare.html#L470-L587) that Node-RED uses with some extensions though.

**Edit** : Apropo prop change, this [example](https://backup.openmindmap.org/?v1=bbcd0a76e5fd7626ac929d728e0b69ae065c71a4&v2=1589ceea613880dcb994251502503590f7d03095&flid=3b1289d7ccf9cb0f) is one -- click on the purple node "Git commit - Press Here" and you'll see a prop change.

 ![Screen Shot 2023-07-23 at 16.58.28](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/3/1/3192a478d6aee1040a407e96f45a9eff316216b7.png)

(ignore the don't press here notice)

Prop comparison is done because of [this](https://github.com/gorenje/cdn.openmindmap.org/blob/7dfcc123f25d7b992b10f6a3c97ccb3114c31ddb/compare.html#L310-L314) else it was always `[object Object]`.

**Edit2** : for those interested in the codebase, it's once again a [Node-RED flow](https://demo.openmindmap.org/omm/#flow/3586922cb708a29c).

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [23 July 2023 15:34 UTC](https://discourse.nodered.org/t/standalone-visual-comparison-of-flows-in-browser/80021/4 "2023-07-23T15:34:14Z")

</div>

> [@gregorius](#):
>
> Can you point me in the direction of an example NPM package that does what you describe? I'm don't have many NPM experiences and I certainly don't know the best practices when it comes to creating packages.

WARNING: Probably a lot of stuff you already know so my apologies for teaching you to suck eggs.

So, forgetting ESM or Deno for a moment, using your viewer in a run of the mill project would typically be imported via npm e.g. `const gregsViewer = required('name-of-your-package')` so publishing to NPM is a great way to get this out there.

Similarly, using this actually in node-red, a user can import (`require`) your module by entering it in the setup tab of a function node. This would enable a user to import this & use it on (for example) a dashboard, uibuilder, pdfMake, a straight endpoint, or wherever!

Something like

```auto
// const const gregsViewer = required('name-of-your-package') // imported via setup tab
msg.payload = gregsViewer.flowToSVG(msg.payload)
return msg; // return the SVG generated by gregsViewer

```

* * *

#### The tricky part - naming.

As it is not a Node-RED node package or plugin (YET 😉 ) naming can really be whatever you wish however you might want to scope it to your NPM username. e.g. `@gornje/flow2svg` or `@gornje/nr-flow-utils` or anything really.

#### The easy part - packaging.

To publish your module to NPM, you will need to package up the node.

1. Create a new directory e.g. `flow2svg`
2. `cd` to the directory
3. initialise it as a package - enter `npm init` & answer the questions...

```auto
$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (flow2svg) @gorenje/flow2svg
version: (1.0.0) 0.0.1-beta.1
description: A stand-alone utility package for visualising Node-RED flows
entry point: (index.js) flowviewer.js
test command:
git repository:
keywords: node-red,flow,viewer
author: gorenje
license: (ISC) Apache-2.0
About to write to \flow2svg\package.json:

{
  "name": "@gorenje/flow2svg",
  "version": "0.0.1-beta.1",
  "description": "A stand-alone utility package for visualising Node-RED flows",
  "main": "flowviewer.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "node-red",
    "flow",
    "viewer"
  ],
  "author": "gorenje",
  "license": "Apache-2.0"
}

Is this OK? (yes)

```

1. open the directory it in your fav editor (vscode?), drop the necessary files in
2. update the `package.json` file - add other fields e.g.
  - 

```
"repository": { "type": "git", "url": "https://github.com/..." }

```

  - 

```
"bugs": { "url": "https://github.com/.../issues" }

```

  - 

```
"engines": { "node": ">=14.x" } 

```

  - 

```
"files": ["README.md", "LICENSE", "file1.js", "file2.html"]

```

3. Publish as alpha/beta
  - `npm publish --tag beta`

4. try it in a node-red function node / ask others to try it out
5. titivate
6. bump version number to something better (e.g. `1.0.0`) and publish it for real
  - `npm publish --tag beta`

TIP: Do not include copies of JQuery or any other external packages in your package - this leads to duplication, larger packages, collisions and other nasty issues. Instead, you would include them as dependencies or peer dependencies in your `package.json`

* * *

Version Numbers. Node and NPM generally use [sematic versioning](https://docs.npmjs.com/about-semantic-versioning). Early releases are often `0.0.1` or `0.1.0` or even `1.0.0-alpha.1` - you decide, but users expect the patch/minor/major version number changes to reflect compatibility or incompatibility.

* * *

Happy to help you out if you wish - I'm around for the next hour or 2.

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [23 July 2023 16:07 UTC](https://discourse.nodered.org/t/standalone-visual-comparison-of-flows-in-browser/80021/5 "2023-07-23T16:07:40Z")

</div>

Cheers!

I would work now but gotta do some afk work... I'll have a tomorrow 👍

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [23 July 2023 16:15 UTC](https://discourse.nodered.org/t/standalone-visual-comparison-of-flows-in-browser/80021/6 "2023-07-23T16:15:13Z")

</div>

Ah, I would also set this up to be a github action of some sort, ie the code would be developed in node red, push to github into a repo and then a github action to push off to npmjs.

Commiting to github via node Red is really simple and I can avoid terminal work. Plus all the development is done remote, no local hard drive needed.

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [10 August 2023 17:11 UTC](https://discourse.nodered.org/t/standalone-visual-comparison-of-flows-in-browser/80021/7 "2023-08-10T17:11:51Z")

</div>

I had a look at doing this since I won't mind having something outside of the browser for other purposes however I began to realise that this is harder than it might appear.

The reason for this is the fact that the plugin "draws" the SVG in the browser and then depends on that rendering to compute things such as group sizes (i.e. the size of the group box to surround all members of the group). It does this by using `getBBox()` (in fact that gets used quite often) which can only work if the the SVG is already rendered in the browser (or some other headless creature).

So I had a quick look and found [JSDOM](https://github.com/jsdom/jsdom) which does quite a lot but which doesn't implement the `getBBox()` on SVG elements (or any other elements).

Hence I'm leaving this for an extremely rainy day (which in my part of the world do occur) and am moving on to other things.
