Building imgplex: part 5

game-devimgplextooling

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.

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.

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.

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