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:
viser.GuiInputHandle.on_update()
for input changes (sliders, dropdowns, checkboxes)viser.GuiButtonHandle.on_click()
for button press eventsviser.GuiUploadButtonHandle.on_upload()
for file upload events
Features demonstrated:
Dynamic scene updates using
viser.SceneApi.add_grid()
andviser.SceneApi.add_point_cloud()
Conditional GUI state changes (dynamically updating dropdown options)
Global scene visibility control with
viser.SceneApi.set_global_visibility()
Coordinated updates where multiple controls affect the same scene objects
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

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()