GUI controls issues

Hi, guys !

I am very impressed by quality and simplicity of ‘lil-gui’ embedded in three.js and how it works in XR, but found few issues with ‘options’, ‘string’, ‘number’ and ‘boolean’ - they refuses to work in XR and non-XR modes, example code is here: Glitch.

Moreover, ‘colorpicker’ is not working in Oculus Go browser, but this is not a problem to me.

I am using as reference. Please, tell me, if I am doing something wrong, or maybe the ‘lil-gui’ functions in three.js are really incomplete ?

Any suggestions about best practice of embedding GUI in XR are also welcome.

Thanks in advance.

Officially lil-gui does not support WebXR or WebGL rendering. The “glue” here is the HTMLMesh class, which as you can gather from its short (500 lines of code) implementation, draws shapes and propagates events, but cannot replicate form elements and all of their complexity. Perhaps that can be improved, but there are limits on the approach.

Ideally you’d want a UI library that is actually made for WebXR. I’m not sure what the best options here currently would be…


Thanks, I see. At present time I will stick to lil-gui, while searching for something more serious.
In any case it’s better than DIY ).

1 Like

I’ve been working on a native three.js version of lil-gui. As you can imagine, that’s not an easy task. I have to build each UI element with basic features that match HTML. Here’s a screen shot.

It will work in VR, but is currently missing a VR keyboard. I’m working to add that.

When not in VR, it supports keyboard input, tabbing between enable fields, etc.

For now, its just part of an example of three-flow diagramming library, but will be a separate library by end of February.

I’ll add a full demo to three-flow by end of next week.

Just to prove its native three.js and not some CSS magic, here’s another camera angle


Thanks, looks attractive and sounds promising !

I know, the GUI is very time-consuming work, because it assumes a detailed approach, so wish you good luck and patience and will be glad to try it, as all my three.js projects assumes XR mode. In general I can stay with present three.js lil-gui.module.js, but will be happy to have following features presently not implemented (in order of descending of desire):

  • Checkboxes
  • Button response to click (shading effect or maybe color change)
  • Rounded corners not only of control, but of GUI object itself
  • Text area rendered with high quality for annotations
  • Folders
  • Options
  • Keyboard input

While looking for solution I found this guy, who also was working on XR-GUI and achieved some good results, especially in text rendering and keyboard input, but, unfortunately, with lack of other necessary controls. Maybe you can use some of his code in your work.

Please keep me informed about your progress.

It should be a drop-in replacement for lil-gui. Some advanced features might be missing. The GUI and controller model is separate from this implementation.

The three.js version is extensible. You can override with your own controls if you want to customize the defaults, but assume they derived from a common base class.

All of the above are implemented, except for text area. Lil-gui doesn’t use text area, just single line text input. Drop-down list even supports a scroll bar!

The GUI attempts to re-use geometry and materials where it can and will support some basic theming too. It is geometry based (rather than a canvas like HTML mesh), so does impact the scene graph and render loop.

I should support (and demonstrate) putting the GUI in a layer so its easy to include and exclude rather than using visible and rebuilding each time.

As you know, three.js has no automatic layout/positioning mechanism. Once all the major UI controls are implemented, I’ll take a look at three-mesh-ui to see if it can help with this.

1 Like

A WebXR-friendly lil-gui would be something very useful. However, I’m a bit hesitant in respect to this “drop-in replacement”, as CSS styling might be considered as advanced features. Here is an example of lil-gui with custom CSS:

1 Like

It should be possible to customize it like this. Challenge accepted.

1 Like

Very intriguing, looking forward to trying it, good luck !

we are also working on a UI kit, though the scale will be bigger than lil-guy, it’s basically shadcn (or bootstrap) for threejs. though it will be component oriented. GitHub - coconut-xr/koestlich: user interfaces for three.js

it has support for themes as well GitHub - coconut-xr/apfel-kruemel: Pre-Designed Component Library for Spatial User Interfaces

