Skip to content
EN

Fire vs. Clip — When to Use Each

The Mode field on an EventMap entry (Fire / Clip) has a significant impact on your Unity integration approach, where haptic assets live, and the iteration loop. This page clarifies how to choose between the two and how to implement each — from a Unity developer’s perspective.

For protocol / schema level details (manifest bucket, wire format, gain application timing, etc.), see Fire vs. Clip.

Fire (FIRE / command)Clip (CLIP / stream_clip)
In one sentencePlay a device-stored clip by IDSend a Unity AudioClip as PCM
Recommended forProduction use, short one-shotsPrototyping, long audio, dynamic modulation
Device deploymentRequired (write Kit from Studio)Not required
LatencyLow, stableHigher, environment-dependent
Dynamic modulationgain is fixed per firegain / pan can be modulated per-chunk during playback

Using FireUsing Clip
Where haptic assets liveHapbeat Studio Kit (install-clips/)Unity AudioClip
Iteration loopEdit WAV → Studio → deploy to device → Unity PlaySwap AudioClip in Inspector → Play immediately
Trigger-side codeMode-agnostic (Trigger / StateBehaviour do not need to know the mode)Same
Dynamic modulationNot supported (fixed gain only)Supported (Trigger.GainMultiplier / HapbeatParameterBinding)

Trigger / StateBehaviour code is mode-agnostic, so switching later is possible. Changing the Mode field on the EventMap entry is all it takes to change the wire format. This is a key feature of the Unity SDK’s EventMap abstraction when comparing Fire and Clip.

1-2. Concrete example: mode choices in Showcase

Section titled “1-2. Concrete example: mode choices in Showcase”
Showcase ZoneMode chosenReason
Z1 Pin hitClipExperimenting with different impact feels during development. Switching to Fire is natural for production.
Z2 Door open/closeClipShort one-shot tied to Animator state. Kept as Clip during the prototype stage.
Z3 Grab loopClip (loop)Requires a long loop with dynamic modulation driven by object speed.
Z5 Charge releaseClipClip is required for gain modulation via AnimationCurve based on charge amount.
  • Prototyping → Clip (faster iteration)
  • Haptics finalized + shipping → Switch to Fire (lower latency, stable)
  • Long audio / loops / dynamic modulation required → Stay on Clip
  • Congested Wi-Fi or many simultaneous users → Fire (lower bandwidth)
  • Same entry needs both modes → BOTH representation in the manifest (details in Fire vs. Clip)

Select FIRE (Command) / CLIP (Stream Clip) in the Mode field of the EventMap entry. Components and Behaviours are mode-agnostic and internally branch to the appropriate API (HapbeatManager.Play / StreamAudioClip).

ModeRequired fieldsWire format
FIRE (Command)Category + Event NamePLAY / STOP packet (Event ID + parameters)
CLIP (Stream Clip)Stream Clip (AudioClip)STREAM_BEGIN / STREAM_DATA × N / STREAM_END

Details: EventMap Window.


Using the same classification as Integrating into Your Project. A and B are mode-agnostic so the Unity-side code is shared; only C uses mode-specific APIs.

Attach a Hapbeat component (Collision / Sequence / UnityEvent / TickEmitter) to a GameObject, or attach HapbeatStateBehaviour to an AnimatorController state, then select the target EventMap entry in the Inspector. No code required.

Because the difference between Fire and Clip is absorbed by the Mode field and related fields on the EventMap entry, the Inspector workflow for components and Behaviours is identical:

GameObject Inspector
└─ HapbeatUnityEventTrigger
Event Map : MyEventMap
Event : [▶ sword_hit] ← entry.mode = FIRE
: [♪ ambient_drone] ← entry.mode = CLIP

Switching the Mode on the EventMap entry changes the wire format without touching the component or Behaviour at all.

B. Call Trigger from script (mode-agnostic)

Section titled “B. Call Trigger from script (mode-agnostic)”

Hold a Trigger reference via [SerializeField] and call Fire() from game logic. The call is the same regardless of whether the entry mode is FIRE or CLIP.

public class GunController : MonoBehaviour
{
[SerializeField] private HapbeatUnityEventTrigger _shootTrigger;
void OnShoot() {
_shootTrigger.Fire();
}
}

For Clip, writing to GainMultiplier every frame enables dynamic modulation during playback (for Fire, the setter itself works but does not affect the current waveform — it takes effect from the next Fire() call):

void Update() {
if (_isCharging)
_shootTrigger.GainMultiplier = _gainCurve.Evaluate(_chargeT);
}

Reference implementation: Showcase Z5 ChargeShooter (Samples~/Showcase/Scripts/ChargeShooter.cs).

C. Call Manager.Play() / StreamAudioClip() directly (mode-specific APIs, special cases)

Section titled “C. Call Manager.Play() / StreamAudioClip() directly (mode-specific APIs, special cases)”

When bypassing EventMap, choose the API based on mode.

Fire:

HapbeatManager.Instance?.Play(
eventId: "my-game.sword_hit",
gain: 0.8f,
target: "player_1/pos_r_arm"
);

Clip:

[SerializeField] private AudioClip _footstepClip;
void OnFootstep() {
var playback = HapbeatManager.Instance?.StreamAudioClip(
clip: _footstepClip,
gain: 0.7f
);
// optionally: playback.SetGain(0.5f) / playback.Stop()
}

Automatic corrections provided by EventMap (manifest intensity / latency offset / wiring list / Inspector tuning) are lost. For when to choose this path, see Integrating into Your Project.


Before using Fire mode, the Kit (WAV files + manifest.json) must be written to the device. See the Studio documentation:


For both Fire and Clip, the SDK sends UDP directly to the device at runtime — Helper is not required. Helper is needed when:

  • Deploying a Kit from Studio (mDNS discovery + WebSocket relay)
  • Running playback tests or waveform previews in Studio