Log inGet started

The editor UI

Updated October 2, 2026

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. F3 hides 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.