lil-gui

Makes a floating panel for controllers on the web. Works as a drop-in replacement for dat.gui.

Basic DemoExamplesGuideAPIGitHub

import GUI from 'lil-gui'; 

const gui = new GUI();

const myObject = {
	myBoolean: true,
	myFunction: function() { ... },
	myString: 'lil-gui',
	myNumber: 1
};

gui.add( myObject, 'myBoolean' );  // Checkbox
gui.add( myObject, 'myFunction' ); // Button
gui.add( myObject, 'myString' );   // Text Field
gui.add( myObject, 'myNumber' );   // Number Field

// Add sliders to number fields by passing min and max
gui.add( myObject, 'myNumber', 0, 1 );
gui.add( myObject, 'myNumber', 0, 100, 2 ); // snap to even numbers

// Create dropdowns by passing an array or object of named values
gui.add( myObject, 'myNumber', [ 0, 1, 2 ] );
gui.add( myObject, 'myNumber', { Label1: 0, Label2: 1, Label3: 2 } );

// Chainable methods
gui.add( myObject, 'myProperty' )
	.name( 'Custom Name' )
	.onChange( value => {
		console.log( value );
	} );

// Create color pickers for multiple color formats
const colorFormats = {
	string: '#ffffff',
	int: 0xffffff,
	object: { r: 1, g: 1, b: 1 },
	array: [ 1, 1, 1 ]
};

gui.addColor( colorFormats, 'string' );
Built SourceMinified = 29.1kb, 8.3kb gzipped

Examples

Hot Swaps

  • three.js/examples - Replaces dat.gui across all three.js example pages. Except for a few, this was done just by replacing the import URL.
  • PixiJS Filters Demo - Replaces a very long dat.gui.

Advanced

  • Undo / Redo - Implements an undo / redo stack using gui.onFinishChange.
  • Save Server - Uses a node server to write gui.save() values to a file on disk.
  • Auto GUI - Defines a declarative syntax for adding controllers.
  • Multi-Line Controller - Creates a custom controller using a native form element.
  • Bezier Controller - Creates a controller that manipulates a complex data-type.

Guide

lil-gui gives you an interface for changing the properties of any JavaScript object at runtime. It's intended as a drop-in replacement for dat.gui, implemented with more modern web standards and some new quality of life features.

If you've used dat.gui before, the beginning of this guide will be review. The Migrating section points out the notable differences between the two libraries.

Installation

You can install lil-gui with npm for use with a bundler.

$ npm install lil-gui --save-dev
import GUI from 'lil-gui';

For quick sketches, you can import lil-gui directly from a CDN.

<script type="module">
import GUI from 'https://cdn.jsdelivr.net/npm/lil-gui@0.15/+esm';
</script>

The library is also available in UMD format under the namespace lil.

<script src="https://cdn.jsdelivr.net/npm/lil-gui@0.15"></script>
<script>
var GUI = lil.GUI;
</script>

Adding Controllers

This code creates an input that lets you change this page's title.

const gui = new GUI();
gui.add( document, 'title' );

lil-gui will choose an appropriate controller type based on the property's value when it was added to the GUI. Since document.title was a string, a text field was created.

Here are some more of the variable types you can control:

obj = {
	myBoolean: true,
	myString: 'lil-gui',
	myNumber: 1,
	myFunction: function() { alert( 'hi' ) }
}

gui.add( obj, 'myBoolean' ); 	// checkbox
gui.add( obj, 'myString' ); 	// text field
gui.add( obj, 'myNumber' ); 	// number field
gui.add( obj, 'myFunction' ); 	// button

Numbers and Sliders

Numbers can be constrained to a range using min() and max(). You can use step() to round values to multiples of a given number.

obj = { hasMin: 1, hasMax: 99, hasStep: 50 }

gui.add( obj, 'hasMin' ).min( 0 );
gui.add( obj, 'hasMax' ).max( 100 );
gui.add( obj, 'hasStep' ).step( 10 );

