procNode — custom procedural nodes as content
A .procNode is a procedural operation authored as content: a folder whose init.luau returns a def table. When the asset registers, its def joins the proc op registry keyed by the asset's stable GUID,…
MyNode.procNode/
init.luau -- returns the def table
README.md
.metadata
Two def kinds
Op-style — a custom operation
init.luau returns an op def: typed input sockets, params, typed output
sockets, and an eval that produces the outputs.
return {
kind = "op",
description = "Scale a Geometry uniformly.",
inputs = { geometry = { type = "Geometry" } },
params = { factor = { type = "Float", default = 2.0 } },
outputs = { geometry = { type = "Geometry", default = true } },
-- eval(ctx, inputs, params) -> outputs table.
-- ctx carries seed / nodeId / compositePath / hash / checkpoint.
eval = function(ctx, inputs, params)
return { geometry = inputs.geometry:scaled(params.factor) }
end,
}
The def's version is derived from the source text on every registration, so
editing init.luau re-keys the node caches of every graph that reaches this
node — downstream cooks refresh automatically.
init.luau is compiled from its current source on each registration, so any
module it pulls in uses an absolute require (require("@builtin::…")).
Subgraph-style — a .procGraph graph used as a node
init.luau returns a def bound to a .procGraph graph asset. The graph's declared
inputs become the node's params; its declared outputs become the node's
outputs. Evaluating the node evaluates the graph.
return {
kind = "graph",
-- A `.procGraph` asset ref (resolve it by guid, not by name).
graph = asset.resolve("<graph-guid>", "procGraph"),
}
A .procGraph graph can also be used as a node directly, without a wrapping
procNode, by passing its asset ref to Graph.node:
local g = proc.graph("parent")
local childRef = asset.resolve("<graph-guid>", "procGraph")
g:node("sub", childRef):param("radius", 3)
Either way, referencing a graph as a node persists a typed GUID ref in the
parent's graph.json, so the referenced asset travels with the parent as a
dependency (it appears in asset.deps). A graph that reaches itself — directly
or transitively — errors loudly at eval rather than recursing without bound.
Registration is push-model
The registry is populated by this type's lifecycle hooks: onRegister
registers instances present at load, onChange re-registers + hot-reloads on
edit, onDelete unregisters. A graph that references a procNode which is not
registered errors loudly at load/eval, naming the missing GUID — there is no
name-probing fallback.