This is a series of posts on building imgplex, best read in order:
Part 1 - The why, what, and how of imgplex
Part 2 - Getting things up and running
Part 3 - The node definition system
Part 4 - Executing the node graph, making it fast
Part 5 - Two graphs in one
Part 6 - Multiple inputs and outputs, processing images as sets
Part 7 - Profiling and Optimization
Two graphs in one
The canvas actually contains two kinds of computation. Image nodes build the ImageMagick processing pipeline, while value nodes calculate numbers, strings, booleans, and other values used by node parameters. Part 4 focused mainly on the image-processing side. This post covers the value side and how the two interact.
This is similar to Substance Designer or Blender geometry nodes, where data-processing nodes and ordinary value nodes share the same graph. In imgplex, image and mask ports belong to the processing graph. Math, logic, constants, and similar nodes are evaluated in JavaScript and do not launch ImageMagick. Typed ports allow both kinds of nodes to coexist on the same canvas.
Typed wires
Every port has a type, and the editor rejects incompatible connections, self-connections, and connections that would introduce a cycle. The behavior is similar to the connection validation in a shader graph.
- image - orange (the main pixel pipeline)
- mask - purple (a greyscale image used as a mask)
- path - mint green (a filesystem path)
- number - cyan
- string - green
- bool - yellow
- color - pink
- vector2 / vector3 / vector4 - amber / indigo / teal
The preview wire uses the source port’s color while it is being dragged, and established connections retain that color. This made it easier to trace value types through larger graphs. I checked the palette against the dark canvas background and adjusted colors that were difficult to distinguish.
The pure-value graph
Pure-value nodes have no image or mask ports. Their inputs and outputs carry ordinary values such as numbers, strings, booleans, colors, or vectors.
- Value nodes - value nodes provide constants such as numbers and colors.
- Math nodes - Add, Subtract, Multiply, Divide, Power, Lerp. Ordinary arithmetic on wired-in values.
- Vector nodes - build a vector from components, split one back apart, dot product, length, normalize.
- Logic nodes - AND, OR, NOT, comparisons, and a Branch node that picks between two values based on a condition.
- Properties nodes - these read a fact about the current image (its width, height, name, file size, bit depth) and output it as a value.
Property nodes are the main point where the current image influences value evaluation. They read information such as width or filename and expose it as an ordinary number or string.
How the two graphs connect
The connection point is parameters. Every editable parameter on an image node - the
sigma on a Blur, the angle on a Rotate - exposes an input handle on its left
side. That handle accepts a wire from any compatible value output.
So you can do things like: read an image’s width with a Dimensions node, halve it with a Math node, and wire the result straight into a Crop node’s parameter. The resulting crop size is calculated separately for each image rather than stored as a fixed value.
Before running an image node, the engine resolves the value nodes feeding its parameters. The image executor therefore receives ordinary values without needing to understand the chain that produced them.
When command templates are not enough
My first node definitions only supported command_template: a fixed ImageMagick
argument string with placeholders.
Placeholder substitution was not enough for every node. Some operations needed to omit arguments conditionally, format several parameters into one argument, or calculate a value without invoking ImageMagick. I added two executable definition fields for those cases.
Most image nodes still use command_template, which performs ordinary placeholder
substitution.
Nodes that need conditional or calculated arguments can instead provide command_js.
The snippet receives resolved parameters and returns the ImageMagick argument array.
Pure-value nodes use compute_js, which returns output values rather than command
arguments.
For nodes that fit either of these execution models, the logic can remain inside the JSON definition and does not require a new TypeScript executor. The loader treats these definitions the same way as ordinary template-based nodes.
These snippets create an important trust boundary. command_js and compute_js
execute in the main process, where they can access Node.js APIs. Node definitions
must therefore be treated as installed executable extensions, not as ordinary
workflow data. Because the snippets run with main-process privileges, imgplex only
loads them from trusted node-definition files.
Workflow files are treated differently because they are designed to be shared. They can reference installed node types and store parameter values, but they cannot contain executable JavaScript. I will cover the workflow-loading and validation rules in a later post.
Some nodes still require dedicated TypeScript executors. Channel Split has several
image outputs, and nodes whose port layout changes with configuration cannot
currently be represented by the JSON schema. Other cases that initially appeared to
need custom executors could be handled with command_js.
Conditional controls and search aliases
The node definitions also control parts of the Inspector UI and search behavior.
-
Definitions can include visibility rules for Inspector controls. For example, a Resize node can show a percentage slider in percentage mode and a pixel field in pixel mode. I originally handled cases like this inside individual components, but moving the rules into JSON allowed the shared Inspector to handle them.
-
Definitions can also include search aliases. This lets a search for “sharpen” or “blur” return relevant nodes even when those terms are not part of the displayed name.
What the separation enabled
Separating value evaluation from image execution kept parameter logic out of the individual ImageMagick nodes. A processing node receives resolved values without needing to know whether they came from a constant, an image property, or a chain of math and logic nodes.
The same separation also supports the web version of imgplex. Graph construction and value-node logic can run in JavaScript, while the desktop backend is only needed when the workflow processes actual images.
Next post in this series: Building imgplex: part 6 - Multiple inputs and outputs, processing images as sets