Number controllers with a minimum and a maximum automatically become sliders. You can use an abbreviated syntax to define them both at once.

obj = { number1: 1, number2: 50 }

gui.add( obj, 'number1', 0, 1 ); // min, max
gui.add( obj, 'number2', 0, 100, 10 ); // min, max, step

Dropdowns

You can create a dropdown for any data type by providing an array of accepted values. If you pass an object, its keys will be used as labels for the options.

obj = { size: 'Medium', speed: 1 }

gui.add( obj, 'size', [ 'Small', 'Medium', 'Large' ] )
gui.add( obj, 'speed', { Slow: 0.1, Normal: 1, Fast: 5 } )

Colors

lil-gui recognizes colors in a number of formats: CSS strings, RGB objects or integer hex values to name a few. You can use addColor() to create a color picker for controlling these values. lil-gui uses an RRGGBB format for display, but it always honors the original data type when updating colors.

obj = {
	color1: '#AA00FF',
	color2: '#a0f',
	color3: 'rgb(170, 0, 255)',
	color4: 0xaa00ff
}

gui.addColor( obj, 'color1' );
gui.addColor( obj, 'color2' );
gui.addColor( obj, 'color3' );
gui.addColor( obj, 'color4' );

RGB Objects & Arrays

Some libraries use objects or arrays of RGB values to describe colors. These can also be controlled by addColor(). The color channels are assumed to be between 0 and 1, but you can also set your own range. Color objects and arrays are never replaced—only their components are modified.

obj = {
	colorObject: { r: 0.667, g: 0, b: 1 },
	colorArray: [ 0.667, 0, 1 ]
}

gui.addColor( obj, 'colorObject' );
gui.addColor( obj, 'colorArray' );

RGB Channel Ranges

The channel range for RGB objects and arrays can be overriden per controller by passing a third parameter to addColor(). If your colors are coming out too dark, you might need to set this to 255.

obj = {
	colorObject: { r: 170, g: 0, b: 255 },
	colorArray: [ 170, 0, 255 ]
}

gui.addColor( obj, 'colorObject', 255 );
gui.addColor( obj, 'colorArray', 255 );

Folders

You can organize controllers in collapsible groups using addFolder(). The method returns a new GUI instance representing the folder. You can add controllers to the folder just like you would with any GUI.

// top level controller
gui.add( obj, 'scale', 0, 1 );

// nested controllers
const folder = gui.addFolder( 'Position' );
folder.add( obj, 'x' );
folder.add( obj, 'y' );
folder.add( obj, 'z' );

Change Events

If you want to call a function every time a controller is changed, you can pass it to the controller's onChange method. The new value will be passed to your function after every change (so long as it originates from that controller and not from code elsewhere).

gui.add( params, 'foo' ).onChange( value => {
	console.log( value );
} );

The onFinishChange handler fires after a controller changes and loses focus. This comes in handy if you're using a slow function with a controller that produces continuous change events (like numbers or colors for example).

gui.add( params, 'mySlider', 0, 1 ).onFinishChange( complexFunction );

Global Change Handlers

GUI also provides an onChange handler that fires after changes to any of its children. These handlers receive an event object with details about the controller that was modified.

gui.onChange( event => {

	event.object     // object that was modified
	event.property   // string, name of property
	event.value      // new value of controller
	event.controller // controller that was modified

} );

GUI.onChange events bubble upward. A handler applied to the root GUI will fire after every change. Handlers applied to folders will only be called after changes to that folder or its descendents.

GUI.onFinishChange works just like GUI.onChange, but it only fires at the end of change events.

Listening and Updating

If a value controlled by the GUI is changed in code anywhere outside of the GUI, the new value won't be reflected by the controller's display. You can call listen() to update the controller's display every frame.

gui.add( params, 'feedback', -1, 1 )
   .listen()
   .disable();

animate() {
	params.feedback = Math.sin( Date.now() / 1000 );
}

You can also call controller.updateDisplay() at any time to manage this behavior yourself.

Saving

