UI ​
Add custom widgets to the menu. Every widget call takes (tab, section, name, ...) as its first three arguments. Tabs must be created explicitly with ui.new_tab() before any widget call targets them - if the tab does not exist, the engine calls luaL_error with a "tab does not exist" message. Sections auto-create within an already-existing tab on first widget reference.
Every widget call returns a ref string. You don't get the value back directly. To read or write a widget's value, use ui.get(ref) and ui.set(ref, value) against the ref. State is persisted between sessions automatically.
Quick example ​
ui.new_tab("My ESP", "My ESP")
local on = ui.checkbox("My ESP", "Boxes", "Enabled", true)
local thickness = ui.slider_int("My ESP", "Boxes", "Thickness", 1, 5, 2)
local color = ui.color_picker("My ESP", "Boxes", "Color")
ui.button("My ESP", "Boxes", "Reset", function()
print("reset clicked")
end)
function on_tick()
if not ui.get(on) then return end
for _, p in ipairs(get_players()) do
if p:is_enemy() and p:is_alive() then
local b = p:screen_box()
if b then
draw.rect(b.x, b.y, b.w, b.h,
ui.get(color), ui.get(thickness))
end
end
end
endThe first three arguments ("My ESP", "Boxes", the widget name) determine where the widget appears in the menu. Reuse the same tab+section across calls to group widgets together.
Custom tab labels ​
ui.new_tab(id, display) takes a separate group id and display label. The id (the first argument to every widget call) groups widgets and stays fixed in code; the display label is what shows in the menu's tab bar and can have spaces or special characters. Pass a distinct display label when you want the two to differ:
ui.new_tab("toast_demo", "Toast Demo")
ui.button("toast_demo", "styles", "Default", function()
toast("Hi", 2)
end)| Function | Returns | Description |
|---|---|---|
ui.new_tab(id, label) | - | Register a tab. The internal id (used by every widget call's first argument) can differ from the display label shown in the menu. Must be called before any widget call that targets this tab. If you use the same string for both id and label, calling ui.new_tab(id, id) is still required |
Widgets ​
| Function | Returns | Description |
|---|---|---|
ui.checkbox(tab, section, name, [default], [in_line]) | ref | Toggle. default is a bool; in_line places the widget inline with the previous one |
ui.slider_int(tab, section, name, min, max, [default]) | ref | Integer slider |
ui.slider_float(tab, section, name, min, max, [default]) | ref | Float slider |
ui.dropdown(tab, section, name, options, [default_index]) | ref | Dropdown. options is an array of strings; selection returned as 1-based index |
ui.multiselect(tab, section, name, options) | ref | Multi-select. ui.get returns a boolean array indexed by option position (1-based) |
ui.color_picker(tab, section, name, [default], [in_line]) | ref | Color picker. default is an optional {r,g,b,a} Color table; ui.get returns a Color table {r,g,b,a} (0-255) |
ui.input_text(tab, section, name, [default]) | ref | Text input |
ui.keybind(tab, section, name, [default_vk]) | ref | "Click to bind" keybind picker. Stored as VK code. default_vk defaults to 0x06 (VK_XBUTTON2) when omitted |
ui.font_picker(tab, section, name, [default_font]) | ref | Font picker with system font search. Stored as font name string. See Draw > Fonts |
ui.button(tab, section, name, callback) | ref | Button. callback runs when pressed |
ui.label(tab, section, text) | ref | Static wrapped text label. No interactive value |
Each widget also has a ui.new_* alias (ui.new_checkbox, ui.new_slider_int, etc.) for consistency with older script styles. The aliases are identical to the unprefixed versions.
Reading and writing values ​
| Function | Returns | Description |
|---|---|---|
ui.get(ref) | varies | Current value of the widget. Also accepts (tab, sec, name) directly instead of a ref string. Type depends on the widget |
ui.get_value(ref) | varies | Alias for ui.get |
ui.set(ref, value) | - | Override the widget's current value. Also accepts (tab, sec, name, val) directly |
ui.set_value(ref, value) | - | Alias for ui.set |
ui.set_visibility(ref, bool) | - | Show or hide the widget. Also accepts (tab, sec, name, visible) directly |
local mode = ui.dropdown("Misc", "General", "Mode", {"Off", "Casual", "Tryhard"}, 1)
function on_tick()
if ui.get(mode) == 3 then
-- Tryhard mode
end
end
-- Set programmatically:
ui.set(mode, 2)Use ui.set_visibility for conditional UI without re-creating widgets:
local advanced = ui.checkbox("Misc", "General", "Advanced mode")
local detail = ui.slider_int("Misc", "General", "Detail level", 1, 10, 5)
function on_tick()
ui.set_visibility(detail, ui.get(advanced))
endSections ​
ui.new_section creates a named card (section) inside a tab, giving you control over layout options that are not available when sections are created implicitly by widget calls.
| Function | Returns | Description |
|---|---|---|
ui.new_section(tab, ref, display, [opts]) | - | Create a section card. opts is an optional table: {autosize, next, halfsize, visible}. If tab does not exist, silently does nothing (returns without creating anything) |
ui.new_container(tab, ref, display, [opts]) | - | Alias for ui.new_section |
local sec = ui.new_section("My Tab", "main_sec", "Options", { halfsize = true })
local on = ui.checkbox("My Tab", "main_sec", "Enable")Tooltips ​
| Function | Returns | Description |
|---|---|---|
ui.set_tooltip(ref, text) | - | Attach hover tooltip text to a widget. Also accepts (tab, sec, name, text) directly |
ui.tooltip(ref, text) | - | Alias for ui.set_tooltip |
local spd = ui.slider_float("Aim", "General", "Speed", 0.1, 1.0, 0.5)
ui.set_tooltip(spd, "Lower = smoother. 1.0 = instant snap.")Theme and menu state ​
| Function | Returns | Description |
|---|---|---|
ui.accent() | Color {r,g,b,a} | Current menu accent color (matches the theme). a is always 255 |
ui.accent_color() | Color {r,g,b,a} | Alias for ui.accent |
ui.is_menu_open() | boolean | True while the overlay menu is open. Use this to gate panel dragging so panels cannot be moved during gameplay |
ui.menu_open() | boolean | Alias for ui.is_menu_open |
Draggable and resizable panels ​
Call these each tick (inside on_tick) to let users reposition and resize script-drawn panels while the menu is open.
| Function | Returns | Description |
|---|---|---|
ui.draggable(id, x, y, w, h) | nx, ny, dragging | Drag a panel. id must be unique per panel. Returns the (possibly updated) top-left position and a dragging flag. Persist nx, ny yourself (e.g. via ui.set on release) |
ui.resizable(id, x, y, w, h, value, [min], [max]) | new_value, resizing, factor | Proportional resize grip (bottom-right corner). value is the size parameter to scale; factor is the raw drag ratio, apply it to other size params for uniform scaling. Call before ui.draggable on the same panel so the grip grab takes priority. Inert while the menu is closed |
local px = ui.slider_int("Radar", "Panel", "X", 0, 3840, 100)
local py = ui.slider_int("Radar", "Panel", "Y", 0, 2160, 100)
local ps = ui.slider_int("Radar", "Panel", "Size", 50, 800, 300)
function on_render()
if not ui.is_menu_open() then return end
local x, y, s = ui.get(px), ui.get(py), ui.get(ps)
-- resize grip first so it wins over the body drag
local ns, resizing = ui.resizable("radar_panel", x, y, s, s, s, 50, 800)
if resizing then ui.set(ps, ns) end
local nx, ny, dragging = ui.draggable("radar_panel", x, y, s, s)
if dragging then ui.set(px, nx); ui.set(py, ny) end
endReturn value types ​
| Widget | ui.get returns |
|---|---|
checkbox | boolean |
slider_int, keybind | integer |
slider_float | number |
dropdown | integer (1-based index) |
multiselect | table of booleans (indexed by option position; true = selected) |
color_picker | Color table {r, g, b, a} (0-255 per channel) |
input_text, font_picker | string |
button | nothing meaningful (use the callback) |
label | nothing meaningful (display-only) |
settings vs ui ​
For most scripts, declare config in the settings = { ... } table instead. It's simpler, auto-renders below your script in the menu, and uses the config.* namespace for reading/writing.
Use ui.* when you need custom tabs, button callbacks, or runtime-mutable visibility via ui.set_visibility.