TeslasuitDocumentation
Frameworks

Haptic navigation (custom haptic library)

Directional haptic cues. Demonstrates the HapticLibrary extension point — declaring named slots for CustomPlayable and toggling their IsMuted field to fire/silence them.

Source: examples/haptic_navigation/


What it shows#

This is the canonical example of using EMS for non-functional output — i.e. tactile feedback rather than producing movement. The suit is used as a body-mounted haptic display: arrow keys in the GUI fire haptic cues on different parts of the wearer's body.

Capabilities demonstrated:

  • HapticLibrary subclassNavCues declares named CustomPlayable slots: belly_cue, back_cue, left_shoulder_cue, right_shoulder_cue.
  • SuitHandler.create_haptic_touch() — building looped EMS playables programmatically (no haptic asset files needed).
  • Slot mute toggle patternslot.IsMuted = False to fire, slot.IsMuted = True to silence. The framework's LibraryStimulator translates these to SDK calls.
  • ControlMessage for direction state — the GUI signals which arrow keys are held; the strategy maps that to slot mutes.

Project layout#

examples/haptic_navigation/
├── main.py                              ── orchestrator.launch()
├── haptic_navigation_strategy.py        ── HapticNavigationStrategy
├── haptic_navigation_types.py           ── NavControlMessage, NavCues (HapticLibrary)
└── gui/
    └── main.py                          ── PyQt5 GUI with arrow-key handling

How the strategy works#

In setup(), the strategy populates four playable slots — one per direction:

def setup(self, muscles=None, suit=None, config=None) -> None:
    super().setup(muscles, suit, config)
    self.haptic_library = NavCues()
    self.haptic_library.belly_cue = self.suit.create_haptic_touch(
        bone_id=BELLY_BONE,
        channel_list=BELLY_CHANNELS,
        period=20.0, amplitude=30, pulse_width=120,
    )
    # ... back, left_shoulder, right_shoulder similarly ...

In process(), the strategy reads which directions are held in self.params (a NavControlMessage subclass) and toggles slot mutes:

def process(self) -> None:
    if self.params is None:
        return
    self.haptic_library.belly_cue.IsMuted = not self.params.up_held
    self.haptic_library.back_cue.IsMuted = not self.params.down_held
    self.haptic_library.left_shoulder_cue.IsMuted = not self.params.left_held
    self.haptic_library.right_shoulder_cue.IsMuted = not self.params.right_held

That's it. The framework's LibraryStimulator runs after process() and converts each mute change to the corresponding SDK call. Custom haptic patterns become a one-line toggle.


Running it#

python -m examples.haptic_navigation.main

A small GUI window appears. Arrow keys fire haptic cues:

  • — belly
  • — back
  • — left shoulder
  • — right shoulder

Multiple arrow keys can be held simultaneously.


When to use HapticLibrary vs. EmsData#

If you want to …Use
Stimulate one of the 20 standard musclesEmsData (self.ems_output.<muscle>)
Fire a custom looped haptic pattern by mute toggleHapticLibrary slot
Load a pre-recorded .hpt haptic assetHapticLibrary + suit.load_haptic_asset()
Target arbitrary bones / channel groupsHapticLibrary + suit.create_haptic_touch()
Use the SDK's haptic API directly(escape hatch — self.suit.haptic.*)

HapticLibrary is the supported way to extend beyond the 20 muscle slots. See Messaging — HapticLibrary.


Things you can copy from this example#

For your applicationCopy
Pattern for naming and populating playable slotshaptic_navigation_strategy.py setup()
Direction-flag → slot-mute translationprocess()
Arrow-key event handling for GUI cuesgui/main.py
Setting up a NavControlMessage for "which keys are held"haptic_navigation_types.py

Safety notes#

The strategy uses a 30% amplitude default — comfortable as tactile feedback without producing involuntary movement. Adjust upward only if the wearer reports the cue as too subtle.

Unlike the elbow-flexion example, there's no closed-loop feedback — the user responds to the cues by changing what keys they hold. Treat this as a UX device, not a control loop.


See also#