Skip to content

Add a custom movement

Named movements are defined in a TOML config, not in code - so adding one is an edit, not a rebuild. This is the right path when the gesture is something you can name ahead of time. (For arbitrary runtime poses, see Stream a custom pose instead.)

Where the config lives

On first run VHI generates user://movements.toml. Godot's user:// resolves to a per-project app-data directory:

OS Resolved path
macOS ~/Library/Application Support/Godot/app_userdata/Virtual Hand Interface/movements.toml
Windows %APPDATA%\Godot\app_userdata\Virtual Hand Interface\movements.toml
Linux ~/.local/share/godot/app_userdata/Virtual Hand Interface/movements.toml

The default path is the ConfigFilePath export, baked into the build at edit-time - so a compiled binary can't change its default. To use a TOML kept under version control, open the control panel (right side, > button) and click Load Config File to pick any TOML from disk; VHI then watches that file for hot-reload instead. The same panel has Edit Config File, which opens the current file in your OS's default text editor.

Edit and hot-reload

VHI watches the file with a FileSystemWatcher - save the TOML and the hand updates live, no restart.

Each movement is a [movements.<Name>] table with one line per joint - joint_name = [x, y, z], the joint's max-flexion pose in Euler degrees. There is no rest pose in the file; rest is always neutral ([0, 0, 0]). On the X axis, positive = flexion, negative = extension. The generated file is the best reference - open it and copy an existing entry like Fist, rename it, and adjust the joint values.

Files written before VHI 2 are signed the other way

They said negative = flexion, and VHI negated every row again on the way to the bone, so the two cancelled. That is fixed, which means the old signs now mean what they say — backwards. A file without a convention key is detected as Unity-signed and migrated in place on first load, with a .unity-signed.bak copy left beside it. Check the result if you had hand-tuned poses.

# movements.toml - one movement entry (joint_name = [x, y, z], degrees)
convention = "rig-native"   # positive X is flexion; omit and the file is migrated

[movements.MyGesture]
wrist          = [0, 0, 0]
thumb_proximal = [20, 0, 0]
thumb_middle   = [30, 0, 0]
thumb_distal   = [30, 0, 0]
index_proximal = [40, 0, 0]
# … index_middle, index_distal, then middle_*, ring_*, pinky_* (16 joints total)

Make it selectable

Once the movement is in the config:

  • it appears in GetRecordingSessionState().available_movements, so a client can discover it without hard-coding anything;
  • a standard discrete DOF declaring "mygesture" as one of its states resolves to it and holds its end pose, and StartRecordingTrajectory cycles it when you want a swept trajectory for recording;
  • the keyboard Left / Right cycle through it;
  • it shows up in MyoGestic's movement palette.

Keep names in sync

a discrete DOF matches by name. If MyoGestic sends class labels straight through to a discrete DOF, the classifier's class names and the movement names in this TOML need to agree - unknown names are rejected with applied = false.