GUI Interactions#

The GuiParam class provides a declarative way to create interactive GUI controls (dropdowns, sliders, checkboxes) that write to GPU uniform buffers. A single GuiParam instance can be shared across multiple renderers — it appears once in the GUI and writes to all bound targets.

Quick start#

from webgpu import GuiParam

# A slider that writes directly to a uniform field
shrink = GuiParam("slider", "Shrink", default=1.0, min=0.0, max=1.0)
shrink.bind(mesh_uniform, "shrink")

# A dropdown whose value is written to a buffer field
component = GuiParam("dropdown", "Component",
                     options={"Norm": -1, "x": 0, "y": 1}, default=-1)
component.bind(settings, "component")

# Side effects: when the dropdown changes, also update colormap range
component.affects(colormap, "min", values={-1: 0.1, 0: -1.0, 1: 0.5})
component.affects(colormap, "max", values={-1: 5.0, 0: 1.0, 1: 3.0})

Renderers expose their params via _gui_params. The scene collects them automatically at render time — no manual wiring needed.

Concepts#

bind(ref, field_name)

Write the selected value directly to a uniform buffer field. The byte offset and data type are derived automatically from the ctypes structure definition.

affects(ref, field_name, values=…)

On change, look up the selected value in a dict and write the result to a different field. Use this for side effects like updating a colormap range when a component changes.

Sharing

Multiple renderers can call bind() on the same GuiParam. The GUI shows one control that writes to all bound targets. This is the primary mechanism for keeping multiple views in sync.

Lazy resolution

bind and affects accept references that are resolved at export time. You can pass:

  • A UniformBase (ctypes Structure) directly

  • An object with a .uniform attribute (e.g. FunctionSettings)

  • An object with a .uniforms attribute (e.g. Colormap)

  • A callable that returns a uniform

Control types#

Slider#

phase = GuiParam("slider", "Phase",
                 default=0.0, min=0.0, max=6.283, step=0.01)
phase.bind(complex_settings, "phase")

Checkbox#

enabled = GuiParam("checkbox", "Clipping", default=True)
enabled.affects(clipping_uniforms, "mode", values={True: 1, False: 0})

Shared parameters across renderers#

When multiple renderers visualize the same data, they can share a GuiParam so that one control updates all of them:

from webgpu import GuiParam
from ngsolve_webgpu import FunctionData, CFRenderer, ClippingCF

function_data = FunctionData(mesh_data, cf, order=5)

colormap = Colormap()
cfr = CFRenderer(function_data, colormap=colormap, clipping=clipping)
clip = ClippingCF(function_data, clipping=clipping, colormap=colormap)

# Both renderers auto-bind to function_data.component_param
# One dropdown appears, writes to both, rescales colormap.
Draw([cfr, clip, Colorbar(colormap)])

In this example, FunctionData creates a shared component_param internally. Both CFRenderer and ClippingCF bind their settings uniform to it during construction. The colormap min/max side effects are also set up automatically when the colormap has autoscale=True.

Custom parameters#

You can create your own GuiParam and attach it to a renderer:

my_param = GuiParam("slider", "Threshold", default=0.5, min=0.0, max=1.0)
my_param.bind(my_renderer.uniforms, "threshold")

# Register so the scene picks it up
my_renderer._gui_params.append(my_param)

Draw([my_renderer])

How it works internally#

  1. Renderers store GuiParam instances in self._gui_params.

  2. When the scene is initialized, it collects all params (deduplicated by identity — same object = one control).

  3. Each GuiParam exports itself as an Interaction entry containing the control definition and write targets.

  4. The JavaScript engine creates a lil-gui control and executes the buffer writes on change.

API Reference#

Declarative GUI parameters that work in both live and export paths.

A GuiParam describes a single interactive control (dropdown, slider, checkbox) that writes to one or more GPU uniform buffer fields. Multiple renderers can share the same GuiParam instance — it appears once in the GUI.

class webgpu.gui_param.GuiParam(kind, label, *, default, options=None, min=None, max=None, step=None)#

Declarative GUI parameter. Works in both live and export paths.

Examples:
component = GuiParam(“dropdown”, “Component”,

options={“Norm”: -1, “x”: 0, “y”: 1}, default=-1)

component.bind(settings, “component”) # settings.uniform.component component.affects(colormap, “min”, values={-1: 0.1, 0: -1.0, 1: 0.5})

shrink = GuiParam(“slider”, “Shrink”, default=1.0, min=0.0, max=1.0) shrink.bind(mesh_uniform, “shrink”)

affects(ref, field_name, *, values)#

On change, look up selected value in dict and write result to field.

ref: a UniformBase, or object with .uniform/.uniforms attr, or callable. values: dict mapping option values to write values, or a callable

returning such a dict (resolved at export time). E.g. {-1: 0.1, 0: -1.0, 1: 0.5}

bind(ref, field_name)#

Write selected value directly to this uniform field on change.

ref: a UniformBase, or object with .uniform/.uniforms attr, or callable.

export(registry)#

Produce an Interaction for the JS engine.