Mesh click events

Click on meshes to select them.

This example demonstrates how to make 3D objects interactive using click events. Each mesh in the grid responds to mouse clicks, cycling through different states (gray box → colored box → colored sphere).

Key concepts:

  • Click handlers attached to meshes and icospheres

  • Scene objects created with viser.SceneApi.add_box() and viser.SceneApi.add_icosphere()

  • Dynamic object replacement by using the same name parameter

  • GUI state updates from 3D interactions (displaying clicked mesh coordinates)

This pattern is useful for building interactive 3D applications where users can select, modify, or inspect scene objects by clicking on them. The click coordinates are automatically calculated by the 3D engine and passed to your callback functions.

Source: examples/03_interaction/00_click_meshes.py

Mesh click events

Code

 1import time
 2
 3import matplotlib
 4
 5import viser
 6
 7
 8def main() -> None:
 9    grid_shape = (4, 5)
10    server = viser.ViserServer()
11
12    with server.gui.add_folder("Last clicked"):
13        x_value = server.gui.add_number(
14            label="x",
15            initial_value=0,
16            disabled=True,
17            hint="x coordinate of the last clicked mesh",
18        )
19        y_value = server.gui.add_number(
20            label="y",
21            initial_value=0,
22            disabled=True,
23            hint="y coordinate of the last clicked mesh",
24        )
25
26    def add_swappable_mesh(i: int, j: int) -> None:
27
28        colormap = matplotlib.colormaps["tab20"]
29
30        def create_mesh(counter: int) -> None:
31            if counter == 0:
32                color = (0.8, 0.8, 0.8)
33            else:
34                index = (i * grid_shape[1] + j) / (grid_shape[0] * grid_shape[1])
35                color = colormap(index)[:3]
36
37            if counter in (0, 1):
38                handle = server.scene.add_box(
39                    name=f"/sphere_{i}_{j}",
40                    position=(i, j, 0.0),
41                    color=color,
42                    dimensions=(0.5, 0.5, 0.5),
43                )
44            else:
45                handle = server.scene.add_icosphere(
46                    name=f"/sphere_{i}_{j}",
47                    radius=0.4,
48                    color=color,
49                    position=(i, j, 0.0),
50                )
51
52            @handle.on_click
53            def _(_) -> None:
54                x_value.value = i
55                y_value.value = j
56
57                # The new mesh will replace the old one because the names
58                # /sphere_{i}_{j} are the same.
59                create_mesh((counter + 1) % 3)
60
61        create_mesh(0)
62
63    for i in range(grid_shape[0]):
64        for j in range(grid_shape[1]):
65            add_swappable_mesh(i, j)
66
67    while True:
68        time.sleep(10.0)
69
70
71if __name__ == "__main__":
72    main()