Skip to content

Getting Started

This walks you from a fresh clone to a running VHI window with a hand moving.

Prerequisites

Tool Version Notes
Godot 4.6 .NET build The Mono/.NET edition - not the standard build. C# won't load otherwise.
.NET SDK 8.0 The project targets net8.0 to match Godot 4.6's runtime. A .NET 8 SDK is also required for exporting.
Python 3 3.x Only for the LSL test scripts in tests/ (pip install pylsl numpy). Optional.

Use the .NET build of Godot

On macOS the .NET build is Godot.app reported as …mono… by Godot --version. The standard (non-.NET) build cannot run VHI.

Restore and run

# 1. Restore NuGet packages (SharpLSL, Grpc.AspNetCore, Tomlyn)
dotnet restore

# 2. Open the project in the Godot editor and press F5 - or run headless:
godot --path .

On first run VHI:

  • builds the C# assembly,
  • opens the main scene (scenes/HandMain.tscn) with the control and predicted hands,
  • starts the gRPC control server on 127.0.0.1:50051,
  • begins resolving its LSL inlets - one per DOF, named vhi.prediction.* and vhi.control.pose.*.

You should see a console line like:

✅ gRPC control server listening on 127.0.0.1:50051

See a hand move

Out of the box both hands sit at rest - VHI is waiting for input. Two quick ways to get motion:

With the window focused, the control hand responds to:

Key Action
/ cycle the selected movement (wraps around at both ends)
start the movement
stop, return to rest
Space freeze / unfreeze at the current pose

Cycling is the keyboard equivalent of a discrete DOF - both pick a movement by name from the same available_movements list. The programmatic path is a discrete control, commanded by name (see gRPC control).

Publish the nine vhi.prediction.* streams - one per DOF, one channel each - and the predicted hand follows them:

python tests/test_lsl_sender.py

A real client publishes only the DOFs it drives; this script publishes all nine so the patterns have a whole hand to animate. See LSL streams for the names.

Command the control hand by name. VHI declares vhi.control.gesture as a discrete control, so you map a name of your own onto it and select a state:

from myogestic.controls import ControlBus, load_control_map, resolve
from myogestic.remote import RemoteTarget
from myogestic.vhi import virtual_hand

vhi = virtual_hand()
client = vhi.control_client()
controls = resolve(
    load_control_map({"dofs": {"gesture": "vhi.control.gesture"}}),
    client.capabilities(),          # needs VHI running
)
bus = ControlBus(controls, targets=[RemoteTarget(client=client, interface=vhi)])
bus.select("gesture", "Fist")       # control hand snaps to the Fist pose

The states come from VHI's own manifest, so "Fist" is a name it reported rather than one hard-coded here. See Drive VHI from MyoGestic.

Next steps