dat.GUI - A lightweight graphical user interface and controller library

dat.GUI - A lightweight graphical user interface and controller library that allows you to easily manipulate variables and fire functions on the fly, inspired by the venerable dat-gui js.


local DatGUI = require(game.ReplicatedStorage:WaitForChild("dat.GUI"))

local obj = {
  Name = "Alex Rodin",
  Num = 1,
  Winner = true

local gui = DatGUI.new()

gui.add(obj, 'Name')
gui.add(obj, 'Num', 1, 50).step(1)
gui.add(obj, 'Winner')




You can do the installation directly from Roblox Studio, through the Toolbox search for dat.GUI , this is the minified version of the engine (dat.GUI - Roblox).

If you want to work with the original source code (for debugging or working on improvements), access the repository at https://github.com/nidorx/roblox-dat-gui

Roblox dat.GUI

Allows you to easily manipulate variables and fire functions on the fly, inspired by the venerable dat-gui.

dat.GUI magically generates a graphical user interface (sliders, color selector, etc) for each of your variables.

dat.gui’s niche is in listening to and controlling data such that it can be visualized into charts or other graphics. Creating a new DAT.GUI instance provides a new sliding pane for which to add controls to:

Use cases:

1 - To visually debug the various variables in your scripts during development
2 - To create a rich interface for your auxiliary tools (map editors, effects editors, etc.)
3 - For the construction of fast administrative interfaces

How to use

In your script, import the dat.GUI and instantiate it

local DatGUI = require(game.ReplicatedStorage:WaitForChild("dat.GUI"))

-- Create an instance, which also creates a UI pane
local gui = DatGUI.new();

Color3, Color3Value


local color3Value = Instance.new('Color3Value')
local Color3Object = {
   Color3 = Color3.fromRGB(255, 0, 255),
   Color3Value = color3Value

gui.add(Color3Object, 'Color3').listen().onChange(function(value)
   assert(Color3Object.Color3.R == value.R)
   assert(Color3Object.Color3.G == value.G)
   assert(Color3Object.Color3.B == value.B)

gui.add(Color3Object, 'Color3Value').listen().onChange(function(value)
   assert(Color3Object.Color3Value.Value.R == value.R)
   assert(Color3Object.Color3Value.Value.G == value.G)
   assert(Color3Object.Color3Value.Value.B == value.B)

Boolean, BoolValue


local boolValue = Instance.new('BoolValue')

local BooleansObject = {
   Bool = true,
   BoolValue = boolValue

gui.add(BooleansObject, 'Bool').listen().onChange(function(value)
   assert(BooleansObject.Bool == value)

gui.add(BooleansObject, 'BoolValue').listen().onChange(function(value)
   assert(BooleansObject.BoolValue.Value == value)

Number, NumberValue


Note: with a number slider available depending on options passed to it

local numberValue = Instance.new('NumberValue')
local NumbersObject = {
   Number = 25,
   NumberSlider = 0.5,
   NumberDouble = 33,
   NumberValue = numberValue,
   NumberValueSlider = numberValue

gui.add(NumbersObject, 'Number').step(1).listen().onChange(function(value)
   assert(NumbersObject.Number == value)

gui.add(NumbersObject, 'NumberSlider', 0, 1).listen().onChange(function(value)
   assert(NumbersObject.NumberSlider == value)

gui.add(NumbersObject, 'NumberDouble').step(0.001).listen().onChange(function(value)
   assert(NumbersObject.NumberDouble == value)

gui.add(NumbersObject, 'NumberValue').listen().onChange(function(value)
   assert(NumbersObject.NumberValue.Value == value)

gui.add(NumbersObject, 'NumberValueSlider', 0, 100).listen().onChange(function(value)
   assert(NumbersObject.NumberValueSlider.Value == value)

String, StringValue


Note: with Multiline option

local stringValue = Instance.new('StringValue')
local StringsObject = {
   String = 'Lorem ipsum dolor',
   StringValue = stringValue,
   StringMultiline = 'Lorem ipsum dolor \nLorem ipsum dolor '

gui.add(StringsObject, 'String').listen().onChange(function(value)
   assert(StringsObject.String == value)

gui.add(StringsObject, 'StringValue').listen().onChange(function(value)
   assert(StringsObject.StringValue.Value == value)

gui.add(StringsObject, 'StringMultiline', true).listen().onChange(function(value)
   assert(StringsObject.StringMultiline == value)

Options = Enum, EnumItem, Array (Strings), Object (Key => Value string)


local OptionsObject = {
   OptionsEnum       = 2,
   OptionsEnumItem   = Enum.ScaleType.Slice,
   OptionsArray      = 1,
   OptionsObject     = 'THREE',

gui.add(OptionsObject, 'OptionsEnum', Enum.ScaleType).listen().onChange(function(value, text)
   assert(OptionsObject.OptionsEnum == value)

gui.add(OptionsObject, 'OptionsEnumItem').listen().onChange(function(value, text)
   assert(OptionsObject.OptionsEnumItem == value)

gui.add(OptionsObject, 'OptionsArray', {'One', 'Two', 'Three'}).listen().onChange(function(value, text)
   assert(OptionsObject.OptionsArray == value)

gui.add(OptionsObject, 'OptionsObject', { ONE = 'One', TWO = 'Two', THREE = 'Three' }).listen().onChange(function(value, text)
   assert(OptionsObject.OptionsObject == value)

Vector3, Vector3Value


Note: with a number slider available depending on options passed to it

local vector3Value = Instance.new('Vector3Value')
local Vector3Object = {
   Vector3 = Vector3.new(10, 11, 12),
   Vector3Slider = Vector3.new(10, 11, 12),
   Vector3Value = vector3Value

gui.add(Vector3Object, 'Vector3').step(1).listen().onChange(function(value)

gui.add(Vector3Object, 'Vector3Slider', 0, 100).listen().onChange(function(value)

gui.add(Vector3Object, 'Vector3Value', 0, 100).listen().onChange(function(value)

Function, removeChild and changeName


local toggleVec3Controller

local Vector3Object = {
   ToggleVector3Slider = function()
      if vec3Controller ~= nil then
         vec3Controller = nil
         vec3Controller = guiVector3.add(Vector3Object, 'Vector3Slider', 0, 100).listen().onChange(function(value)

toggleVec3Controller = gui.add(Vector3Object, 'ToggleVector3Slider')


  • gui.addCustom(name, {Frame, Color, OnRemove, Height})
    • dat_gui_custom
  • controller.help (text)
    • dat_gui_help
  • gui.addLogo(assetId, height)
    • image
  • pin folders
    • dat_gui_pin
  • move (stick when being moved)
    • dat_gui_move
  • resize
    • dat_gui_resize
  • close
    • dat_gui_close
  • actions
    • actions


Folder (GUI)

  • DatGUI.new(params)
  • gui.addFolder(name: string, params)
    • Creates a new GUI/subfolder instance
    • params.name The name of this GUI
      • {string}
    • params.parent The parent GUI
      • {gui} (optional)
    • params.width The initial width of the GUI
      • {number} (optional default=250)
    • params.closeable If true, this GUI can be permanently closed. (Only applicable if parent == nil)
      • {bool} (optional default=false)
    • params.fixed If true, this folder cannot be unpinned from the parent. (Only applicable if parent ~= nil)
      • {bool} (optional default=true)
    • params.closed If true, starts closed
      • {bool} (optional default=false)
  • gui.add(object, property: string, ...arguments) :Controller
    • Adds a new Controller to the GUI. The type of controller created is inferred from the initial value of object[property].
  • gui.addLogo(assetId: string, height: number) :Controller
    • Add a control that displays an image.
  • gui.addCustom(name: string, config: number) :Controller
    • Allows creation of custom controllers
    • config.frame The content that will be presented in the controller
      • {Frame}
    • config.height the height of the content
      • {number}
    • config.color The color of the controller’s side edge
      • {Color3} (optional)
    • config.onRemove Invoked when controller is destroyed
      • {function()} (optional)
    • config.methods Allows you to add custom methods that can be invoked by the controller instance
      • {{ [key:String] => function }} (optional)
  • gui.name(name: string)
    • Sets the name of the gui
  • gui.open()
    • Opens the GUI
  • gui.close()
    • Closes the GUI
  • gui.remove()
    • Removes the GUI and unbinds all event listeners
  • gui.removeChild(child: GUI|Controller)
    • Removes the given controller/folder from the GUI
  • gui.resize(width: number, height: number)
    • Allows to resize the gui. Only applicable when parent=nil or is unpinned from parent
  • gui.move(hor, vert)
    • Updates the position of the instance. Only applicable when parent=nil or is unpinned from parent
    • hor If negative, consider the position from the right edge of the screen
      • {number|"left"|"right"|"center"}
    • vert If negative, consider the position from the bottom edge of the screen
      • {number|"top"|"bottom"|"center"}
  • gui.action(config)
    • Add an action button on the right side of the header
    • config.icon Roblox image asset url (Ex. "rbxassetid://7229830060")
      • {string}
    • config.title Add a HINT on the icon
      • {string} (optional)
    • config.color
      • {Color3} (optional)
    • config.colorHover
      • {Color3} (optional)
    • config.onHover
      • {function(isHover: bool)} (optional)
    • config.onClick
      • {function()} (optional)
    • @return
      • { {frame:Frame, remove:function()} }
  • gui.onRemove(callback: function) :RBXScriptConnection
    • allows to be informed when the gui is removed
  • Gui Fields
    • gui.Panel {Panel}
    • gui.Content {Frame = panel.Content}


  • controller.name(name: string) :Controller
    • Sets the name/label of the controller
  • controller.label(visible: bool) :Controller
    • show/hide label
  • controller.help(text: string) :Controller
    • Add a help box when the cursor is positioned over the controller
  • controller.readonly(value: bool) :Controller
    • Disable editing of values by the controller
  • controller.onChange(callback: function) :Controller
    • Listening to Value Changes
  • controller.getValue() :value
    • Get the object’s value
  • controller.setValue(value) :Controller
    • Sets the object’s value
  • controller.listen() :Controller
    • Sets controller to listen for changes on its underlying object.
  • controller.remove()
    • Removes the controller from its parent GUI and unbinds all event listeners
  • controller.onRemove(callback: function) :RBXScriptConnection
    • allows to be informed when the controller is removed
  • Controller Fields
    • controller.frame {Frame}
    • controller.height {number}


Dat.GUI exposes the various components used internally. For usage details see source code

DatGUI.Lib = {
   Panel       = Panel, 
   Popover     = Popover,
   Scrollbar   = Scrollbar,
   GUIUtils    = GUIUtils,
   GuiEvents   = GuiEvents,
   Constants   = Constants

The basic structure of the GUI. Can be used to create custom dashboards outside of the DAT.GUI framework

  • Panel.new() :Panel
  • panel.attachTo(parent: Frame, isFixed: bool)
    • Add this panel to another element. When doing this, the panel loses some features like scrollbar, resizing and moving
    • isFixed When true, no longer allow unpin this panel
  • panel.detach(closeable: bool)
    • Unpin this panel from the parent element
    • closeable When true, display panel close button, if false, display panel atach button
  • panel.resize(width: number, height: number)
    • Allows you to resize the panel only if it is detached
  • panel.move(hor, vert)
    • Allows the panel to be moved (only when it is not fixed)
    • hor If negative, consider the position from the right edge of the screen
      • {number|"left"|"right"|"center"}
    • vert If negative, consider the position from the bottom edge of the screen
      • {number|"top"|"bottom"|"center"}
  • panel.addAction(config)
    • Add an action button on the right side of the header
    • config.icon Roblox image asset url (Ex. "rbxassetid://7229830060")
      • {string}
    • config.title Add a HINT on the icon
      • {string} (optional)
    • config.color
      • {Color3} (optional)
    • config.colorHover
      • {Color3} (optional)
    • config.onHover
      • {function(isHover: bool)} (optional)
    • config.onClick
      • {function()} (optional)
    • @return
      • { {frame:Frame, remove:function()} }
  • panel.onDestroy(callback: function) :RBXScriptConnection
    • Add a callback to be executed when this panel is destroyed
  • panel.destroy()
    • destroy this instance and unbinds all event listeners
  • panel.updateContentSize()
    • Force update of content size information. Informs parents about the content update and triggers the scrollbar update
  • Panel Fields
    • panel.Frame {Frame}
    • panel.Content {Frame}
    • panel.Header {Frame}
    • panel.Closed {BoolValue}
    • panel.Label {StringValue}

Popover is a tooltip-like component that allows displaying content above other screen elements. Internally used in ColorController and OptionController. There are 4 popover placement options (“left”|“top”|“bottom”|“right”)

  • Popover.new(reference, size, position, offset)
    • Instantiate a new popover
    • reference GUI object used for popover positioning reference
      • {GUIObject|Frame}
    • size Popover dimensions
      • {Vector2}
    • position Preferred popover position.
      • {"left"|"top"|"bottom"|"right"} (optional default=top)
    • offset Allows you to add a margin between the reference object and the popover
      • {number} (optional default=0)
  • popover.resize(size: Vector2)
  • popover.show(chevron: bool, chevronColor: Color3)
  • popover.hide()
  • popover.destroy()
  • Popover Fields
    • panel.Frame {Frame}

Utility methods to facilitate interaction with interface events on elements

  • GuiEvents.onEnter(element :GUIObject|"*", callback :function(isEnter:bool)) :CancelFunction
    • Fired whenever the mouse is over the element (or when it starts a touch event)
  • GuiEvents.onHover(element :GUIObject|"*", callback :function(isHover:bool)) :CancelFunction
    • Fired when the mouse is over the element and this is the element with the highest Z-index
  • GuiEvents.onDown(element :GUIObject|"*", callback :function()) :CancelFunction
    • Fired when the mouse key is pressed over element and this is the element with the highest Z-index
  • GuiEvents.onUp(element :GUIObject|"*", callback :function()) :CancelFunction
    • Fired when the mouse is released over element and this is the element with the highest Z-index
  • GuiEvents.onClick(element :GUIObject|"*", callback :function()) :CancelFunction
    • The click event is sent to an element when the mouse pointer is over the element, and the mouse button
      is pressed and released.
  • GuiEvents.onMove(element :GUIObject|"*", callback :function(position:Vector2)) :CancelFunction
    • Occurs when the mouse is moving over an element
  • GuiEvents.onDrag(element :GUIObject|"*", callback) :CancelFunction
    • Allows dragging elements
    • callback {function(event, startPos:Vector2, position:Vector2, delta:Vector2)}
      • event:"start"|"drag"|"end"
  • GuiEvents.onScroll(element :GUIObject|"*", callback :function(z:number)) :CancelFunction
    • Allows you to watch the mouse scroll over elements

For more details see the source code of SHOWCASE


You can contribute in many ways to this project.

Translating and documenting

I’m not a native speaker of the English language, so you may have noticed a lot of grammar errors in this documentation.

You can FORK this project and suggest improvements to this document (https://github.com/nidorx/roblox-dat-gui/edit/master/README.md).

If you find it more convenient, report a issue with the details on GitHub issues.

Reporting Issues

If you have encountered a problem with this component please file a defect on GitHub issues.

Describe as much detail as possible to get the problem reproduced and eventually corrected.

Fixing defects and adding improvements

  1. Fork it (https://github.com/nidorx/roblox-dat-gui/fork)
  2. Commit your changes (git commit -am 'Add some fooBar')
  3. Push to your master branch (git push)
  4. Create a new Pull Request


This code is distributed under the terms and conditions of the MIT license.


Nice one!
That looks a really good library!
Can you upload a copy to GitHub, so we can send PR’s etc?


I haven’t looked in the source yet but I am wondering did you use roact to create the components, or is it created by instances?

1 Like

I am seeing how to migrate to a Rojo project (never used it). I tried rojo-rbx but it didn’t generate the GUI templates. If you know how to migrate feel free.


1 Like

I did not use Roact only Script and ScreenGui, the project started only with a GUI to update the value of a Vector3

I made a pull request to your GitHub repository with your project in Rojo format :smiley:

Have a great day!


PR accepted on Github (https://github.com/nidorx/roblox-dat-gui), thank you very much @goldenstein64 for the contribution (I wouldn’t know how to convert to a Rojo project), I’m adding some test scripts to facilitate the development.

1 Like

Thanks to @aaron_mccoy for the feedback, I added it on the roadmap, with the conversion to the Rojo project done by @goldenstein64 it will be possible to do a lot of cool things in this project. :smile:

1 Like

Hey! A few questions, are you supposed to assign to a frame, gui object of some sort, and if so how.
If not, then does it create a frame, and is there any way to control the position of it inside the script. Other than that I can see myself using this in the near future, really nice plugin keep it up!

I intend to make it more dynamic, allowing it to be used within other elements for example (and from external information such as height and width, internally manage the behavior [scroll, etc.]).

But I confess that it is not my main focus at the moment (I am focused on a game I am developing).

But an important detail, the source code is available on Github, take a look there, very easy to test with Rojo.

Update v1.1

  • Fixed several bugs
  • Removal of duplicate codes
  • Various code improvements

The code in its current state is ready to receive the expected evolutions, among them:

  • Allow themes
  • Resizing
  • Individual panels
  • Inject in external panels
  • Allow custom controllers

Update v1.2

  • Added support for StringValue, NumberValue, Vector3Value, Color3Value, BoolValue
  • Correction in the removeChild method
  • Added Multiline String

Update v1.2.2

  • Fixed initialization of numbers (Min, Max, Value)

This. Is. Amazing. I just made a gui library right now - and oh my god - yours is so much better.
I kinda regret making the library now.

1 Like

Another thing, why is the module obfuscated? I don’t want some backdoor in my game.

It is minified for ease of use (copying just one file is easier to work with). There is no backdoor, The source code is available on Github, you can generate the minified version itself if you want, or even use it without the minification (you only have to copy more files, as it is modularized).

If you want to work with the original source code, access the repository at GitHub - nidorx/roblox-dat-gui

1 Like

I’m working on some improvements (because I use it to make my tools).

Among the improvements in progress are: Correction of Color and Options (sometimes it is getting stuck), allowing to add custom components, controllers for drawing/plot (Lines, Curves, etc.)


BIG Update v1.2.3

  • Allows custom controllers gui.addCustom (name, {Frame, Color, OnRemove, Height})
  • Added the controller.help (text) method
  • Added the gui.addLogo (assetId, height) method
  • Allows you to pin folders
  • Allows to Move (Panels stick when being moved)
  • Allows resizing
  • Allows you to close
  • Bug fixes in OptionController and ColorController
  • Improved Scroll (works with Drag)
  • Compatibility with touch devices (Mobile, Tablet)
  • Visual and event capture improvements

This looks very cool!
Is this supported on mobile? The example place only works on pc.

Update v1.2.4

  • Improved documentation (https://github.com/nidorx/roblox-dat-gui)
  • Allows to add actions
    • actions
  • API exposure
    DatGUI.Lib = {
       Panel       = Panel, 
       Popover     = Popover,
       Scrollbar   = Scrollbar,
       GUIUtils    = GUIUtils,
       GuiEvents   = GuiEvents,
       Constants   = Constants