Using gui.save() you can create an object that saves the current value of all properties added to the GUI. You can pass that object to gui.load() to restore the saved values.

The following creates a GUI that can save a preset. Press the savePreset button, then modify any controller. Pressing the recallPreset button restores the values you saved.

let preset = {};

const obj = {
	value1: 'original',
	value2: 1996,
	savePreset() {
		// save current values to an object
		preset = gui.save();
		loadButton.enable();
	},
	loadPreset() {
		gui.load( preset );
	}
}

gui.add( obj, 'value1' );
gui.add( obj, 'value2' );

gui.add( obj, 'savePreset' );

const loadButton = 
	gui.add( obj, 'loadPreset' )
	   .disable();

Save Object Format

The following is an example of an object returned by gui.save(). The object will be JSON compatible. It can be saved to disk, unless you're using non-primitive data types in a dropdown (color objects and arrays are fine).

{
	controllers: {
		value1: 'original',
		value2: 1996,
	},
	folders: {
		// if GUI has folders ...
		folderName1: { controllers, folders },
		folderName2: { controllers, folders }
		...
	}
}

Both save and load accept a recursive parameter, which is true by default. Use save( false ) and load( data, false ) to ignore any folders within the GUI. The saved object will contain an empty folders object.

Name Collisions

save() will throw an error if the GUI contains more than one controller or folder with the same name. You can avoid these collisions by renaming the controllers with name().

gui.add( position, 'x' ).name( 'position.x' );
gui.add( rotation, 'x' ).name( 'rotation.x' );

Styling

By default, the GUI is added to document.body and attached to the top right of the window with fixed positioning. You can add the GUI to a different element by passing a container parameter to the constructor.

const gui = new GUI( { container: $('#gui') } );

Width and Long Names

The GUI can be made wider by passing a pixel width to the constructor. This is usually done when controller names are too long to fit within the panel.

const gui = new GUI( { width: 400 } );

The library provides a few ways to manage this using CSS variables as well.

.lil-gui { 
	--width: 400px;
	--name-width: 65%;
}

The --width property does the same thing the one in the constructor, but allows us to use any valid CSS value. Adjusting --name-width allows you to increase the size of names relative to controllers, which might be better than enlarging the entire panel.

CSS Variables and Custom Stylesheets

lil-gui exposes a number of CSS variables that allow you to customize colors and dimensions. You can see an exhaustive list of these variables in the Kitchen Sink demo.

.lil-gui { 
	--background-color: #000;
	--widget-color: #0af;
	--padding: 2px;
}

If you want to start a new stylesheet from scratch, the default styles can be left out entirely with the injectStyles parameter.

new GUI( { injectStyles: false } );

Touch Styles

Controllers are larger on touch devices to make them easier to use. By default, these styles are applied using a CSS query @media (pointer: coarse). You can disable this behavior with the touchStyles parameter.

gui = new GUI( { touchStyles: false } );
gui.domElement.classList.add( 'force-touch-styles' );

You can then apply these styles at a time of your choosing by adding the .force-touch-styles CSS class to the GUI's root element.

Migrating

For most projects, moving from dat.gui to lil-gui should be as simple as changing the import URL. The API is designed to be as backwards-compatible as is reasonably possible, but this section aims to address any breaking changes.

API Changes

  • gui.__children is now gui.children.
  • gui.__folders is now gui.folders and it's an array, not a map.
  • gui.remove( controller ) is now controller.destroy()
  • gui.removeFolder( folder ) is now folder.destroy()
  • Folders are open by default.

DOM Structure

The DOM structure of the GUI has changed, so code that interacts with dat.gui's inner DOM elements is likely to break.

  • gui.__ul is now gui.$children.
  • gui.__closeButton is now gui.$title.
  • domElement is still domElement for both Controller and GUI.

CSS class names are also different:

  • .dg.ac becomes .lil-gui.autoPlace

Color Controller Changes

There's one major difference in the way dat.gui and lil-gui handle color controllers: channel ranges for RGB objects and RGB arrays are assumed to be in the range of [0-255] in dat.gui and [0-1] in lil-gui.

