Commands

Register commands that users can discover and trigger from a command palette.

Press Ctrl/Cmd+K to open the palette, then search for commands by name. (Ctrl/Cmd+Shift+P also works on browsers that don’t reserve it, e.g. everything except Firefox.) Commands can also be bound to keyboard hotkeys.

Key methods:

  • viser.GuiApi.add_command() to register a named command

  • viser.CommandHandle.on_trigger() to attach a callback

  • viser.CommandHandle.remove() to unregister a command

Features demonstrated:

  • Registering commands with labels, descriptions, icons, and hotkeys

  • Fuzzy search filtering in the palette

  • Dynamic command updates (label, description, icon changes)

  • Removing commands at runtime

Source: examples/02_gui/10_commands.py

Commands

Code

 1import time
 2
 3import viser
 4
 5
 6def main() -> None:
 7    server = viser.ViserServer()
 8
 9    # Basic command.
10    hello_cmd = server.gui.add_command(
11        "Say Hello",
12        description="Show a greeting notification",
13        icon=viser.Icon.MESSAGE,
14    )
15
16    @hello_cmd.on_trigger
17    def _(event: viser.CommandEvent) -> None:
18        assert event.client is not None
19        event.client.add_notification(
20            title="Hello!",
21            body="You triggered the Say Hello command.",
22            color="teal",
23        )
24
25    # Command with a hotkey.
26    reset_cmd = server.gui.add_command(
27        "Reset Camera",
28        description="Move the camera back to the default view",
29        hotkey=("cmd/ctrl", "shift", "R"),
30        icon=viser.Icon.REFRESH,
31    )
32
33    @reset_cmd.on_trigger
34    def _(event: viser.CommandEvent) -> None:
35        assert event.client is not None
36        event.client.camera.position = (3.0, 3.0, 3.0)
37        event.client.camera.look_at = (0.0, 0.0, 0.0)
38        event.client.add_notification(
39            title="Camera Reset",
40            body="Camera position has been reset.",
41            color="blue",
42        )
43
44    # Toggle command (changes label/icon on each trigger).
45    grid_visible = True
46    grid_handle = server.scene.add_grid("/grid", width=10.0, height=10.0)
47
48    toggle_cmd = server.gui.add_command(
49        "Hide Grid",
50        description="Toggle grid visibility",
51        icon=viser.Icon.EYE_OFF,
52    )
53
54    @toggle_cmd.on_trigger
55    def _(event: viser.CommandEvent) -> None:
56        nonlocal grid_visible
57        grid_visible = not grid_visible
58        grid_handle.visible = grid_visible
59
60        if grid_visible:
61            toggle_cmd.label = "Hide Grid"
62            toggle_cmd.icon = viser.Icon.EYE_OFF
63        else:
64            toggle_cmd.label = "Show Grid"
65            toggle_cmd.icon = viser.Icon.EYE
66
67    # Removable command.
68    counter = 0
69    removable_cmd = server.gui.add_command(
70        "Self-Destruct",
71        description="This command removes itself after being triggered",
72        icon=viser.Icon.BOMB,
73    )
74
75    @removable_cmd.on_trigger
76    def _(event: viser.CommandEvent) -> None:
77        nonlocal counter
78        counter += 1
79        assert event.client is not None
80        event.client.add_notification(
81            title="Boom!",
82            body=f"Triggered {counter} time(s). Removing command...",
83            color="red",
84        )
85        removable_cmd.remove()
86
87    server.scene.add_frame("/frame", show_axes=True, axes_length=2.0)
88    toggle_cmd_disabled = server.gui.add_button("Toggle grid command disabled")
89
90    @toggle_cmd_disabled.on_click
91    def _(_) -> None:
92        toggle_cmd.disabled = not toggle_cmd.disabled
93
94    while True:
95        time.sleep(1.0)
96
97
98if __name__ == "__main__":
99    main()