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.
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
A ScreenGui in StarterGui draws its elements over the viewport.
Billboards
A BillboardGui inside a part draws a camera-facing card above it.
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.
| Class | Role | Drawn today |
|---|---|---|
ScreenGui | Root of a screen overlay | Yes, from StarterGui |
BillboardGui | Camera-facing card in the world | Yes |
Frame | Box with a background, border and rounded corners | Yes |
ScrollingFrame | Frame that clips its contents | Drawn; no scrolling input yet |
TextLabel | Text | Yes |
TextButton | Text with a background, meant to be clicked | Drawn; clicks not delivered yet |
TextBox | Text field | Drawn; typing not wired yet |
ImageLabel, ImageButton | Image | On screen; a placeholder on billboards |
ViewportFrame | A view into a 3D scene | A placeholder box labelled with the class |
SurfaceGui | UI on a face of a part | Stored, not drawn yet |
UIListLayout, UIGridLayout, UIPadding, UICorner, UIStroke and the other UI modifiers | Layout and decoration settings | Stored, 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.
In a file a UDim2 is four numbers in the order X scale, X offset, Y scale, Y offset:
| UDim2 | As a size | As a position |
|---|---|---|
[0, 200, 0, 50] | 200 x 50 px, whatever the parent | 200 px right, 50 px down |
[1, 0, 1, 0] | Fills the parent | The parent's bottom-right corner |
[0.5, 0, 0.5, 0] | Half the parent each way | The parent's center |
[1, -20, 0, 40] | Full width less 20 px, 40 px tall | 20 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.
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.
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:
[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.0Colors 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.
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
| Key | Value | Default |
|---|---|---|
[gui] position | UDim2 | [0, 0, 0, 0] |
[gui] size | UDim2 | [0, 100, 0, 30] |
[gui] anchor_point | Two fractions | [0, 0] |
[gui] background_color | Color | Dark gray, 80% opaque |
[gui] background_transparency | 0 opaque to 1 invisible | Taken from the color's alpha |
[gui] border_size, border_color | Pixels, color | 0, gray |
[gui] corner_radius | Pixels | 0 |
[gui] visible | true or false | true |
[gui] z_index | Integer | 0 |
[text] text | String | Empty |
[text] text_color, text_transparency | Color, 0 to 1 | White, 0 |
[text] font_size | Pixels | 14 |
[text] font | Font name, such as GothamBold | Default font |
[text] text_x_alignment, text_y_alignment | Left, Center, Right; Top, Center, Bottom | Left; Center |
[text] text_scaled | Fit the text to the box | false |
[text] text_stroke_color, text_stroke_transparency | Outline color, 0 to 1 | Black, 1 (no outline) |
[asset] path | Image file for an ImageLabel or ImageButton, relative to the folder holding the element's file | None |
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
- On the ribbon's UI tab, click Screen. A ScreenGui appears in
StarterGui. - Select it in the Explorer, then click Text, Frame, Image or Button. Each new element is created inside the selected container.
- Style each element in its
_instance.tomlusing 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.
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:
| Feature | Screen overlay | Billboard card |
|---|---|---|
| Background, border, rounded corners | Yes | Yes |
| Text color and size | Yes, at least 8 px | Yes, at least 8 px |
text_scaled | No | Yes |
| Font name and weight | No | Yes |
| Text outline | No | Yes |
| Horizontal alignment | Centered unless the value is written lower case, left or right | Left, Center, Right |
| Vertical alignment | Centered | Top, Center, Bottom |
| Text too long for its box | Cut off with an ellipsis | Wrapped at words |
| Images | Yes, fitted inside the box | Placeholder 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.
[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[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.0Saving 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.
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] key | Effect | Default |
|---|---|---|
size | Canvas size; scale is in meters | 200 x 50 px |
units_offset | Offset in the parent's axes, meters; grows with the parent's scale | [0, 0, 0] |
units_offset_world_space | Offset along the world axes, meters, whatever the parent's rotation or scale | [0, 0, 0] |
always_on_top | Draw over all geometry instead of being hidden behind it | false |
z_index | Pull the card 0.5 m toward the camera per unit; negative pushes it away | 0 |
max_distance, distance_upper_limit | Hide beyond this many meters; the smaller of the two applies | 1000 |
distance_lower_limit | Hide when the camera is closer than this | 0 (off) |
enabled | Show or hide the billboard | true |
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.
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.
| Function | Changes |
|---|---|
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 |
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.