Learn/UI Systems

UI Systems

In-experience UI is what a player sees on the screen and over the world: a ScreenGui overlay of labels, frames and images, and BillboardGui labels that float above parts. Every element is an object in the Space, sized with Roblox-style UDim2 values and stored as a TOML file you can read, edit and version.

Time12 min readLevelIntermediateUpdatedUpdated Sep 2026

01Overview

What In-Experience UI Is

In-experience UI is the interface you build for the people using your Space: a score in the corner, a status panel, a name floating over a machine. It is made of GUI objects that live in the Space alongside parts. This page is about those objects; the panels, ribbon and tabs of the editor itself are covered on the Studio page.

Screen UI

Screen UI

A ScreenGui in StarterGui draws its elements over the viewport.

Billboards

Billboards

A BillboardGui inside a part draws a camera-facing card above it.

Scripts

Scripted

Rune scripts change text, colors and visibility while you play.

The GUI Classes

The classes carry Roblox's names and properties, so imported places keep their interface. Containers hold other elements; leaves draw content.

ClassRoleDrawn today
ScreenGuiRoot of a screen overlayYes, from StarterGui
BillboardGuiCamera-facing card in the worldYes
FrameBox with a background, border and rounded cornersYes
ScrollingFrameFrame that clips its contentsDrawn; no scrolling input yet
TextLabelTextYes
TextButtonText with a background, meant to be clickedDrawn; clicks not delivered yet
TextBoxText fieldDrawn; typing not wired yet
ImageLabel, ImageButtonImageOn screen; a placeholder on billboards
ViewportFrameA view into a 3D sceneA placeholder box labelled with the class
SurfaceGuiUI on a face of a partStored, not drawn yet
UIListLayout, UIGridLayout, UIPadding, UICorner, UIStroke and the other UI modifiersLayout and decoration settingsStored, not applied yet

02Layout

Scale and Offset

A UDim2 describes a position or size on two axes, each as a scale plus an offset. The scale is a fraction of the parent's size, the offset is a fixed number of pixels, and the two add up. A top-level element measures against the viewport; an element inside a Frame or BillboardGui measures against that container.

pixels = scale x parent size + offset
computed separately for X and Y

In a file a UDim2 is four numbers in the order X scale, X offset, Y scale, Y offset:

UDim2As a sizeAs a position
[0, 200, 0, 50]200 x 50 px, whatever the parent200 px right, 50 px down
[1, 0, 1, 0]Fills the parentThe parent's bottom-right corner
[0.5, 0, 0.5, 0]Half the parent each wayThe parent's center
[1, -20, 0, 40]Full width less 20 px, 40 px tall20 px in from the right edge, 40 px down

The table form { x = { scale = 1.0, offset = 0.0 }, y = { scale = 0.0, offset = 40.0 } } is read too. A two-number [w, h] value is rejected, so a file using it fails to load instead of guessing.

Anchor Point

The anchor point chooses which point of the element sits on its position, as fractions of the element's own size. [0, 0] (the default) hangs the element from its top-left corner, [0.5, 0.5] centers it on the position, and [1, 1] puts its bottom-right corner there.

parent (the viewport for a top-level element)0.5 of the widthScore: 0position (0.5, 0, 0, 24)anchor_point (0.5, 0)size (0, 240, 0, 48)24 px
The position picks a point in the parent: half its width, 24 px down. The anchor point says which point of the label lands there, here the middle of its top edge, so the label stays centered at any window size.

Layering and Visibility

  • z_index orders elements: a higher value draws on top of a lower one.
  • visible = false hides that element. On a ScreenGui it hides everything inside; on a Frame it hides only the Frame, not the elements inside it.
  • Clipping: a ScrollingFrame on a billboard cuts off anything inside it that extends past its edges.

03GUI Files

One Folder per Element

Each GUI element is a folder with an _instance.toml inside it, and nesting the folders nests the elements. The UI tab of the ribbon and the Insert Object dialog (Ctrl+I) create elements this way, inside the ScreenGui, BillboardGui or Frame you have selected, or in StarterGui when nothing is selected.

Space folder
StarterGui/
  HUD/
    _instance.toml        class_name = "ScreenGui"
    Score/
      _instance.toml      class_name = "TextLabel"
    Panel/
      _instance.toml      class_name = "Frame"
      Status/
        _instance.toml    class_name = "TextLabel"

Single files named by class also load, such as Score.textlabel.toml, Panel.frame.toml or Logo.imagelabel.toml, in StarterGui and in Workspace.

The TOML Format

A GUI file has up to four sections: [metadata] for the class, [instance] for the name, [gui] for layout and appearance, and [text] for anything that shows text. This label sits centered at the top of the screen:

