imgui draws Dear ImGui UI over the game through gd-imgui-cocos.
Use it for player mod menus, settings panels, tabs, popups, and debug tools.
Register one draw callback with imgui.onDraw. Build all ImGui UI inside that callback.
Widget calls must run inside imgui.onDraw.
See Getting started for the shared main-thread rule.
onDraw starts the backend on first use. cancel removes a callback.
Dropping the handle cancels it on GC. A callback that errors is removed.
Use setVisible, toggle, and isVisible to show or hide the overlay without unregistering.
A minimal closable window:
local open = true
local enabled = true
imgui.onDraw(function()
if not open then return end
open = imgui.window("my.mod/quick", function()
enabled = imgui.checkbox("Enabled", enabled)
end, { closable = true })
end)An input text round-trip (the shared buffer means you save the return every frame):
local name = ""
imgui.onDraw(function()
name = imgui.inputText("Name", name, 64)
end)ImGui is immediate mode. It does not store your widget state. Pass current values each frame and save returned values in Luau.
The game keeps input when ImGui is not hovered or focused. Focused ImGui windows capture the input they need.
window, child, group, tabBar, tabItem, popup, popupModal, table, menuBar, menu, style.with,
and font.with always close their ImGui region after fn, even when fn errors.
windowreturns false when its close button is pressed. Setclosable = trueto get the close button.- All mods share one ImGui context. Identical window titles collide silently, so prefix windows with your mod id.
- Use
sizeCondorposCondwithimgui.Cond.Alwaysfor animated windows.
- Text is drawn as raw text, so percent signs are safe.
- Input text uses a shared per-thread buffer with a length cap. See Limits and errors.
- Combo and list indexes are zero-based. Colors use
{ x, y, z, w }floats from 0 to 1. treeNodereturns a tracked open value only whenopts.openis set.tabItemandpopupModalreturn a close state only whenclosableis true.- Use
openPopupbeforepopuporpopupModal. imgui.tooltip(fn)shows a tooltip for the previous item only when it is hovered.- Call table row and column helpers inside
imgui.table. menuItemreturns clicked when no selected value is passed. When selected is passed, it returns the new selected value.
Use one imgui.onDraw per mod when possible.
Do not do IO, web work, or heavy list rebuilds in onDraw.
Image, texture, docking, viewport, and draw list APIs are not bound.
local open = true
local enabled = true
local volume = 0.7
local mode = 1
local modes = { "Easy", "Normal", "Hard" }
local accent = { x = 0.3, y = 0.7, z = 1.0, w = 1.0 }
imgui.onDraw(function()
if not open then return end
imgui.theme.apply("dark")
open = imgui.window("my.mod/settings", function()
imgui.style.with({
frameRounding = 4,
colors = {
[imgui.Col.Button] = accent,
},
}, function()
imgui.tabBar("tabs", function()
imgui.tabItem("General", function()
enabled = imgui.checkbox("Enabled", enabled)
volume = imgui.sliderFloat("Volume", volume, 0, 1)
mode = imgui.combo("Mode", mode, modes)
accent = imgui.colorEdit4("Accent", accent)
end)
imgui.tabItem("Danger", function()
if imgui.button("Reset") then
imgui.openPopup("reset-confirm")
end
imgui.popupModal("reset-confirm", function()
imgui.text("Reset settings?")
if imgui.button("Yes") then
volume = 0.7
imgui.closeCurrentPopup()
end
imgui.sameLine()
if imgui.button("No") then
imgui.closeCurrentPopup()
end
end)
end)
end)
end)
end, {
closable = true,
size = { x = 420, y = 280 },
flags = imgui.Flag.Window.NoCollapse,
})
end)See mod/demo/demo_modmenu.luau for a full mod menu.
imgui.theme.apply("dark" | "light" | "classic") and imgui.theme.applyCustom(opts) change global ImGui style until changed again.
Use imgui.style.with(opts, fn) for per-window or per-mod styling that does not affect other onDraw callbacks.
Useful option fields include alpha, windowPadding, windowRounding, framePadding, frameRounding, itemSpacing, and colors.
Color keys can be imgui.Col.* values or color names from imgui.Col.
LuauAPI exposes an ImGui scale slider in its Geode mod settings. There is no Lua API for this.
Call imgui.font.add(root, path, size) outside imgui.onDraw. It errors if called inside a draw callback.
Use raw .ttf resource files, not GD bitmap fonts. The first argument uses the same roots as fs.
Before the backend initializes, add stores the font and builds it at backend init.
imgui.font.with(font, fn) must run inside imgui.onDraw and always pops after fn, even when fn errors.
See Limits and errors for font error strings.
local font, err = imgui.font.add("resources", "assets/Inter.ttf", 18)
if not font then
error(err)
end
imgui.onDraw(function()
imgui.font.with(font, function()
imgui.text("Custom font")
end)
end)See Limits and errors for draw callback caps, script deadlines, input text caps, font errors, and GPU session disable.
The authoritative argument lists live in the generated type stubs, surfaced as editor autocomplete.
See type stubs.
Handwritten extras are in tools/luau_codegen/extra_bindings/imgui.dluau.
Constant tables live under imgui.Flag.*, imgui.Col.*, imgui.StyleVar.*, and imgui.Cond.*.
Use constants instead of magic numbers.
- UI and layouts
- type stubs
- Getting started
- globals
- LuauAPI mod guidelines
- ImGui draw scheduler
- Limits and errors
tools/luau_codegen/extra_bindings/imgui.dluausrc/bindings/imgui/ImGuiCore.cppsrc/bindings/imgui/ImGuiWidgetsLayout.cppsrc/bindings/imgui/ImGuiPopupsTablesMenus.cppsrc/bindings/imgui/ImGuiStyleFonts.cppsrc/bindings/imgui/ImGuiBindingInternal.hpp