Try it in your browserpreview Source

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 CodeLuau Expression
Lands wherea statement goesa value goes
Execution pinsYes — it is a step in the flowNone. It is pure
Hands a value backNo output pinIts output, wired anywhere
LengthAs many statements as you likeOne expression, however many lines that takes
Reach for it whenyou are doing somethingyou are computing something
Custom Code-- your Luau hereCode
Luau Expression0Code
The shape says which is which before you read the title: Custom Code has execution pins and no output, Luau Expression has an output and no execution pins.

Custom Code

Script StartCustom Codelocal hits = 0CodePrintDoneValue
A step in the flow. Its statements run in order, and whatever is wired after it runs next.
luau
local hits = 0
hits += 1
print(hits)
print("Done")

Luau Expression

Script StartLuau Expressionos.clock() * 2CodePrintValue
A value. Its text is written where the value is used — here, inside the Print.
luau
print(os.clock() * 2)

Danger

Statements do not go in a Luau Expression. 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

Completion reads the code you are typing: its own locals, and the parameters and loop variables around the cursor, are offered ahead of the graph's names.

There are exactly two

Tip

Those two nodes are the only places hand-written Luau enters a graph. Every other pin shows its constant and will not take code.

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

Code in these nodes is parsed before the file is written: a syntax mistake stops the build and points at its line. Names and types are not checked — that is still Luau's job, in Studio.

Last reviewed 11 September 2026 by @neopolitans