StarterGui/HUD/Score/_instance.toml
[metadata]
class_name = "TextLabel"
archivable = true

[instance]
name = "Score"

[gui]
position = [0.5, 0.0, 0.0, 24.0]
size = [0.0, 240.0, 0.0, 48.0]
anchor_point = [0.5, 0.0]
background_color = [20, 24, 32]
background_transparency = 0.2
corner_radius = 8.0
z_index = 10

[text]
text = "Score: 0"
text_color = [255, 255, 255]
font_size = 28.0

Colors take three or four numbers. When every number is a whole number they are read as 0 to 255, otherwise as 0.0 to 1.0, so [255, 128, 0] and [1.0, 0.5, 0.0] are the same orange. Keys written in PascalCase, such as BackgroundColor, are read as their snake_case names.

Advanced
A text element without [instance] name is called _instance

Text, button, box and image elements take their name from [instance] name; frames, ScreenGuis and BillboardGuis take the folder's name. Scripts find elements by name, so give every element a unique name and use the same name for its folder.

Keys Reference

KeyValueDefault
[gui] positionUDim2[0, 0, 0, 0]
[gui] sizeUDim2[0, 100, 0, 30]
[gui] anchor_pointTwo fractions[0, 0]
[gui] background_colorColorDark gray, 80% opaque
[gui] background_transparency0 opaque to 1 invisibleTaken from the color's alpha
[gui] border_size, border_colorPixels, color0, gray
[gui] corner_radiusPixels0
[gui] visibletrue or falsetrue
[gui] z_indexInteger0
[text] textStringEmpty
[text] text_color, text_transparencyColor, 0 to 1White, 0
[text] font_sizePixels14
[text] fontFont name, such as GothamBoldDefault font
[text] text_x_alignment, text_y_alignmentLeft, Center, Right; Top, Center, BottomLeft; Center
[text] text_scaledFit the text to the boxfalse
[text] text_stroke_color, text_stroke_transparencyOutline color, 0 to 1Black, 1 (no outline)
[asset] pathImage file for an ImageLabel or ImageButton, relative to the folder holding the element's fileNone

On billboards, a font name containing Bold draws bold and one containing Light or Thin draws light. Changes to a BillboardGui's file apply while the Space is open; the other GUI files are read when the Space opens. Billboard keys are listed in Readable Labels.

04Screen UI

Building a HUD

  1. On the ribbon's UI tab, click Screen. A ScreenGui appears in StarterGui.
  2. Select it in the Explorer, then click Text, Frame, Image or Button. Each new element is created inside the selected container.
  3. Style each element in its _instance.toml using the keys above.

Screen UI draws in the viewport while you edit and while you play. A ScreenGui belongs in StarterGui, and an element draws on the screen only when it sits inside one. Set visible = false under [gui] in the ScreenGui's own file to hide the whole overlay.

Advanced
A new ScreenGui shows its contents after the Space reopens

A ScreenGui created during the session starts drawing its elements the next time the Space is opened; until then they stay hidden. The files are written the moment you insert them, so close and reopen the Space once after adding a ScreenGui.

How It Draws

Screen elements are drawn by the same overlay that draws the editor, above the 3D view, measured in the viewport's logical pixels. Billboards use their own rasterizer, and the two support different subsets of the properties:

FeatureScreen overlayBillboard card
Background, border, rounded cornersYesYes
Text color and sizeYes, at least 8 pxYes, at least 8 px
text_scaledNoYes
Font name and weightNoYes
Text outlineNoYes
Horizontal alignmentCentered unless the value is written lower case, left or rightLeft, Center, Right
Vertical alignmentCenteredTop, Center, Bottom
Text too long for its boxCut off with an ellipsisWrapped at words
ImagesYes, fitted inside the boxPlaceholder box

In Studio a click on a screen element also reaches the scene, so the part behind a HUD element can still be selected.

05Billboards

Labels over Parts

A BillboardGui is a card in the world that turns to face the camera. It follows whatever it is placed inside, so keep it in the folder of the part it labels. To add one in Studio, select the part, open Insert Object (Ctrl+I), choose BillboardGui, then select the new BillboardGui and add a Text element from the UI tab.

Workspace/Beacon/Label/_instance.toml
[metadata]
class_name = "BillboardGui"
archivable = true

[gui]
size = [4.0, 0.0, 1.0, 0.0]                 # 4 m x 1 m
units_offset_world_space = [0.0, 2.0, 0.0]  # 2 m above the part's center
always_on_top = true
max_distance = 150.0
Workspace/Beacon/Label/Text/_instance.toml
[metadata]
class_name = "TextLabel"
archivable = true

