describeChildren () {
    if (this.isXR) {
      // In XR mode, use our WebGL dat-gui implementation and put it inside
      // the wrist-mounted UI container
      return {
        facade: WristMountedUI,
        children: {
          facade: BottomAnchoredDatGuiFacade,
          scale: 0.75,
          maxHeight: 0.48,
          overflow: 'scroll',
          items: [{
            type: 'button',
            label: 'Exit XR',
            onClick: () => {
              this.notifyWorld('endXRSession')
            }
          }].concat(this.items),
          data: this.data,
          onUpdate: this.onUpdate
        }
      }
    } else {
      // On screen, use react-dat-gui to avoid the perspective distortion at the
      // screen edge and so we don't have to worry about scaling. Renders into a
      // portal element so it isn't position-synced.
      return {
        facade: HtmlOverlay3DFacade,
        html: <ReactPortalContainer>
          <DatGui data={this.data} onUpdate={this.onUpdate}>
            {this.items.map((item, i) => {
              if (!item) { return null }
              item = utils.assign({key: i}, item)
              let Cmp = reactCmpMap[item.type]
              if (item.type === 'select') {
                item.optionLabels = []
                item.options = item.options.map(opt => {
                  if (opt && typeof opt === 'object') {
                    item.optionLabels.push(opt.label || opt.value)
                    return opt.value
                  } else {
                    item.optionLabels.push(opt)
                    return opt
                  }
                })
              }
              return <Cmp {...item} />
            })}
          </DatGui>
        </ReactPortalContainer>
      }
    }
  }
@member {boolean} isXR
@member {object} data
@member {function} onUpdate