Skip to main content
Version: 3.5.x

07. Basis & Mount Playground

Interactively drives the device's mount transform and demonstrates how configure.basis, configure.preset, and configure.mount work together on the wire. Keeps a fixed horizontal floor so the interactive focus stays on the configure commands.

What you'll learn:

  • Setting a basis permutation ("XZY" → Y-up application frame)
  • Choosing a preset (arm_front) and understanding what it configures
  • Overriding the mount at runtime with a rotation quaternion (keyboard-driven)
  • The mutual exclusion rule: mount and preset cannot coexist in the same device configure block
  • One-shot configure semantics: each key press sends exactly one configure message
  • (C++ Glaze) modelling mutually-exclusive fields with std::optional

Workflow

  1. On first message: send the session profile, configure.basis: "XZY", and configure.preset: arm_front. Start sending set_cursor_force for the fixed floor.
  2. Each tick: read cursor Y, compute force_y = max(0, (floor_pos - y) * stiffness), send it.
  3. When the user presses a mount-rotation key, flag pending_configure = true.
  4. On the next tick: build a configure.mount block with a transform whose rotation is a unit quaternion (Z-then-X composition of the current pitch / yaw). Omit preset — the two are mutually exclusive on the wire.
  5. Reset key (R) clears the override; next configure falls back to preset again.

Parameters

NameDefaultPurpose
BASIS"XZY"Axis permutation — Y-up application frame
DEVICE_PRESET / DEVICE_CONFIG_PRESET"arm_front"Named preset — origin at device base
FLOOR_POS_Y0.0 mFixed floor plane (application-Y)
STIFFNESS1000 N/mFloor spring constant
MOUNT_STEP_DEG10°Rotation per key press
PRINT_EVERY_MS200Telemetry throttle

Controls

KeyAction
W / SRotate mount ±10° around device +X (pitch)
A / DRotate mount ±10° around device +Z (yaw)
RReset mount — revert to preset
HShow controls
QQuit
mount and preset are mutually exclusive

The service rejects a device configure block containing both. Once the user overrides the mount, the tutorial omits preset from every subsequent configure. Pressing R re-enables preset on the next configure and drops mount.

Different input models

C++ variants read line-based input on a background stdin thread (press ENTER after each letter). Python uses the keyboard package for real-time key polling in the main async loop — no ENTER needed. Same keys, same commands.

State fields read

From data.inverse3[i].state:

  • cursor_position.yvec3, used to compute floor penetration
  • current_cursor_force — reported for telemetry

Send / receive

The payload shape is the same across variants; the interesting differences are how each builds the mutually-exclusive mount / preset branching and how the input thread signals the WebSocket thread.

Single async loop with real-time key polling via the keyboard package. pending_configure is a global flag set by the key handlers and cleared each time a configure block is sent.

async with websockets.connect(URI) as websocket:
while True:
msg = await websocket.recv()
data = json.loads(msg)

if first_message:
first_message = False
device_id = data["inverse3"][0]["device_id"]
# Handshake: profile + basis + preset
request_msg = {
"session": {"configure": {"profile": {"name": SLUG}}},
"inverse3": [{
"device_id": device_id,
"configure": build_configure_block(first_handshake=True),
# -> {"basis": {"permutation": "XZY"},
# "preset": {"preset": "arm_front"}}
}],
}
else:
handle_key_inputs() # may set pending_configure = True (classic, not shown)

y = data["inverse3"][0]["state"]["cursor_position"]["y"]
force_y = 0.0 if y > FLOOR_POS_Y else (FLOOR_POS_Y - y) * STIFFNESS

entry = {
"device_id": device_id,
"commands": {"set_cursor_force":
{"vector": {"x": 0.0, "y": force_y, "z": 0.0}}},
}
if pending_configure:
entry["configure"] = build_configure_block(first_handshake=False)
# -> {"mount": {...}} OR {"preset": {...}} (never both)
pending_configure = False

request_msg = {"inverse3": [entry]}

await websocket.send(json.dumps(request_msg))

Source: Python · C++ · C++ Glaze

Related: Basis Permutation · Mount & Workspace · Device Configuration · Control Commands (set_cursor_force) · Types (transform) · Tutorial 04 (Hello Floor)