GUI callbacks

Attach event handlers to GUI elements for real-time interaction.

This example demonstrates how to create responsive interfaces using GUI callbacks. Callbacks are functions that execute automatically when users interact with controls, enabling real-time updates to the 3D scene.

Key callback methods:

Features demonstrated:

The pattern shown here—connecting GUI controls to scene updates via callbacks—is fundamental for building interactive 3D applications with viser.

Source: examples/02_gui/01_callbacks.py

GUI callbacks

Code

 1import time
 2
 3import numpy as np
 4from typing_extensions import assert_never
 5
 6import viser
 7
 8
 9def main() -> None:
10    server = viser.ViserServer()
11
12    gui_reset_scene = server.gui.add_button("Reset Scene")
13
14    gui_plane = server.gui.add_dropdown(
15        "Grid plane", ("xz", "xy", "yx", "yz", "zx", "zy")
16    )
17
18    def update_plane() -> None:
19        server.scene.add_grid("/grid", width=10.0, height=20.0, plane=gui_plane.value)
20
21    gui_plane.on_update(lambda _: update_plane())
22
23    with server.gui.add_folder("Control"):
24        gui_show_frame = server.gui.add_checkbox("Show Frame", initial_value=True)
25        gui_show_everything = server.gui.add_checkbox(
26            "Show Everything", initial_value=True
27        )
28        gui_axis = server.gui.add_dropdown("Axis", ("x", "y", "z"))
29        gui_include_z = server.gui.add_checkbox("Z in dropdown", initial_value=True)
30
31        @gui_include_z.on_update
32        def _(_) -> None:
33            gui_axis.options = ("x", "y", "z") if gui_include_z.value else ("x", "y")
34
35        with server.gui.add_folder("Sliders"):
36            gui_location = server.gui.add_slider(
37                "Location", min=-5.0, max=5.0, step=0.05, initial_value=0.0
38            )
39            gui_num_points = server.gui.add_slider(
40                "# Points", min=1000, max=200_000, step=1000, initial_value=10_000
41            )
42
43    def draw_frame() -> None:
44        axis = gui_axis.value
45        if axis == "x":
46            pos = (gui_location.value, 0.0, 0.0)
47        elif axis == "y":
48            pos = (0.0, gui_location.value, 0.0)
49        elif axis == "z":
50            pos = (0.0, 0.0, gui_location.value)
51        else:
52            assert_never(axis)
53
54        server.scene.add_frame(
55            "/frame",
56            wxyz=(1.0, 0.0, 0.0, 0.0),
57            position=pos,
58            show_axes=gui_show_frame.value,
59            axes_length=5.0,
60        )
61
62    def draw_points() -> None:
63        num_points = gui_num_points.value
64        server.scene.add_point_cloud(
65            "/frame/point_cloud",
66            points=np.random.normal(size=(num_points, 3)),
67            colors=np.random.randint(0, 256, size=(num_points, 3)),
68        )
69
70    # We can (optionally) also attach callbacks!
71    # Here, we update the point clouds + frames whenever any of the GUI items are updated.
72    gui_show_frame.on_update(lambda _: draw_frame())
73    gui_show_everything.on_update(
74        lambda _: server.scene.set_global_visibility(gui_show_everything.value)
75    )
76    gui_axis.on_update(lambda _: draw_frame())
77    gui_location.on_update(lambda _: draw_frame())
78    gui_num_points.on_update(lambda _: draw_points())
79
80    @gui_reset_scene.on_click
81    def _(_) -> None:
82        gui_show_frame.value = True
83        gui_location.value = 0.0
84        gui_axis.value = "x"
85        gui_num_points.value = 10_000
86
87        draw_frame()
88        draw_points()
89
90    # Finally, let's add the initial frame + point cloud and just loop infinitely. :)
91    update_plane()
92    draw_frame()
93    draw_points()
94    while True:
95        time.sleep(1.0)
96
97
98if __name__ == "__main__":
99    main()