Scripting nodes

Lua, Python and two live-coded canvases, for logic an Expression cannot carry.

An Expression covers short per-element formulas. A scripting node covers loops, helper functions, file access, and generating a whole image from code.

Each one takes code typed into it or wired in from upstream, and runs it every frame, returning values or an image usable further down the graph.

Choosing a node

TaskNode
Fast, lightweight per-frame logic and mathsLua (Lua)
Python with libraries, files, or shell commandsScript (Python)
A generative drawing canvas (Processing-style)p5.js (p5.js)
Live-coded, ever-shifting video texturesHydra (Hydra)

The first two return numbers. The last two return an image, a colour field that feeds materials, post effects, or a composite like any other layer.

Lua

The quickest scripting node. Write an execute function that receives the inputs and a context and returns a table of outputs.

function execute(inputs, ctx)
  return { out_0 = inputs.in_0 * 2 }
end

Four number inputs (in_0in_3), four number outputs (out_0out_3). The ctx table carries ctx.frame, ctx.time, ctx.fps, ctx.bpm and ctx.is_playing. An output the script does not set comes out as 0.

Lua is the one scripting node with genuinely private memory: each Lua node gets its own interpreter, so a variable left at module scope persists across frames and two copies of a subgraph stay independent.

ParameterTypeDefaultWhat it does
codeString"function execute(inputs, ct..."Lua code. Define: function execute(inputs, ctx) return {out_0 = ...} end
in_0NumberInput 0
in_1NumberInput 1
in_2NumberInput 2
in_3NumberInput 3

Python

For heavier work. Write an execute function that returns a dictionary.

def execute(inputs, cx):
    value = inputs.get("in_0", 0.0)
    return {"out_0": value}

input_count runs 0 to 8 and output_count 1 to 4, with the extra ports appearing as the counts are raised. The cx dictionary carries frame, time, timeline_frame, canvas_width, canvas_height and is_playing, which is a different set from Lua's.

allow_shell unlocks a shell(command, timeout=30) helper returning a dictionary of stdout, stderr and returncode.

Two Python nodes carrying identical code share one compiled module, and therefore one set of globals. Give them different code, or keep the state on the graph, if they need to stay independent.

Python can reach your files and run programs, so it, and the shell helper in particular, is gated by trust. In an untrusted project the Python node refuses to run until trust is granted. Only trust .dna files from people you know.

ParameterTypeDefaultWhat it does
codeString"def execute(inputs, cx): ..."
input_countNumber1Number of input ports
output_countNumber1Number of output ports
allow_shellBooleanfalseEnable shell() helper for subprocess execution
in_0Number
in_1Number
in_2Number
in_3Number
in_4Number
in_5Number
in_6Number
in_7Number

p5.js

A creative-coding sketch that renders to an image. Define p.draw, and p.setup if needed, as in Processing.

p.draw = function() {
  background(0);
  fill(255);
  circle(width/2, height/2, sin(getTime()) * 100 + 150);
}

Set the canvas width and height, and animate with getTime() or getFrame(). p.setup runs once when the code changes; p.draw runs every frame. The node outputs a colour image that goes anywhere a raster goes: a material, a post effect, or straight onto a layer.

ParameterTypeDefaultWhat it does
codeString"p.draw = function() { bac..."p5.js sketch code (define p.setup and p.draw)
widthNumber512Canvas width in pixels
heightNumber512Canvas height in pixels
opacityNumber1Layer opacity (0 = transparent, 1 = opaque). Editable, wirable, keyframable.

Hydra

A visual synth. Short chainable expressions become continuously morphing video.

osc(10, 0.1, 1.2).out(o0)

Set the output width and height and the node renders a live colour image for backgrounds, textures and live visuals. Editing the code while it runs updates the picture.

Both hand back a real raster at the width and height set on the node, so a filter or a material downstream works at that size rather than a resampled stand-in.

p5.js and Hydra run their sketches in an embedded browser engine, so both need the desktop app and a moment to warm up on first use. The first frame comes back as a placeholder while the browser starts. Both also fetch their library over the network on first load, so an offline machine gets no image. Two nodes of the same kind set to the same resolution share one browser and therefore one sketch: change the width or height to separate them.

ParameterTypeDefaultWhat it does
codeString"osc(10, 0.1, 1.2).out(o0)"Hydra pattern code (e.g. osc(10,0.1).out(o0))
widthNumber512Output width in pixels
heightNumber512Output height in pixels
opacityNumber1Layer opacity (0 = transparent, 1 = opaque). Editable, wirable, keyframable.

Across all scripting nodes

The code field can be wired from upstream, so a text node or a file can drive the script without retyping it. All four count as scripting for trust and carry the lock badge, though Python is the one that refuses to run without it.

Use an Expression first for short formulas and promote to a scripting node when the structure needs it. Decompose also turns many nodes into editable Expressions to read.

See also