ChromaMode

Browser-based visual node editor + language for creating generative art

ChromaMode is a browser-based node editor for creating 3D/2D generative art. Under the hood there is a full programming language compiler running a special type checker that tracks contextual information to reduce the complexity for the user.

Node graph editors are good for very visual programming since the feedback loop is very quick. But there are some downsides compared to writing code in a text format. Most importantly is allowing the user to create abstractions. In textual code it's very easy to create a function or variable to abstract some code, or create a for-loop that iterates over multiple values. I'm trying to bridge the gap to make visual coding more viable.

Fields

I first heard about fields in the context of node graphs in the 3D modelling software Blender. In Blender they introduced a new way of creating prodecural geometry in their node editor called Geometry Nodes. Geometry Nodes uses a mathematical concept called 'fields' as a way of composing behaviour. Traditionally in node-graphs it is difficult to provide something like a for-loop or function call, a lot of editors will have some way of inserting a callable subgraph to provide some reusable behaviour but this gets unweidly to compose. When producing procedural geometry, the user wants to control the calculation for different vertices. They can create a graph that performs a calculation across all vertices at once using a 'field' of values.

Instead of a node representing a transformation from one value to another, it represents a transformation from one field to another field. The field holds a context which is all the contextual parameters the graph refers to, and then this graph can be executed any number of times with different contextual parameters. It's a bit like composing functions together except the functions all have the same context parameter.

I realised this can be made to go further than just geometry, you can produce a field of over the pixels on a screen to create a shader, or a field over time to create an animation or music. These can be composed together to bring all sorts of ideas under one graph network.

This opens up a lot of possibility for code reuse and algorithms that work across many values at once, which has been traditionally a bit hard in node graphs. In ChromaMode there is a Repeat node that can repeat the entire input with an index context variable. We can then be execute the graph on the GPU or CPU by compiling into straight forward GLSL/Javascript/WebAssembly code, where repeat nodes compile to for-loops.

Unified representation

There already exists shader node graphs onling for creating GPU shaders but ChromaMade goes one step further an unifies the representation of shader code and CPU code into the same thing. This is done transparently and extensive type checking can correctly figure out where the calculation must be compiled in to.

For example a math expression that refers to a UV coordinate must be calculated inside the fragment shader, but an expression that only uses the current time can be calculated on the CPU side and passed in as a uniform value. This is all transparent to the user so they don't have to worry about different code representations, and type checking inserts code implicitly to do the transformations and pass the values.

The unified structure means abstractions can be made in the standard library or user code that cross the boundaries of CPU and GPU code. For example a Bloom post processing network can be made to do filtering on the GPU and will emit code for multiple shader passes. Again this is handled all transparently without user thinking about it.

User experience

I've tried to improve on aspects that frustrate me about other node graph editors. The major one is that sometimes it takes up a lot of physical space for simple calculations. If the user was writing traditional code it may be a very short expression, but some graph editors require each operation to be an individual node. This draws attention away from the important parts of the algorithm. For this reason I have a simple Math node that allows any arbitrary expression which will get compiled and typechecked as usual. Any variables used in the expression will be converted to inputs of the node.

Another thing that keeps the the node graph simpler is inlining constant variables inside the node so the user doesn't have to create a variable node for every single value. The user can link in a value, or drag a value directly inside the node.

Screenshots