The editor UI
The shape of the editor
The editor surface is three layers, stacked top to bottom:
- The top bar — always present in the editor. On the left, domain menus
(
Scene,Content,Render,Debug,Agents,Window,Settings): click one to drop it open, hover siblings to switch between them, click away or press Esc to dismiss. In the center, the transport (below). Everything in a menu either opens a panel or runs an action. - The dock — the area under the bar. Tools live here as dockable panels: tabs you can drag between regions, split side-by-side, stack into tab groups, and close.
- The world itself — rendering in the Scene panel, and underneath
everything.
F3hides and shows the dock when you want nothing between you and the scene.
The arrangement you leave is the arrangement you come back to: where each panel
sat, how the splits were sized and which tab was up are kept per world under
/source/local, on your machine only, so no world carries a record of how one
person arranged their editor. Window → Reset layout is the escape hatch, and
it forgets that record too.
A few panels worth knowing exist out of the box: Scene (the viewport), Entities (the scenegraph), Inspector, Files, Console, Execute (a Luau console against the live world), Terminal (the engine shell over the VFS), and the agent tabs — Claude Code, Codex CLI and Cursor CLI seated side by side under Agents.
An agent puts something in front of the user with the show toolbox:
show.file opens a file, folder or asset and show.text opens text the agent
wrote, each in a new, closable panel in the dock where the user can read and
review it, and edit it when it is text.
The transport: reading mode at a glance
The centered ▶ / ⏸ pair are state toggles — they always show what is,
never what would be:
▶lit means the world is playing. Press the lit button to stop and return to edit. Unlit means you're editing; press it to play.⏸lit means the gameplay clock is stopped. In edit mode it is always lit (editing is the world held still); in play mode it pauses and resumes gameplay.
One rule governs both: lit = on, press to flip. There is no button whose label describes a future state.
Edit mode and the editor's own liveliness
Entering edit stops the gameplay clock — update loops, physics, the clock —
but the editor UI itself keeps running. Pausing play does the same: the
authoring surface comes back while the world sits still, which is why you can
select, drag a gizmo and reparent in a paused play session.
If you write editor tooling this distinction reaches your code. A component that
only defines update(dt) goes idle with gameplay, while an editor-side one also
defines editorUpdate(dt) — the authoring channel, which runs in edit mode and
in play while paused. Panels registered as assets don't need to care: the shell
ticks them either way.
Extending the editor from your world
Your own panels are instances of the editorPanel asset type. Create a new one
and its tab exists immediately, carrying the template's body. Then edit the
init.luau it came with: it returns the tab's spec, and saving re-registers the
panel, so the tab changes under you as you work:
local edui = require("@builtin::~edui.framework")
return {
title = "Time of Day",
menu = "scene",
dock = "bottom",
startClosed = true,
build = function()
return edui.panel{ body = edui.vbox{ gap = 6, children = {
edui.field{ label = "Sun angle", value = currentAngle(),
kind = "number", key = "tod:angle",
onChange = function(v) setSunAngle(v) end },
} } }
end,
}
Open the editor and Time of Day sits in the Scene menu, opening a real dock tab — draggable, splittable, closable like Entities. A panel naming a menu nobody registered creates that menu rather than vanishing.
What the editor owns so you don't have to: docking chrome and rearrangement;
scheduling (only the ACTIVE tab of a leaf rebuilds, so a panel stacked behind
another costs nothing); fault isolation (an init.luau that will not load comes
up as a tab carrying the error rather than breaking the editor); and persistence
across edit ↔ play.
guides { path = "editor/edui/edui" } is the full account: the spec's
fields, what build is called by and when, where panel state lives, and the
edui.* surface a panel body draws with.
The boundary
Panels compose editor surface. What happens inside one is ordinary world code — the same widgets, callbacks and engine APIs as anywhere else. If you need an editor capability that doesn't exist — a new docking behavior, a viewport interaction — that's an engine change to file an issue for, not something to simulate from a panel.