Skip to content
EN

Event ID and Kit

Hapbeat haptic content is organized into units called Kits, and the SDK specifies clips to trigger using strings called Event IDs. This page covers the structure and naming conventions for both.

For the formal specification, see Contracts: kit-format / event-id.

A Kit is a folder that bundles haptic waveforms (WAV files) + metadata (<kit-name>-manifest.json) together. It is created in Studio and transferred to the Hapbeat device via Helper.

my-game/ ← Kit folder (= kit-name)
my-game-manifest.json ← event definitions + baseline intensity
install-clips/ ← WAV files for Fire (command) mode
sword-hit.wav
footstep-grass.wav
stream-clips/ ← WAV files for Clip (stream) mode
bgm-tension.wav

For the distinction between install-clips/ and stream-clips/, see Fire vs. Clip. Think of it as: “install” = baked onto the device; “stream” = sent on demand each time.

Manifest filename: On the host side (Studio / Unity SDK / Helper) the canonical filename is <kit-name>-manifest.json. It is renamed to manifest.json only when transferred to the device’s LittleFS. This naming convention improves identifiability when multiple Kits are placed in one project (in OS Explorer or the SDK picker).

The manifest has two buckets: events (for Fire) and stream_events (for Clip). Each bucket is an object (dictionary) keyed by Event ID. Placing the same Event ID in both buckets expresses “this semantic event can be played in either Fire or Clip mode (= BOTH mode).”

{
"schema_version": "2.0.0",
"name": "my-game",
"version": "1.0.0",
"target_device": {
"firmware_version_min": "0.1.0",
"board": "duo_wl_v3"
},
"events": {
"my-game.sword-hit": {
"clip": "sword-hit.wav",
"parameters": {
"intensity": 0.8,
"device_wiper": 64
}
}
},
"stream_events": {
"my-game.bgm-tension": {
"clip": "bgm-tension.wav",
"parameters": {
"intensity": 0.5,
"loop": true
}
}
}
}
FieldRequiredMeaning
schema_versionManifest schema version. Current value: "2.0.0"
nameKit name. Use the same string as the on-disk folder name. Equal to the kit_id payload on the wire (DEC-028)
versionKit version (semver recommended)
target_device.firmware_version_minMinimum required firmware version
target_device.boardExpected board identifier (e.g., duo_wl_v3 / neck_wl_v2). A mismatch with firmware metadata produces a warning
eventsDictionary of Fire (command) events. Constitutes the device event table; the key appears in the event_id field of PLAY/STOP packets
stream_eventsDictionary of Clip (stream) events. Sent by the SDK as a UDP audio stream. The device does not recognize the eventId — it is used only as a binding label within the SDK
<bucket>[<id>].clipWAV filename (bare filename) for the corresponding bucket. events.<id>.clip → resolved as install-clips/<clip>; stream_events.<id>.clip → resolved as stream-clips/<clip>
<bucket>[<id>].parameters.intensityBaseline vibration intensity 0.0–1.0 (the baseline value in the gain multiplication chain)
events[<id>].parameters.device_wiperMCP4018 wiper value (0..127). Reference information for reproducing authoring-time intensity. Not applicable to the stream_events side
<bucket>[<id>].parameters.loopWhether to loop playback (default false)

The mode field was removed in schema 2.0.0 (DEC-031). Which mode an entry uses is determined by which bucket it is in. Placing the same Event ID in both buckets = BOTH mode. See the kit-format spec for the complete schema.

An Event ID is a string that uniquely identifies a haptic event. The canonical format is defined by the event-id spec (DEC-040).

<kit-name>.<file-name>
  • <kit-name> — the name of the Kit the clip belongs to (the manifest name above / on-disk folder name / wire kit_id — all the same string)
  • <file-name> — the clip’s filename inside the Kit with the .wav extension stripped
  • Separated by a single . (dot). Because <kit-name> doubles as a namespace, IDs never collide across Kits.
RuleValue
<kit-name>^[a-z][a-z0-9-]*$ (lowercase start, letters/digits/hyphen; no underscore)
<file-name>^[a-z][a-z0-9_-]*$ (lowercase start, letters/digits/underscore/hyphen)
Separatorexactly one . (dot)
Uppercasenot allowed (normalized to lowercase)
<file-name> startmust be a letter (sine_100hz, not 100hz)
Max total length255 characters

Examples:

sample-kit.sine_100hz ← official connectivity-test event (sample-kit)
showcase-kit.z1_pin_hit ← showcase-kit clip z1_pin_hit.wav
my-game.sword_slash ← my-game kit clip sword_slash.wav

Studio auto-generates the Event ID from Kit name + clip filename (you never assemble it by hand). sample-kit / showcase-kit / hapbeat-* are reserved for official Hapbeat use; content developers must not use them.

Why “Folder Name = manifest.name = Wire kit_id” Was Unified (DEC-028)

Section titled “Why “Folder Name = manifest.name = Wire kit_id” Was Unified (DEC-028)”

Previously, the manifest had both kit_id and name fields, causing display inconsistencies (Basic Exam Kit vs. basic-exam-kit) between Studio and OS Explorer. DEC-028 (2026-04-28) removed kit_id and consolidated everything into a single name field. The folder name, JSON, and wire payload all use the same string.

The kit_id field still exists in the wire protocol, but its value is physically guaranteed to always equal manifest name.

  1. Edit the Kit in Studio’s Library (adjust intensity, toggle Fire / Clip / BOTH, add clips)
  2. Studio writes <kit-name>-manifest.json and WAV files to the working directory
  3. Transfer to the device’s Kit partition via Helper (events-derived = Fire WAVs only; manifest is renamed to manifest.json on the wire)
  4. stream_events-derived WAVs are NOT deployed — the SDK sends them directly via UDP at runtime