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
| Task | Node |
|---|---|
| Fast, lightweight per-frame logic and maths | Lua (Lua) |
| Python with libraries, files, or shell commands | Script (Python) |
| A generative drawing canvas (Processing-style) | p5.js (p5.js) |
| Live-coded, ever-shifting video textures | Hydra (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_0…in_3), four number outputs (out_0…out_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.
| Parameter | Type | Default | What it does |
|---|---|---|---|
code | String | "function execute(inputs, ct..." | Lua code. Define: function execute(inputs, ctx) return {out_0 = ...} end |
in_0 | Number | — | Input 0 |
in_1 | Number | — | Input 1 |
in_2 | Number | — | Input 2 |
in_3 | Number | — | Input 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.
| Parameter | Type | Default | What it does |
|---|---|---|---|
code | String | "def execute(inputs, cx): ..." | — |
input_count | Number | 1 | Number of input ports |
output_count | Number | 1 | Number of output ports |
allow_shell | Boolean | false | Enable shell() helper for subprocess execution |
in_0 | Number | — | — |
in_1 | Number | — | — |
in_2 | Number | — | — |
in_3 | Number | — | — |
in_4 | Number | — | — |
in_5 | Number | — | — |
in_6 | Number | — | — |
in_7 | Number | — | — |
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.
| Parameter | Type | Default | What it does |
|---|---|---|---|
code | String | "p.draw = function() { bac..." | p5.js sketch code (define p.setup and p.draw) |
width | Number | 512 | Canvas width in pixels |
height | Number | 512 | Canvas height in pixels |
opacity | Number | 1 | Layer 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.
| Parameter | Type | Default | What it does |
|---|---|---|---|
code | String | "osc(10, 0.1, 1.2).out(o0)" | Hydra pattern code (e.g. osc(10,0.1).out(o0)) |
width | Number | 512 | Output width in pixels |
height | Number | 512 | Output height in pixels |
opacity | Number | 1 | Layer 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.