backupRoswaal has moved to roswaal.app. These pages are a backup copy.Go to roswaal.app

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.

Code BlockLuau 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
{ }Code Block-- your Luau here
ƒxLuau Expression0
The shape says which is which before you read the title: Code Block has execution pins and no output, Luau Expression has an output and no execution pins.

Code Block

Script Start{ }Code Blocklocal hits = 0hits += 1print(hits)PrintDoneValue
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 StartƒxLuau Expressionos.clock() * 2PrintValue
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 Code Block.

Code Block 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. The node shows its first six lines on its face, and clicking them opens the code in the Code panel: 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.

The Code panel

The panel opens along the foot of the graph, between the side panels, so the tree, Variables and the Inspector stay where they are while you write. Each field you open is a tab of its own in the panel's header. A tab's mark says what it edits — { } a Code Block, ƒx a Luau Expression, <T> a type written out — then comes the node's label if it has one, and the graph it is in, or its first line when two would read the same. A graph's tabs come back when you return to it.

What it does
TypingApplied to the node once you pause, and at once when you click away
Ctrl + Z in the codeThe code's own undo, a keystroke at a time
Ctrl + Z on the graphUndoes a pause's worth of typing, and the panel follows
Go to nodeSelects the node on the graph and brings it into view
Full view, or Ctrl + Shift + EnterThe code over the graph, the side panels kept. Esc or the same keys go back
Drag from VariablesWrites the name: a variable, service, module, local, function or type
Drag from the DataModelWrites the path, from the nearest name that already holds part of it
× or a middle-click on a tabCloses it

There is no Done: what you type is what the node holds. A graph that is compiling takes the change once it finishes, rather than dropping it. The panel moves like any other — drag its header to a side, onto the Inspector's header to share its card, or over the graph for a window — and its menu brings it back Along the foot.

What is in scope

Completion offers the locals a 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.

Hovering a name says what it is, with the comment above it, and a required module's members and the place's instances are known too. Reading your Luau has the detail.

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. Code Block 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