canaryThese pages document the canary: an unreleased build. What they describe may not be in the version you have, and may change before it is.The stable build
Try it in your browsercanary Source

Coming from Blueprints✎

What the thing you already know is called here, and what is genuinely missing.

Reviewed

If you have written Blueprints, most of Roswaal is already familiar and the rest is a rename. This page is the rename, plus the parts that genuinely have no counterpart.

Tip

Where there is no equivalent, this page says so — Construction Scripts, for one.

Info

Unreal, Unreal Engine and Blueprint are trademarks or registered trademarks of Epic Games, Inc. in the United States of America and elsewhere. They are used on this page to name Epic's product while explaining Roswaal's. Roswaal is not affiliated with, sponsored by, or endorsed by Epic Games, Inc., and contains no code or content from Unreal Engine.

The shape of a project

The biggest adjustment is not the nodes. A Blueprint is an asset inside the engine; a Roswaal graph is a file on disk that compiles to a Luau file next to it. Rojo syncs that into Studio. Nothing about Roswaal runs inside the engine, and the generated code is meant to be read.

In UnrealIn RoswaalNotes
Blueprint classA .nodescript fileOne graph compiles to one Script, LocalScript or ModuleScript. There is no class hierarchy: Roblox composes instances rather than subclassing them.
Event GraphThe nodescript's own graphEach function has a graph of its own, listed under the nodescript in the project tree.
Construction Script— nothing equivalent —No equivalent, and not an oversight. Roblox has no edit-time construction pass — instances are built by Rojo from files, or at runtime by your own code. Put what you would have put in a Construction Script into a node map, or into Script Start.
Blueprint Function LibraryA node packA .nodedef.json or .nodedef.luau file. Packs are data and are never executed, so depending on someone else's pack cannot run their code at author time.
Level BlueprintA Script under ServerScriptServiceThere is no per-place graph; a place is a DataModel, described by a node map.
Content BrowserThe project tree, and BeakoThe tree shows graphs and generated files side by side. Beako is the sibling tool for browsing the rest of a Roblox project.

Wiring

This part transfers almost unchanged. White execution wires, coloured data wires, pure nodes with no execution pins. The pin colours are deliberately close to Unreal's where the types line up.

In UnrealIn RoswaalNotes
Exec pinsExecution pinsSame idea, same white pins, same rule that a node is either in the line or pure. Drawn as a triangle outside the node rather than an arrow inside it.
Pure node (green)Pure node (green left edge)Same meaning: no execution wire, evaluated where it is used. Roswaal inlines a pure value with one consumer and binds it to a local with two, so it is evaluated once.
Reroute nodeRerouteDouble-click a wire. Compiles to nothing at all.
Split Struct PinSplit Struct PinRight-click a Vector2, Vector3, CFrame, Color3, UDim or UDim2 pin. Same wording, same behaviour. CFrame offers three decompositions rather than one.
Make / Break (Vector, Rotator…)Split Struct Pin, or a constructor nodeSplitting a pin replaces Make and Break for most uses. The constructor nodes are there for when you want the value as its own node.
Comment boxComment — and it reaches the fileThe same box drawn round the same nodes, with one difference worth knowing before it surprises you: Roswaal writes a comment's header into the generated Luau, above the code of the nodes it encloses. Unreal keeps a comment entirely in the editor and so does every other visual scripting tool, so this is the habit to unlearn — or to keep, with Settings → Project → Comment headers turned off. It is on by default because a generated file here is committed and read beside hand-written Luau, and explaining the same block twice is the alternative.
Promote to VariablePromote to VariableRight-click an unwired input. The variable takes the value already typed into the pin, as it does in Unreal.
Cast To <Class>Is A, then CastLuau's :: is a compile-time claim with no runtime branch, so there is no Cast Failed pin to wire. Is A is the runtime half. See Getting at the world below for the full picture.
Add pin +The + in a node's headerOr drop a wire on the node body and it grows a pin to catch it.

Flow control

Near enough one-to-one, with the Luau names where they differ.

In UnrealIn RoswaalNotes
BranchBranch
SequenceSequence
For LoopFor RangeRoblox's numeric for. First and Last are inclusive, as in Luau.
For Each LoopFor EachFor Each walks values; For Index walks index and value, Luau's ipairs.
While LoopWhile Loop
Break / ContinueBreak / Continue
Return NodeReturnLuau requires a return to be the last statement in its block, so a Sequence output that returns cannot be followed by another. Roswaal reports that rather than emitting a file that will not parse.
DelayWaitYields the thread. Marked latent, and never inlined.
Gate / MultiGate / DoOnce / FlipFlop— nothing equivalent —No equivalents. Each is a small piece of state Unreal hides inside the node; here you would hold it in a script variable and branch on it, which is what the generated Luau would have said anyway.
Timeline— nothing equivalent —No equivalent. Use TweenService, or drive a value from RunService per frame.

