Controls overview

All thirteen controls, the options every one shares, and how handles work.

Every control is a method on a section. It takes one options table and returns a handle.

Luau
local handle = section:Toggle({ Text = "Enabled", Callback = print })

The thirteen#

Options every control shares#

Anything a control adds on top is documented on its own page.

These build the row itself:

OptionTypeDefaultWhat it does
TextstringThe label. Non-strings are coerced, so a number is safe.
DescriptionstringA second, quieter line beneath the label.
IconstringOne of the 1,573 built-in icon names.
IconColorColor3Overrides the icon colour for this control only.
IconSizenumber16Icon size in pixels.
LayoutOrdernumberExplicit ordering. Without it, controls appear in creation order.
FlatbooleanfalseDrops the card background and its border. Set for you on controls inside a Group.
HoverCardbooleantrueThe row lifts under the pointer. Never applies to a flat card.

These are read after the control is built, so they work the same on a control you registered yourself:

OptionTypeDefaultWhat it does
CallbackfunctionRuns when the value changes. On Button it runs on click, and on Keybind when the bound key is pressed.
Savestring | booleanPersists the value. true derives a key from the section and label; a string sets the key yourself.
FireOnStartbooleanfalseCalls Callback once at creation with whatever the control holds.
ReapplybooleanRe-applies the value when the character is replaced. Set false to opt out when the window sets ReapplyOnRespawn.
RevertOnClosebooleanApplies an off value when the window closes: false for a toggle, otherwise Default.
RevertToanyThe value RevertOnClose sends instead.
Applyfunction(value)What Reapply and RevertOnClose run instead of Callback. Needed on Button, Keybind and IconButton, whose Callback is an action rather than a value.
TooltipstringShown while the pointer is over the row.
DisabledbooleanfalseRefuses input and dims the control.
DisabledReasonstringShown as a tooltip while it is disabled.

Save, Reapply and RevertOnClose are covered in Saving settings and Lifecycle.

Title, Description and Separator return a Roblox instance instead of a handle, so nothing in the second table reaches them. Their options are on Title, Description & Separator.

Handles#

Stateful controls return a handle with Get and Set:

Luau
local speed = main:Slider({ Text = "Speed", Min = 0, Max = 100, Default = 50 })
 
speed:Get()      --> 50
speed:Set(80)    --> updates the UI and fires Callback

Set returns true, or false and a reason when the value is refused:

Luau
local ok, why = dropdown:Set("Nowhere")   --> false, "value is not present in Options"

Pass true as a second argument to set the value without firing anything. A panic button can put every control back that way without re-running the work.

Set usually fires the callback

On Toggle, Slider, Input, Dropdown, ColorPicker, Palette and Status, Set behaves as though the user moved the control, so calling it inside that control's own Callback loops forever. Keybind:Set is the exception: it fires Changed, not Callback.

Every handle carries these:

MethodWhat it does
Show() / Hide() / SetVisible(bool)Shows or hides the whole control
Destroy()Removes it from the page and disconnects its events

handle.Instance is the card frame, for when you need the Roblox instance.

Some controls add more. A dropdown, for example:

MethodWhat it does
Get()The current value: a string, or an array when Multi
Set(value)Selects a value or list of values
SetOptions(list)Replaces the option list in place
Open() / Close() / IsOpen()Controls the menu

Callbacks#

Callback fires whenever the value changes, with the new value as its first argument:

Luau
main:Toggle({
    Text = "Godmode",
    Callback = function(enabled)   -- boolean
        print("godmode:", enabled)
    end,
})
 
main:Slider({
    Text = "FOV", Min = 70, Max = 120,
    Callback = function(value)     -- number
        workspace.CurrentCamera.FieldOfView = value
    end,
})
 
main:Dropdown({
    Text = "Target", Options = { "Nearest", "Lowest HP" },
    Callback = function(choice)    -- string
        targetMode = choice
    end,
})

A callback does not fire when a control is created. One exception: a Saved control that restored a value calls it once with that value. See Saving settings.

FireOnStart covers the other case: fire once at creation even when nothing was saved, so a control that starts non-default applies itself.

A toggle can name the two halves separately with OnEnabled and OnDisabled. See Toggle.

Turning a control off#

Preview

My Script

v1.0

Developer mode

Unlocks the action below.

Start the farm

Runs until you stop it.

Debug logging
Verbose output

Every control takes Disabled, and every handle has the methods to change it later. A disabled control refuses input and looks like it is refusing.

Luau
local start = main:Button({
    Text = "Start the farm",
    ButtonText = "Go",
    Disabled = true,
    DisabledReason = "Pick a target first",
    Callback = beginFarm,
})
 
targetPicker:Set("Nearest")
start:Enable()
MethodWhat it does
SetDisabled(state, reason?)Turns it off or on. The reason is optional and sticks.
Disable(reason?) / Enable()The same thing, named
IsDisabled()Whether it is currently off

DisabledReason is shown as a tooltip while the control is disabled, so a dead button can say what would bring it back.

It refuses input rather than trusting the callback

There is no overlay. Every button inside the control stops accepting input and every coloured part is faded toward its background, so this works on a control you registered yourself. Guarding inside your callback still lets the control animate as though it did something.

Explaining a control#

Any control takes Tooltip, shown while the pointer is over it.

Luau
main:Slider({
    Text = "Tick rate",
    Min = 1, Max = 60, Default = 30,
    Tooltip = "How often the farm re-scans. Lower is cheaper.",
})

SetTooltip(text) changes it later, and SetTooltip(nil) removes it. DisabledReason takes priority while the control is disabled.

Organising a long section#

Title, Description and Separator break a section into runs. See Title, Description & Separator.

Group puts rows in a container that collapses. See Groups and search.

Adding your own#

RegisterControl installs a builder as a section method, so your control is called like any built-in one and gets the same card, search indexing, Reapply and handle methods:

Luau
Ember.RegisterControl("Badge", function(section, opts)
    local card, slot = Ember.Controls.mount(section, opts)
 
    local value = Ember.Util.label(opts.Value or "", 13, Ember.Theme.accent)
    value.Size = UDim2.new(0, 0, 0, 22)
    value.AutomaticSize = Enum.AutomaticSize.XY
    value.Parent = slot
 
    return {
        Instance = card,
        Set = function(_, text) value.Text = tostring(text) end,
    }
end)
 
main:Badge({ Text = "Build", Value = Ember.Version })

Return a table with an Instance and Ember adds Show, Hide, SetVisible, Destroy, SetTooltip, SetDisabled, Disable, Enable and IsDisabled to it.

CallWhat it does
Ember.RegisterControl(name, build)Installs it. Returns false, reason for a bad name, or one already taken.
Ember.UnregisterControl(name)Removes one of yours. Built-ins cannot be removed.
Ember.HasControl(name)Whether that name exists
Ember.ControlNames()Every control name, sorted

See them running#

Every control page carries a live preview of the real control, and the swatches above each one repaint it into any of the nine built-in themes. For all of them working together in one window, see the docs landing page or the full showcase.