In general, this shouldn't have much of an impact, as it's common practice to use hex values and an onChange handler when using dat.gui with a library like three.js that expects RGB [0-1].

// common three.js + dat.gui color pattern
params = { color: color.getHex() };

dat_gui.addColor( params, 'color' ).onChange( v => {
    color.setHex( v ) 
} );

Since lil-gui and three.js agree on RGB ranges, this code can be simplified:

params = { color };

lil_gui.addColor( params, 'color' );

The other differences in color handling are fairly minor:

  • lil-gui uses the native HTML input[type=color] tag instead of a custom color picker.
  • lil-gui doesn't support any HSL color formats.

Removed

  • "Presets" and gui.remember() are gone in favor of save/load(), which also removes mention of localStorage.
  • gui.hide/show/hideAll() and the H to hide hotkey.

API

GUI (Class)

new GUI( { autoPlace, container, width, title, injectStyles, touchStyles, parent } )

Creates a panel that holds controllers.

new GUI();
new GUI( { container: document.getElementById( 'custom' ) } );

Parameters

  • autoPlace - Adds the GUI to document.body and fixes it to the top right of the page.
    Default: true

  • container - Adds the GUI to this DOM element. Overrides autoPlace.
    Optional: HTMLElement

  • width - Width of the GUI in pixels, usually set when name labels become too long. Note that you can make name labels wider in CSS with .lil‑gui { ‑‑name‑width: 55% }
    Default: 245

  • title - Name to display in the title bar.
    Default: Controls

  • injectStyles - Injects the default stylesheet into the page if this is the first GUI. Pass false to use your own stylesheet.
    Default: true

  • touchStyles - Makes controllers larger on touch devices. Pass false to disable touch styles.
    Default: true

  • parent - Adds this GUI as a child in another GUI. Usually this is done for you by addFolder().
    Optional: GUI

src/GUI.js:49

gui.add( object, property, [$1], [max], [step] )

Adds a controller to the GUI, inferring controller type using the typeof operator.

gui.add( object, 'property' );
gui.add( object, 'number', 0, 100, 1 );
gui.add( object, 'options', [ 1, 2, 3 ] );

Parameters

  • object - The object the controller will modify.
    Required: object

  • property - Name of the property to control.
    Required: string

  • $1 - Minimum value for number controllers, or the set of selectable values for a dropdown.
    Optional: number or object or Array

  • max - Maximum value for number controllers.
    Optional: number

  • step - Step value for number controllers.
    Optional: number

Returns: Controller

src/GUI.js:192

gui.addColor( object, property, rgbScale=1 )

Adds a color controller to the GUI.

params = {
	cssColor: '#ff00ff',
	rgbColor: { r: 0, g: 0.2, b: 0.4 },
	customRange: [ 0, 127, 255 ],
};

gui.addColor( params, 'cssColor' );
gui.addColor( params, 'rgbColor' );
gui.addColor( params, 'customRange', 255 );

Parameters

  • object - The object the controller will modify.
    Required: object

  • property - Name of the property to control.
    Required: string

  • rgbScale - Maximum value for a color channel when using an RGB color. You may need to set this to 255 if your colors are too dark.
    Default: 1

Returns: Controller

src/GUI.js:249

gui.addFolder( title )

Adds a folder to the GUI, which is just another GUI. This method returns the nested GUI so you can add controllers to it.

const folder = gui.addFolder( 'Position' );
folder.add( position, 'x' );
folder.add( position, 'y' );
folder.add( position, 'z' );

Parameters

  • title - Name to display in the folder's title bar.
    Required: string

Returns: GUI

src/GUI.js:265

gui.load( obj, recursive=true )

Recalls values that were saved with gui.save().

Parameters

  • obj
    Required: object

  • recursive - Pass false to exclude folders descending from this GUI.
    Default: true

Returns: this

src/GUI.js:275

gui.save( recursive=true )

Returns an object mapping controller names to values. The object can be passed to gui.load() to recall these values.