Data and state

Blueprints has one kind of variable. Roswaal has two, and the distinction is worth learning early because it is the one thing that is genuinely different rather than merely renamed. For the value types themselves — where FVector, FRotator and TArray land — see Types at the end of this page.

In UnrealIn RoswaalNotes
Blueprint variableScript variableDeclared in the Variables panel, read and written anywhere in the graph. Compiles to a file-level local. Drag one onto the canvas for a Get, Ctrl-drag for a Set.
Local variable (function-scoped)Declare LocalBinds a value mid-flow and only exists inside the block that declared it. Wire its output, or drag it from the Locals list for a Get Local. Reading one outside its block is an error, not silently broken code.
StructA table, or a Roblox typeLuau has no struct declaration. Vector3, CFrame, Color3 and friends are the built-in value types, and anything else is a table.
EnumRoblox Enums, as valuesThere is no user-defined enum. A string with a dropdown is the usual stand-in.
Array / Map / SetTableOne type for all three, as in Lua. There is no separate array node set, because there is no separate array.

Functions and events

Same concepts; Roblox's event model is signals rather than delegates.

In UnrealIn RoswaalNotes
FunctionFunctionIts own graph, in a tab of its own. A hoisted Function is that graph's entry node. A Declare Function sits in a flow and opens its graph on a double-click.
Call FunctionCall FunctionCall Method is the colon form, for calling a method on an instance or module.
Macro— nothing equivalent —No equivalent, but Luau has what a macro is for, and Roswaal writes it. In one script, on Roblox or Lune, a local function — or a function stored in a table — can be called anywhere below its declaration, or anywhere the table holding it is in scope. Across scripts, a ModuleScript on Roblox or a module on Lune, required from somewhere it is visible: a ModuleScript in ServerScriptService cannot be required by a client.
Custom EventA BindableEventAn instance you create, fired with Fire Bindable and listened to through its Event signal. In-process only — crossing between client and server is a remote, and deliberately a different thing to write.
Event Dispatcher / Bind EventConnect Event, Connect OnceGet Event reads a signal off an instance; Connect Event runs a body when it fires and hands the connection back. Connect Once unbinds itself after one fire.
Unbind EventDisconnectRoblox will not do this for you. A connection you never disconnect keeps its handler — and everything the handler captured — alive for as long as the signal is, which is the most common leak in a Roblox game.
Replicated function / RPCRemoteEvent and RemoteFunctionUnreal marks a function Server or Client and the engine routes it. Roblox makes the channel an instance you create and reference from both sides. A RemoteEvent is one-way and does not wait; a RemoteFunction waits for an answer and raises the other side's error on the caller. There is an UnreliableRemoteEvent for data you can afford to lose — same calls, weaker guarantees — and no unreliable equivalent for RemoteFunction, because waiting for an answer needs the answer to arrive.
BeginPlayScript StartA Roblox script runs when it loads; there is no separate begin-play phase.
TickRunService, connectedNo Tick node. Connect to RunService.Heartbeat or RenderStepped — being explicit about which is the point, since they are not interchangeable.
Interface— nothing equivalent —No equivalent. A ModuleScript exporting the expected functions is the idiom.

Getting at the world

Unreal reaches actors through references and Get All Actors Of Class. Roblox reaches instances by path through the DataModel, which is closer to a filesystem.

