Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/hetari/creative-coding/llms.txt

Use this file to discover all available pages before exploring further.

LilGui wraps the lil-gui library and exposes a controls prop — an array of control descriptors, each linked to a Vue ref. Dragging a slider, picking a color, or choosing a dropdown option writes the new value directly back into the bound ref, making the change instantly reactive anywhere that ref is consumed. Equally, if your sketch code updates a ref programmatically, LilGui detects the change through a watch and calls controller.updateDisplay() to keep the panel in sync — giving you true two-way binding between the GUI and your application state.

Props

controls
GuiControl[]
required
An array of control descriptor objects. Each descriptor specifies a Vue ref to bind and optional metadata that determines what kind of control is rendered (slider, color picker, or dropdown).

The GuiControl Interface

Each element in the controls array must conform to the following shape:
interface GuiControl {
  label: string                          // display label shown in the GUI panel
  ref: any                               // a Vue ref or direct primitive value — read and written by the controller
  type?: 'color'                         // when set, renders a color picker via gui.addColor()
  min?: number                           // slider minimum value
  max?: number                           // slider maximum value
  step?: number                          // slider step increment
  options?: any[] | Record<string, any>  // dropdown options (array or key/value map)
  onChange?: (val: any) => void          // optional extra callback fired on every change
}
Control type is resolved in this order:
  1. If type: 'color' — a color picker is rendered using gui.addColor().
  2. Else if options is set — a dropdown is rendered using gui.add(target, key, options).
  3. Otherwise — a slider is rendered using gui.add(target, key, min, max, step).

Usage

The example below is taken from the Shader / Learn sketch, which uses LilGui to expose animation speed, glow intensity, and palette color controls directly in the scene:
<script setup lang="ts">
import { ref } from 'vue'
import LilGui from '@/components/LilGui.vue'

const speed = ref(1.0)
const glow = ref(0.25)
const paletteA = ref('#803333')

const guiControls = [
  { label: 'Speed', ref: speed, min: 0.1, max: 5.0, step: 0.1 },
  { label: 'Glow', ref: glow, min: 0.01, max: 1.0, step: 0.01 },
  { label: 'Palette A', ref: paletteA, type: 'color' as const },
]
</script>

<template>
  <Default>
    <div ref="container" class="fixed inset-0" />
    <LilGui :controls="guiControls" />
  </Default>
</template>

Positioning

The GUI panel is injected with the following inline styles on mount:
PropertyValue
positionfixed
top60px
left10px
rightauto
zIndex9999
The panel is initialized in a collapsed state (gui.close()) so it doesn’t obstruct the sketch on first load. Click the title bar to expand it.

Iframe-aware

On mount, LilGui calls useIsPreview(). If the component detects it is running inside a preview iframe, the entire lil-gui initialization is skipped — no DOM is injected and no watchers are registered. This keeps dashboard preview cards clean.

Two-way sync

For every control whose ref is a Vue ref object, LilGui registers a watch on ref.value. When the watched value changes from outside the GUI (e.g. a programmatic reset), target[key] is updated and controller.updateDisplay() is called to reflect the new value in the panel without triggering the onChange handler in a loop.

Color pickers

When a control descriptor includes type: 'color', LilGui calls gui.addColor(target, key) instead of gui.add(). Bind a hex string ref (e.g. ref('#803333')) and the color picker will read and write the hex value directly.
Stack <StatsPanel /> and <LilGui /> together in the same sketch template — StatsPanel renders its panels at top: 10px (roughly 48px tall), and LilGui starts at top: 60px, so the two never overlap. Both are iframe-aware and will silently skip initialization inside preview cards.

Build docs developers (and LLMs) love