Skip to content

Tips & Best Practices ​

  • Stable element IDs — Make element ids unique and consistent across updates
  • Batch changes — Call ui:commit() once after all updates, not after each element
  • Use handles — Prefer ui:get(id) + handle:set_props/set_style for small updates instead of rebuilding
  • Avoid clearing every frame — Don't call ui:clear() every tick unless intentionally rebuilding everything
  • Multiple surfaces — Use separate surfaces for multi-page UIs instead of hiding/showing elements
  • Nested elements — Use parent:element() for containers with children (headers, panels, toolbars)
  • Throttle live updates — Update at 2–4 Hz for dashboards to avoid excessive UI rebuilds
  • Use layout helpers — ss.ui.grid(), ss.ui.flex(), and ui:layout() eliminate manual pixel math
  • Pin hints — On motherboards/circuitboards, ss.pin_label(0, "Label") … ss.pin_label(5, …) stores short hints for d0–d5 that appear on the built-in config screen (separate from device_label on the IC). One-arg ss.pin_label(i) reads the hint. Not available on tablet cartridges. On computers, open that screen with a Configuration Disk (not a vanilla Data Disk).
  • Portable UI themes — Save looks on a vanilla Data Disk with StationeersLua ic.disk; see Examples/DataDiskPortableTheme.lua.

Debugging ​

print(...) writes to the Lua Debugger motherboard Logs tab. Insert the debugger board into a computer, select your endpoint, and view captured logs.

If you are attached with the StationeersLua VS Code debugger, ScriptedScreens log output and runtime errors are also forwarded into the attached debug session.

Troubleshooting ​

ProblemSolution
UI not showingEnsure you called ss.ui.activate("main")
UI blank after joining a multiplayer serverYour script likely never calls ss.ui.activate for the surface you draw (e.g. "main"). Late-join sync can restore the UI model, but clients only build the on-screen widgets for the active surface, and that choice is replicated when you call activate.
Multiplayer canvas animation looks “steppy” or delayedUse canvas_begin_update / canvas_end_update (or canvas_with_update) so one logical frame is one server batch when limits allow. On the client PC, try BepInEx ScriptedScreens CanvasRemoteEveryBatchGpuUpload = true so each received batch uploads to the GPU instead of merging in-frame (higher GPU cost).
canvas_begin_update is nilLua runs on the server; that process must load a ScriptedScreens build that registers the API. Update ScriptedScreens.dll on the dedicated server (or host) mods folder, not only the client.
Wrong screenCheck the screen index parameter on ss.ui.surface()
Button clicks not firingMake sure the element is on the active surface and visible
Keyboard doesn't workClick the screen to enter Interface Mode
Exit keyboard modePress ALT or call ss.exit_interface_mode()

⚠️ This documentation was AI-generated and may contain inaccuracies. Please submit pull requests with corrections as needed.