{
	controllers: {
		prop1: 1,
		prop2: 'value',
		...
	},
	folders: {
		folderName1: { controllers, folders },
		folderName2: { controllers, folders }
		...
	}
}

Parameters

  • recursive - Pass false to exclude folders descending from this GUI.
    Default: true

Returns: object

src/GUI.js:327

gui.open( open=true )

Opens a GUI or folder. GUI and folders are open by default.

gui.open(); // open
gui.open( false ); // close
gui.open( gui._closed ); // toggle

Parameters

  • open - Pass false to close
    Default: true

Returns: this

src/GUI.js:373

gui.close()

Closes the GUI.

Returns: this

src/GUI.js:388

gui.title( title )

Change the title of this GUI.

Parameters

  • title
    Required: string

Returns: this

src/GUI.js:437

gui.reset( recursive=true )

Resets all controllers to their initial values.

Parameters

  • recursive - Pass false to exclude folders descending from this GUI.
    Default: true

Returns: this

src/GUI.js:452

gui.onChange( callback )

Pass a function to be called whenever a controller in this GUI changes.

gui.onChange( event => {
	event.object     // object that was modified
	event.property   // string, name of property
	event.value      // new value of controller
	event.controller // controller that was modified
} );

Parameters

  • callback
    Required: function

Returns: this

src/GUI.js:470

gui.onFinishChange( callback )

Pass a function to be called whenever a controller in this GUI has finished changing.

gui.onFinishChange( event => {
	event.object     // object that was modified
	event.property   // string, name of property
	event.value      // new value of controller
	event.controller // controller that was modified
} );

Parameters

  • callback
    Required: function

Returns: this

src/GUI.js:503

gui.destroy()

Destroys all DOM elements and event listeners associated with this GUI

src/GUI.js:527

gui.controllersRecursive()

Returns an array of controllers contained by this GUI and its descendents.

Returns: Controller[]

src/GUI.js:546

gui.foldersRecursive()

Returns an array of folders contained by this GUI and its descendents.

Returns: GUI[]

src/GUI.js:558

gui.children : Array<GUI|Controller>

The list of controllers and folders contained by this GUI.

src/GUI.js:75

gui.controllers : Controller[]

The list of controllers contained by this GUI.

src/GUI.js:81

gui.domElement : HTMLElement

The outermost container element.

src/GUI.js:100

gui.folders : GUI[]

The list of folders contained by this GUI.

src/GUI.js:87

gui.parent : GUI

The GUI containing this folder, or undefined if this is the root GUI.

src/GUI.js:63

gui.root : GUI

The top level GUI containing this folder, or this if this is the root GUI.

src/GUI.js:69

gui.$children : HTMLElement

The DOM element that contains children.

src/GUI.js:128

gui.$title : HTMLElement

The DOM element that contains the title.

src/GUI.js:107

gui._closed : boolean

Used to determine if the GUI is closed. Use gui.open() or gui.close() to change this.

src/GUI.js:94

gui._title : string

Current title of the GUI. Use gui.title( 'Title' ) to modify this value.

src/GUI.js:442

Controller (Class)

controller.name( name )

Sets the name of the controller and its label in the GUI.

Parameters

  • name
    Required: string

Returns: this

src/Controller.js:91

controller.onChange( callback )

Pass a function to be called whenever the value is modified by this controller. The function receives the new value as its first parameter. The value of this will be the controller.

const controller = gui.add( object, 'property' );

controller.onChange( function( v ) {
	console.log( 'The value is now ' + v );
	console.assert( this === controller );
} );

Parameters

  • callback
    Required: function

Returns: this

src/Controller.js:115

controller.onFinishChange( callback )

Pass a function to be called after this controller has been modified and loses focus.

const controller = gui.add( object, 'property' );

controller.onFinishChange( function( v ) {
	console.log( 'Changes complete: ' + v );
	console.assert( this === controller );
} );

Parameters

  • callback
    Required: function

Returns: this

src/Controller.js:153