[instance]
name = "BeaconText"

[gui]
size = [1.0, 0.0, 1.0, 0.0]     # fill the billboard
background_transparency = 1.0

[text]
text = "Beacon"
text_color = [255, 220, 120]
text_scaled = true
font = "GothamBold"
text_stroke_transparency = 0.0

Saving a BillboardGui's file while the Space is open resizes and moves the live billboard. A BillboardGui placed directly in a Folder is not drawn, because a folder has no position to follow.

Size in Meters

A billboard's size is a UDim2 whose scale is in meters: one meter is 50 pixels of canvas, so a size resolves to scale x 50 + offset pixels, and the world quad is that many pixels divided by 50 meters across. The default, 200 x 50 px, is a card 4 m wide and 1 m tall. Elements inside the billboard lay out against that canvas.

50 px
Canvas pixels per meter
192 px
Texture tile per billboard
192 x 192, in one shared atlas
300 m
Drawing radius
tiles freed beyond 360 m

Every billboard is drawn on the CPU into a 192 x 192 px tile of a shared texture. A billboard wider or taller than 3.84 m keeps its full size in the world, but its content is drawn into the same tile and stretched, so large cards look softer than small ones. Billboards farther than 300 m from the camera are not drawn at all.

Readable Labels

[gui] keyEffectDefault
sizeCanvas size; scale is in meters200 x 50 px
units_offsetOffset in the parent's axes, meters; grows with the parent's scale[0, 0, 0]
units_offset_world_spaceOffset along the world axes, meters, whatever the parent's rotation or scale[0, 0, 0]
always_on_topDraw over all geometry instead of being hidden behind itfalse
z_indexPull the card 0.5 m toward the camera per unit; negative pushes it away0
max_distance, distance_upper_limitHide beyond this many meters; the smaller of the two applies1000
distance_lower_limitHide when the camera is closer than this0 (off)
enabledShow or hide the billboardtrue

With text_scaled = true, a label on a billboard picks the largest font that fits its box, searching from 1 px up to the box's height or 72 px, whichever is larger. Otherwise it uses font_size. An outline (text_stroke_transparency = 0.0) keeps text legible against busy scenes, and a tight max_distance keeps distant labels from crowding the view.

Tip
Let the text fill the card

Size the TextLabel [1, 0, 1, 0] so it covers the whole billboard, and put the label at the part's center with always_on_top = true, or lift it with a small z_index when it should still hide behind other parts.

06Scripting UI

Changing Elements from Rune

Rune scripts change GUI elements by name while you play, for screen elements and billboard contents alike. If two elements share a name, the change goes to one of them.

FunctionChanges
gui_set_text(name, text)The text
gui_set_visible(name, visible)Whether the element is drawn
gui_set_text_color(name, r, g, b, a)Text color, 0 to 1 per channel
gui_set_bg_color(name, r, g, b, a)Background color
gui_set_border_color(name, r, g, b, a)Border color
gui_set_font_size(name, size)Font size in pixels
SoulService/Hud/Hud.rune
use eustress::{get_sim_value, set_sim_value, gui_set_text, gui_set_text_color};

pub fn on_init() {
    set_sim_value("hud.time", 0.0);
}

pub fn on_update(dt) {
    let t = get_sim_value("hud.time") + dt;
    set_sim_value("hud.time", t);
    gui_set_text("Score", `Time: ${t}`);
    if t > 30.0 {
        gui_set_text_color("Score", 1.0, 0.3, 0.3, 1.0);
    }
}

The lifecycle of on_init and on_update is covered on the Scripting page.

Play and Stop

When Play starts, Studio records how every GUI element looks. When you stop, it puts every element back and drops any change a script queued in the last frame, so a session never leaves its text or colors behind. Script changes affect only what is drawn; the element's file is not touched.

07What's Next

Buttons and Text Input

Clicking a ScreenGui TextButton during Play will call on_button_click(name) in every Rune script that defines it, with the button's name, so one handler can serve a whole menu. The dispatcher is already in place; the button test that feeds it will be matched to the element type the loader records. TextBox typing, ScrollingFrame scrolling and ImageButton hover and pressed images will follow.

Layouts and Surfaces

UIListLayout, UIGridLayout, UIPadding, UICorner, UIStroke and the other modifiers already load with their settings; a layout pass will apply them. SurfaceGui will draw onto the face of its part, billboards will show real images and honor brightness and light_influence, and a billboard's adornee will attach it to a part named anywhere in the Space.

Lay it out in a file. Drive it from a script. Read it anywhere in the world.

Loading Eustress Engine...