In UnrealIn RoswaalNotes
Get Player Character / ControllerLocal Player, Local CharacterBoth reach the Players service themselves, so the common case needs no Get Service wired in. Client-only: on the server they are nil, and Roswaal warns if the graph is not a LocalScript. There is no controller — a Player and its Character are separate instances, and Get Player From Character goes back.
Actor TagsAdd Tag, Remove Tag, Has Tag, Get TagsThe methods on Instance rather than the CollectionService calls they forward to, so no service has to be hoisted. Roblox tags are strings and carry no data — the data goes in attributes.
Actor / Component variables set from the details panelAttributesPer-instance values set in Studio and read at runtime, which is the closest thing to an exposed Blueprint variable. Get Attribute returns nil when unset, and that is how you test for one.
Get Components By Class / Get All Child ActorsGet Children, Get Descendants, Find First Child Which Is AWhich Is A matches derived classes; Of Class is exact. Find First Child with Recursive set searches the whole subtree by name, which is slower than a path — reach for Instance when you already know where it lives.
Get Component / Get Child ActorInstance path, Find First ChildInstance takes a dotted path and compiles to plain indexing. Wait For Child yields until it exists, which has no Blueprint counterpart because Unreal has no replication-order problem of that shape.
Spawn Actor From ClassNew Instance, then Set ParentParenting is what makes an instance live. Nothing exists until it has a parent.
Destroy ActorDestroy
Get / Set propertyGet Property, Set PropertyThe property name is typed in and pasted into the generated source, so it cannot be wired from a value. The pin says so.
Cast To <Class>, to reach membersIs A to ask, Cast to assertThese are two halves of what Cast To does in one node, and keeping them apart is the important part. Is A is the runtime question and gives you a boolean to branch on — that is Cast To's execution pins. Cast is Luau's ::, which tells the typechecker what you know and emits nothing; there is no runtime check, so being wrong is silent. Ask with Is A, then assert with Cast.
Cast an array of actors to a subclassCast ArrayGet Descendants and friends are typed { Instance } even when you know every element is a BasePart. This is how you say so, and it is a step Unreal does not make you take because its containers are already typed.

Escape hatches

Unreal's answer is to drop to C++. Roswaal's is to drop to Luau, in the same graph, and there are exactly two nodes that do it.

In UnrealIn RoswaalNotes
C++ function exposed to BlueprintA node packDeclarative templates, no compilation step, and nothing executes at author time.
Inline C++ (there is none)Custom Code, Luau ExpressionThe only two places hand-written Luau can enter a graph. Every other pin that defaults to something like Vector3.zero shows that constant and will not accept typed code — so these two node titles are a complete list of where to look.

Types

Most of these are a rename. The rows worth reading slowly are the ones with a dash in the middle column: Luau has no Rotator, no Quat, and no typed containers, and those absences change how you write things rather than just what you call them.

In UnrealLuau / RobloxRoswaal pinNotes
boolbooleanboolean
int32, int64numbernumberLuau has one number type, a 64-bit float. No integer type, so bitwise work goes through bit32 and there is no integer overflow to reason about.
float, doublenumbernumberThe same type as the row above.
FString, FName, FTextstringstringOne string type. No localisation type — Roblox handles that at the UI layer.
FVectorVector3Vector3Different conventions. Unreal is centimetres and Z-up; Roblox is studs and Y-up. Vertical is Y here. Vector3 from Z-Up converts a position across.
FVector2DVector2Vector2
FRotatora CFrame's rotationCFrameRoblox has no Euler rotation type; rotation lives inside a CFrame. CFrame from Z-Up Rotator converts a rotator across, degrees and axes both.
FTransformCFrameCFrameA CFrame is position and rotation only — no scale. CFrame from Z-Up Transform converts one and hands its scale back, for the part's Size.
FQuata CFrame's rotationCFrameNo quaternion type, but a CFrame can be built from one. CFrame from Z-Up Rotation converts a quat across, and From Axis Angle covers most of what one was reached for.
FLinearColor, FColorColor3Color3Components are 0–1. Color3.fromRGB takes 0–255 if that is what you have.
TArray<T>{ T }tableLuau has one table type for arrays and maps both, and the Roswaal pin does not carry the element type. Cast Array is how you say what is in it.
TMap<K, V>{ [K]: V }tableThe same type as an array.
TSet<T>{ [T]: true }tableA table used as a set, by convention.
UObject*, AActor*InstanceInstanceA class such as Model narrows it. Is A asks at runtime.
TSubclassOf<T>stringstringA class name is just text: Instance.new("Part"), :IsA("BasePart").
USTRUCTa table, or a Roblox value typetableLuau has no struct declaration. The built-in value types are the exception.
UENUMEnum.X for Roblox's ownstringNo user-defined enums. A string pin with a dropdown is the usual stand-in.
Delegate, Event DispatcherRBXScriptSignalRBXScriptSignalConnect Event binds one.
TOptional<T>T?—Optionality is a Luau type annotation rather than a pin type. Cast to T? where it matters.
nullptrnilthe Nil nodeA missing value, not a null pointer — there are no pointer types.
TSharedPtr, UPROPERTY lifetimegarbage collected—An instance survives while something references it or it is parented into the DataModel. Destroy severs both.

Last reviewed 11 September 2026 by @neopolitans

To verify: someone who has shipped multiplayer in Unreal should check the Networking rows — replicated functions (RPCs) especially — against real use.