controller.reset()

Sets the controller back to its initial value.

Returns: this

src/Controller.js:187

controller.enable( enabled=true )

Enables this controller.

controller.enable();
controller.enable( false ); // disable
controller.enable( controller._disabled ); // toggle

Parameters

  • enabled
    Default: true

Returns: this

src/Controller.js:202

controller.disable( disabled=true )

Disables this controller.

controller.disable();
controller.disable( false ); // enable
controller.disable( !controller._disabled ); // toggle

Parameters

  • disabled
    Default: true

Returns: this

src/Controller.js:215

controller.options( options )

Destroys this controller and replaces it with a new option controller. Provided as a more descriptive syntax for gui.add, but primarily for compatibility with dat.gui.

Use caution, as this method will destroy old references to this controller. It will also change controller order if called out of sequence, moving the option controller to the end of the GUI.

// safe usage

gui.add( object1, 'property' ).options( [ 'a', 'b', 'c' ] );
gui.add( object2, 'property' );

// danger

const c = gui.add( object1, 'property' );
gui.add( object2, 'property' );

c.options( [ 'a', 'b', 'c' ] );
// controller is now at the end of the GUI even though it was added first

assert( c.parent.children.indexOf( c ) === -1 )
// c references a controller that no longer exists

Parameters

  • options
    Required: object or Array

Returns: Controller

src/Controller.js:260

controller.min( min )

Sets the minimum value. Only works on number controllers.

Parameters

  • min
    Required: number

Returns: this

src/Controller.js:273

controller.max( max )

Sets the maximum value. Only works on number controllers.

Parameters

  • max
    Required: number

Returns: this

src/Controller.js:283

controller.step( step )

Sets the step. Only works on number controllers.

Parameters

  • step
    Required: number

Returns: this

src/Controller.js:293

controller.listen( listen=true )

Calls updateDisplay() every animation frame. Pass false to stop listening.

Parameters

  • listen
    Default: true

Returns: this

src/Controller.js:302

controller.getValue()

Returns object[ property ].

Returns: any

src/Controller.js:333

controller.setValue( value )

Sets the value of object[ property ], invokes any onChange handlers and updates the display.

Parameters

  • value
    Required: any

Returns: this

src/Controller.js:342

controller.updateDisplay()

Updates the display to keep it in sync with the current value. Useful for updating your controllers when their values have been modified outside of the GUI.

Returns: this

src/Controller.js:354

controller.destroy()

Destroys this controller and removes it from the parent GUI.

src/Controller.js:371

controller.domElement : HTMLElement

The outermost container DOM element for this controller.

src/Controller.js:45

controller.initialValue : any

The value of object[ property ] when the controller was created.

src/Controller.js:39

controller.object : object

The object this controller will modify.

src/Controller.js:20

controller.parent : GUI

The GUI that contains this controller.

src/Controller.js:14

controller.property : string

The name of the property to control.

src/Controller.js:26

controller.$disable : HTMLElement

The DOM element that receives the disabled attribute when using disable()

src/Controller.js:70

controller.$name : HTMLElement

The DOM element that contains the controller's name.

src/Controller.js:53

controller.$widget : HTMLElement

The DOM element that contains the controller's "widget" (which differs by controller type).

src/Controller.js:63

controller._disabled : boolean

Used to determine if the controller is disabled. Use controller.disable( true|false ) to modify this value

src/Controller.js:33

controller._listening : boolean

Used to determine if the controller is currently listening. Don't modify this value directly. Use the controller.listen( true|false ) method instead.

src/Controller.js:309

controller._name : string

The controller's name. Use controller.name( 'Name' ) to modify this value.

src/Controller.js:96

controller._onChange : function

Used to access the function bound to onChange events. Don't modify this value directly. Use the controller.onChange( callback ) method instead.

src/Controller.js:121

controller._onFinishChange : function

Used to access the function bound to onFinishChange events. Don't modify this value directly. Use the controller.onFinishChange( callback ) method instead.

src/Controller.js:159