here’s a live demo Apfel Kruemel

and one more (plus code) koestlich-dashboard-example - CodeSandbox

this will move to pmndrs soon. we will try to separate some stuff out that can live in vanilla.


Looks beautiful, but unfortunately for unknown reasons I can’t enter AR mode at Apfel Kruemel examples page. All other stuff work slowly, it seems to me that my lovely working horse - Oculus Go is completely outdated (.

i don’t know, i have none to check. but there are already real apps out using it, the ovi app for instance

all the ui you see in the video is made by the uikit.

What device?

Also note that iOS does not have any of the standard WebXR APIs, so nothing AR will work there except handing off a USDZ model to the iOS Quick Look viewer.

As I mentioned in my last post - Oculus Go.

With some customization of the basic UI properties control, this is what’s possible so far.

Three-Flow Custom Properties

The customization code approximates your CSS changes

const gui = this.makeCustomGUI()
const params: PropertiesParameters = {
  width: 1.3,
  fill: { color: 'black' },
  inputMaterial: { color: '#424242' },
  labelwidth: 0.45,
  pickwidth: 0.6,
const properties1 = new CustomProperties(params, app.interactive, options, gui)

class CustomProperties extends UIProperties {

  constructor(private customparams: PropertiesParameters, interactive: ThreeInteractive, options: PanelOptions, gui: GUI) {
    super(customparams, interactive, options, gui)

  override createLabel(parameters: LabelParameters): UILabel {
    parameters.material!.color = 'gray'
    return new UILabel(parameters, this.options)

  override createTextButton(parameters: TextButtonParameters): UIButton {
    parameters.label!.material = { color: 'white' }
    return new UITextButton(parameters, this.interactive, this.options)

  override createExpansionPanel(parameters: ExpansionPanelParameters): UIExpansionPanel {
    parameters.fill = this.customparams.fill
    parameters.label!.material = { color: 'white' }
    parameters.panel!.fill = this.customparams.fill
    parameters.indicatorMaterial = { color: 'white' }
    return new UIExpansionPanel(parameters, this.interactive, this.options)

  override createSliderbar(parameters: SliderbarParameters): UISliderbar {
    parameters.slidermaterial = { color: 'orange' }
    return new UISliderbar(parameters, this.interactive, this.options)

  override createNumberEntry(parameters: NumberEntryParameters, title: string): UINumberEntry {
    parameters.label = { material: { color: 'orange' } }
    if (title == 'level') {
      return new CustomNumberEntry(parameters, this.interactive, this.options, this.radius * 3, this.radius)
    else if (title == 'gloss') {
      return new CustomNumberEntry(parameters, this.interactive, this.options, this.radius, this.radius * 3)
      return new UINumberEntry(parameters, this.interactive, this.options)

  override createTextEntry(parameters: TextEntryParameters): UITextEntry {
    if (!parameters.label) parameters.label = {}
    parameters.label.material = { color: 'orange' }
    return new UITextEntry(parameters, this.interactive, this.options)

  override createSelect(parameters: SelectParameters): UISelect {
    parameters.label!.material = { color: 'white' }
    parameters.indicatorMaterial = { color: 'white' }
    return new UISelect(parameters, this.interactive, this.options)

class CustomNumberEntry extends UINumberEntry {
  constructor(parameters: NumberEntryParameters, interactive: ThreeInteractive, options: NumberOptions, private radiustr: number, private radiusbr: number) {
    super(parameters, interactive, options)

  override panelShape(): Shape {
    return new CustomRectangleShape(this.width, this.height, this.radius, this.radiustr, this.radiusbr)

  override panelBorderShape(borderWidth: number): Shape {
    return new CustomRectangleBorderShape(this.width, this.height, this.radius, this.radiustr, this.radiusbr, borderWidth)


Hey gang… ZIM has TextureActives for three.js and works with VR. Here are a couple resource posts:

and for VR:

Thanks, I will try it.

1 Like