Hand-written Luau✎
The two nodes that take code, what they emit, and why there are only two.
Verified
Not everything is worth wiring. A regular expression, a table literal, a bit of maths you already have — these are shorter as code, and Roswaal has two nodes that take it.
Which one, and why
The difference is where the code lands in the generated file, not how long it is. It is the one thing about these two nodes that is worth getting straight, because everything else follows from it.
| Custom Code | Luau Expression | |
|---|---|---|
| Lands where | a statement goes | a value goes |
| Execution pins | Yes — it is a step in the flow | None. It is pure |
| Hands a value back | No output pin | Its output, wired anywhere |
| Length | As many statements as you like | One expression, however many lines that takes |
| Reach for it when | you are doing something | you are computing something |
Custom Code
local hits = 0
hits += 1
print(hits)local hits = 0 hits += 1 print(hits) print("Done")
Luau Expression
print(os.clock() * 2)
Danger
local x = 1 there would emit print(local x = 1), so it is refused before the file is written. If it would not fit inside brackets, use Custom Code.Custom Code has no output pin, so it cannot hand a value onward. To get one out, write to a script variable, or use Declare Local before it and assign in the code — the completion list will offer that local by name.
Both pins are typed luau rather than string, and clicking one opens a real editor: Luau highlighting, the parser the build runs marking a mistake as you type, and completion over Luau's globals and the names this graph puts in scope.
What is in scope
Completion offers the locals a Custom Code block can actually see: the script's variables, any local declared upstream in the same block, and a function's parameters when the block is inside one. A local declared in a sibling branch is not offered, because it does not exist there.
Tip
There are exactly two
Tip
That guarantee is the point, and it is why a code pin has its own type. Plenty of ordinary pins default to a raw Luau constant because their type has no literal form — a Vector3 input cannot sensibly default to nil. If every one of those opened a code editor, a graph shared with you could hide arbitrary code inside a node whose title says Look At, and reviewing it would mean opening every pin rather than scanning for two node names.
To change a constant on an ordinary pin, wire a node into it or split it into its components. Both got considerably easier than they were.
When to reach for something else
- A missing node. Write it as a node pack instead — declarative, documented automatically, and reusable across graphs. Custom Code is a one-off.
- A whole system. Put it in a ModuleScript and use Require Module and Call Function. Roswaal is happy to call into Luau it did not write.
- Something you cannot express. Say so — a gap in the node library is worth filing, and this month several were closed that way.
Info
Last reviewed 11 September 2026 by @neopolitans