References
The tact-js package provides functions for controlling bHaptics haptic devices from JavaScript. It enables you to play haptic patterns, control device motors directly, and manage haptic playback.
import Tact from "tact-js";
Play Event-driven Haptic
Play haptic patterns bound to specific events (designed in the bHaptics Designer/Portal). These are the recommended functions for most applications.
play
play({ eventKey, startTime, intensityRatio, durationRatio, offsetX, offsetY, deviceIndex }: PlayParams): Promise<void>;
type PlayParams = {
eventKey: string; // Name of the haptic event
startTime?: number; // Playback start position in milliseconds
intensityRatio?: number; // Intensity multiplier [0.0 - 2.0]
durationRatio?: number; // Duration multiplier
offsetX?: number; // Rotation offset [0.0 - 360.0]
offsetY?: number; // Vertical offset [-0.5 - 0.5]
deviceIndex?: number; // Target device index
};
Play the predefined haptic event.
Parameters
eventKey: Name of the haptic event.startTime(optional): Playback start position in milliseconds — the firststartTimemilliseconds of the event are skipped. For example, playing a 3,260ms event withstartTime: 2000plays only the last 1,260ms. (Default:0)intensityRatio(optional): Multiplier for haptic intensity. Valid range is: [0.0-2.0] (Default:1.0)durationRatio(optional): Multiplier for duration. (Default:1.0)offsetX(optional): Rotate haptic counterclockwise. Valid range is: [0.0-360.0] (Default:0.0)offsetY(optional): Move haptic up or down. Valid range is: [-0.5-0.5] (Default:0.0)deviceIndex(optional): Index of the device to target when multiple devices of the same position type are connected. (Default:-1, no specific device)
Returns
The promise resolves with no value (undefined) once the request is sent to bHaptics Player.
Note that it also resolves normally when the event key is not registered — in that case nothing plays and no error is thrown. Use getEvent to check whether an event key exists.
Example
import Tact from "tact-js";
const playExample = async () => {
await Tact.play({ eventKey: "heartbeat" });
};
const playMoreExample = async () => {
await Tact.play({
eventKey: "hit",
startTime: 0,
intensityRatio: 1.0,
durationRatio: 1.0,
offsetX: 180.0,
offsetY: 0.25,
});
};
playLoop
playLoop({ eventKey, intensityRatio, durationRatio, interval, maxCount, offsetX, offsetY, deviceIndex }: PlayLoopParams): Promise<number>;
type PlayLoopParams = {
eventKey: string; // Name of the haptic event
intensityRatio?: number; // Intensity multiplier [0.0 - 2.0]
durationRatio?: number; // Duration multiplier
interval: number; // Pause between repetitions (milliseconds)
maxCount: number; // Total number of plays
offsetX: number; // Rotation offset [0.0 - 360.0]
offsetY: number; // Vertical offset [-0.5 - 0.5]
deviceIndex?: number; // Target device index
};
Play the haptic event in a loop for a specified number of times.
Parameters
eventKey: Name of the haptic event.intensityRatio(optional): Multiplier for haptic intensity. Valid range is: [0.0-2.0] (Default:1.0)durationRatio(optional): Multiplier for duration. (Default:1.0)interval: Pause between repetitions in milliseconds, measured from the end of one playback to the start of the next. For example, a 460ms event withinterval: 500andmaxCount: 3finishes after about 2,380ms (460 + 500 + 460 + 500 + 460).maxCount: Total number of times to play the haptic event, including the first play. Values less than1behave as1.offsetX: Rotate haptic counterclockwise. Valid range is: [0.0-360.0] (Default:0.0)offsetY: Move haptic up or down. Valid range is: [-0.5-0.5] (Default:0.0)deviceIndex(optional): Index of the device to target when multiple devices of the same position type are connected. (Default:-1, no specific device)
Returns
Request ID identifying this playback request.
Note that a request ID is returned even when the event key is not registered — in that case nothing plays. To stop a looping event, use stop with the event key or stopAll.
While the loop is running, isPlayingByEventKey returns true for the whole span, including the interval pauses between repetitions.
Example
import Tact from "tact-js";
const playLoopExample = async () => {
const requestId = await Tact.playLoop({
eventKey: "hit",
intensityRatio: 1.0,
durationRatio: 1.0,
interval: 1000,
maxCount: 5,
offsetX: 0.0,
offsetY: 0.0,
});
console.log(`Playback request ID: ${requestId}`);
};
Play Haptic Directly
If you want to play haptics without events, use these functions.
playDot
playDot({ position, motorValues, duration, deviceIndex }: PlayDotParams): Promise<number>;
type PlayDotParams = {
position: PositionType; // Device position type
motorValues: number[]; // Array of motor intensity values [0 - 100]
duration?: number; // Duration in milliseconds
deviceIndex?: number; // Target device index
};
Play haptic feedback on the specific haptic actuators.
Parameters
position: Type of haptic device to playback. Refer thePositionTypeenum.motorValues: Array of intensity values for each motor. The array length must match the number of motors in the device. Each value must be integer and in range. Valid range is: [0-100].duration(optional): Duration of haptic feedback in milliseconds. Greater than or equal to100is recommended.deviceIndex(optional): Index of the device to target when multiple devices of the same position type are connected. (Default:-1, no specific device)
Returns
Request ID identifying this playback request. To stop playback, use stopAll.
Note that the parameters are not validated client-side — a request ID is returned even when, for example, motorValues has a wrong length or contains out-of-range values; in that case the haptic may simply not play.
Example
import Tact, { PositionType } from "tact-js";
const playDotExample = async () => {
const requestId = await Tact.playDot({
position: PositionType.ForearmL, // Left TactSleeve
motorValues: [100, 80, 60], // TactSleeve has 3 motors
duration: 100
});
console.log(`Playback request ID: ${requestId}`);
}
playPath
playPath({ position, x, y, intensity, duration, deviceIndex }: PlayPathParams): Promise<number>;
type PlayPathParams = {
position: PositionType; // Device position type
x: number[]; // Array of x coordinates [0.0 - 1.0]
y: number[]; // Array of y coordinates [0.0 - 1.0]
intensity: number[]; // Array of intensity values [0 - 100]
duration?: number; // Duration in milliseconds
deviceIndex?: number; // Target device index
};
Play haptic feedback along specified coordinates. Unlike playDot which controls individual motors, this method specifies haptic intensity for particular coordinates on the device.
When specifying haptic position, playDot offers discrete control, while playPath is more continuous. playDot assigns intensity to individual actuators, whereas playPath allows intensity specification for specific coordinates (between 0 and 1 for both X and Y axis), causing nearby actuators to vibrate accordingly.
You can specify multiple coordinates with multiple intensities. Note that all actuators around these coordinates in the list will activate simultaneously, not sequentially. The size of all arrays (x, y, and intensity) must be the same.
If the x, y, and intensity arrays have different lengths, the SDK's internal WebAssembly module crashes with an unrecoverable error (verified in tact-js 2.0.4) — haptics stop working until the page is reloaded. Always make sure the three arrays have the same length before calling.
By continuously calling this function while gradually changing the values, you can achieve the effect of a moving haptic point.

Parameters
position: Type of haptic device to playback. Refer thePositionTypeenum.x: Array of X coordinates for the haptic effect. Each value must be in range: [0.0-1.0]y: Array of Y coordinates for the haptic effect. Each value must be in range: [0.0-1.0]intensity: Array of intensity values for each coordinate. Each value must be integer and in range: [0-100]duration(optional): Duration of haptic feedback in milliseconds. Greater than or equal to100is recommended.deviceIndex(optional): Index of the device to target when multiple devices of the same position type are connected. (Default:-1, no specific device)
Returns
Request ID identifying this playback request. To stop playback, use stopAll.
Example
import Tact, { PositionType } from "tact-js";
const HAPTIC_PATHS = [
{
x: [0.738, 0.723, 0.709, 0.696, 0.682, 0.667, 0.653],
y: [0.680, 0.715, 0.749, 0.782, 0.816, 0.852, 0.885],
intensity: 20
},
{
x: [0.061, 0.072, 0.102, 0.184, 0.254, 0.310, 0.363],
y: [0.632, 0.587, 0.542, 0.498, 0.411, 0.366, 0.301],
intensity: 60
}
];
const TIME_OF_FRAME = 250; // milliseconds
const playPathExample = async () => {
const frames = HAPTIC_PATHS[0].x.length;
for (let i = 0; i < frames; i++) {
await Tact.playPath({
position: PositionType.Vest, // TactSuit
x: HAPTIC_PATHS.map(point => point.x[i]),
y: HAPTIC_PATHS.map(point => point.y[i]),
intensity: HAPTIC_PATHS.map(point => point.intensity),
duration: TIME_OF_FRAME
});
await new Promise(resolve => setTimeout(resolve, TIME_OF_FRAME));
}
}
playGlove
playGlove({ position, motors, playtimes, shapes, repeatCount }: PlayGloveParams): Promise<number>;
type PlayGloveParams = {
position: PositionType; // GloveL or GloveR
motors: Int32Array; // Array of 8 motor intensity values [0 - 100]
playtimes: Int32Array; // Array of 8 time interval values
shapes: Int32Array; // Array of 8 waveform values
repeatCount: number; // Number of times to play the pattern
};
TactGlove Only. Play haptics in TactGlove with fine-grained control over duration and intensity patterns. This provides more detailed control compared to playDot.
Each array parameter must contain exactly eight elements, one for each motor in the glove.
Parameters
-
position: Type of haptic device to playback. Must usePositionType.GloveLorPositionType.GloveR. -
motors: Array of 8 intensity values, one per motor. Each value must be integer and in range: [0-100]Array Index Motor is positioned at… 0Tip of the thumb 1Tip of the index finger 2Tip of the middle finger 3Tip of the ring finger 4Tip of the little finger 5On the wrist 6On the palm (thumb side) 7On the palm (little finger side) -
playtimes: Array of 8 time interval values. Valid values are:Value Duration 15ms 210ms 420ms 630ms 840ms -
shapes: Array of 8 waveform values controlling how intensity changes over time:Value Waveform Pattern 0Constant intensity for the duration 1Starts at specified intensity and decreases by half 2Starts at half intensity and increases to full -
repeatCount: Total number of times to play the 8-motor pattern, including the first play.
Returns
Request ID identifying this playback request. To stop playback, use stopAll.
Example
import Tact, { PositionType } from "tact-js";
const playGloveExample = async () => {
const motors = new Int32Array([100, 100, 100, 100, 100, 100, 100, 100]);
const playtimes = new Int32Array([8, 8, 8, 8, 8, 8, 8, 8]);
const shapes = new Int32Array([2, 2, 2, 2, 2, 2, 2, 2]);
const requestId = await Tact.playGlove({
position: PositionType.GloveR,
motors,
playtimes,
shapes,
repeatCount: 3
});
console.log(`Playback request ID: ${requestId}`);
}
Playback Control
pause
pause(eventKey: string): Promise<void>
Pause a specific haptic event that is currently playing.
Parameters
eventKey: Name of the haptic event to pause.
Example
import Tact from "tact-js";
const playAndPauseExample = async () => {
await Tact.play({ eventKey: "dash" });
await new Promise(resolve => setTimeout(resolve, 1000));
await Tact.pause("dash");
}
resume
resume(eventKey: string): Promise<void>
Resume a previously paused haptic event.
Parameters
eventKey: Name of the haptic event to resume.
Example
import Tact from "tact-js";
const playPauseAndResumeExample = async () => {
await Tact.play({ eventKey: "dash" });
await new Promise(resolve => setTimeout(resolve, 1000));
await Tact.pause("dash");
await new Promise(resolve => setTimeout(resolve, 1000));
await Tact.resume("dash");
}
stop
stop(eventKey: string): Promise<void>
Stop a specific haptic event completely.
Parameters
eventKey: Name of the haptic event to stop.
Example
import Tact from "tact-js";
const stopExample = async () => {
await Tact.play({ eventKey: "dash" });
await new Promise(resolve => setTimeout(resolve, 1000));
await Tact.stop("dash");
}
stopAll
stopAll(): Promise<void>
Stop all currently playing haptics.
Example
import Tact from "tact-js";
const stopAllExample = async () => {
await Tact.play({ eventKey: "dash" });
await Tact.play({ eventKey: "heartbeat" });
await Tact.play({ eventKey: "hit" });
await Tact.play({ eventKey: "slash" });
await new Promise(resolve => setTimeout(resolve, 500));
await Tact.stopAll();
}
Lifecycle
init
init({ appId, apiKey }: InitParams): Promise<boolean>;
type InitParams = {
appId: string; // Your bHaptics App ID
apiKey: string; // Your bHaptics API Key
};
Initialize the bHaptics SDK. This function must be called before using any other functions in the SDK. It initializes the SDK and establishes connection with bHaptics Player.
Parameters
appId: Your bHaptics application ID from the bHaptics Developer PortalapiKey: Your bHaptics API key from the bHaptics Developer Portal
Returns
Returns true if initialization is successful, false otherwise.
Example
import Tact from "tact-js";
const initTact = async () => {
const result = await Tact.init({
appId: "your-app-id",
apiKey: "your-api-key",
});
console.log(result ? "Connection Success!" : "Connection Failed...");
}
Status Checking
getConnectedDevices
getConnectedDevices(): Promise<any[]>
Get a list of all connected haptic devices.
Returns
Array of connected device objects. Returns an empty array ([]) when no devices are connected. Each object has the following fields:
| Field | Type | Description |
|---|---|---|
position | number | Numeric position of the device: 0 Vest, 1 ForearmL, 2 ForearmR, 3 Head, 4 HandL, 5 HandR, 6 FootL, 7 FootR, 8 GloveL, 9 GloveR. Use PositionUtils.positionToType(position) to convert it to a PositionType. |
deviceName | string | Display name of the device (e.g. "TactGlove (L)"). |
address | string | MAC address of the device. Use it with ping. |
connected | boolean | Whether the device is currently connected. |
paired | boolean | Whether the device is paired in bHaptics Player. |
battery | number | Battery level in percent. |
audioJackIn | boolean | Whether an audio jack is plugged in (audio-accessory devices). |
vsm | number | Vibration strength setting configured in bHaptics Player. |
The example of return value follows:
[
{
"position": 8,
"deviceName": "TactGlove (L)",
"address": "F418EB165E99",
"connected": true,
"paired": true,
"battery": 91,
"audioJackIn": false,
"vsm": 20
},
{
"position": 9,
"deviceName": "TactGlove (R)",
"address": "DF8B33412EC5",
"connected": true,
"paired": true,
"battery": 84,
"audioJackIn": false,
"vsm": 20
}
]
Example
import Tact from "tact-js";
const showDevicesExample = async () => {
const devices = await Tact.getConnectedDevices();
console.log("Connected devices:", devices);
}
getHapticMappings
getHapticMappings(): Promise<any[]>
Get a list of all haptic events registered in the current haptic application (deployed from the bHaptics Developer Portal).
Returns
Array of haptic mapping objects. Each object has the following fields:
| Field | Type | Description |
|---|---|---|
eventName | string | Event key. Use it with play and the other event-driven functions. |
eventTime | number | Duration of the event in milliseconds. |
The example of return value follows:
[
{
"eventName": "punch-recoil",
"eventTime": 180
},
{
"eventName": "dash",
"eventTime": 3260
},
{
"eventName": "teleport",
"eventTime": 460
}
]
Example
import Tact from "tact-js";
const showMappingsExample = async () => {
const mappings = await Tact.getHapticMappings();
for (const mapping of mappings) {
console.log(`${mapping.eventName}: ${mapping.eventTime}ms`);
}
}
getEvent
getEvent(eventKey: string): Promise<number>
Get the duration of a registered haptic event.
Parameters
eventKey: Name of the haptic event to look up.
Returns
Duration of the event in milliseconds. Returns 0 if the event key is not registered — you can use this to check whether an event key exists before calling play.
Example
import Tact from "tact-js";
const getEventExample = async () => {
const duration = await Tact.getEvent("dash");
if (duration > 0) {
console.log(`dash is ${duration}ms long`);
} else {
console.log("dash is not registered");
}
}
isDeviceConnected
isDeviceConnected(position: PositionType): Promise<boolean>
Check if a specific device type is connected.
Parameters
position: Type of haptic device to playback. Refer thePositionTypeenum.
Returns
true if the device type is connected, false otherwise.
Example
import Tact, { PositionType } from "tact-js";
const isDeviceConnectedExample = async () => {
const isGloveConnected = await Tact.isDeviceConnected(PositionType.GloveR);
console.log("Right TactGlove connected:", isGloveConnected);
}
isConnected
isConnected(): Promise<boolean>
Check the connection status with bHaptics Player.
Returns
true if connected to bHaptics Player, false otherwise.
isPlaying
isPlaying(): Promise<boolean>
Check if any haptic feedback is currently playing.
Returns
true if any haptic is playing, false otherwise.
isPlayingByEventKey
isPlayingByEventKey(eventKey: string): Promise<boolean>
Check if a specific haptic event is currently playing.
Parameters
eventKey: Name of the haptic event to check
Returns
true if the specified haptic event is playing, false otherwise.
Device Control
ping
ping(address: string): Promise<void>
Sends a ping signal to a specific device to test connection.
Parameters
address: The MAC address of the haptic device. You can get the address fromgetConnectedDevices()
Example
import Tact from "tact-js";
const pingFirstDeviceExample = async () => {
const devices = await Tact.getConnectedDevices();
if (devices.length > 0) {
await Tact.ping(devices[0].address);
}
}
pingAll
pingAll(): Promise<void>
Send a ping signal to all connected devices.
motorTest
motorTest(): Promise<void>
Run a motor test sequence on the TactSuit (Vest): each of the 40 motor positions fires at full intensity for one second, one after another. The full sequence takes about 40 seconds; the returned promise resolves when the sequence finishes.
Note that this test covers the Vest position only — it does not test other device types.
Common Enums
PositionType
enum PositionType {
Vest = "Vest",
ForearmL = "ForearmL",
ForearmR = "ForearmR",
Head = "Head",
HandL = "HandL",
HandR = "HandR",
FootL = "FootL",
FootR = "FootR",
GloveL = "GloveL",
GloveR = "GloveR"
}
String enums. Use it to decide the type of haptic device.
| Value | Device | Motors Count |
|---|---|---|
Vest | TactSuit | 32 |
ForearmL | TactSleeve(Left) | 3 |
ForearmR | TactSleeve(Right) | 3 |
Head | TactVisor | 4 |
HandL | Tactosy for Hands(Left) | 3 |
HandR | Tactosy for Hands(Right) | 3 |
FootL | Tactosy for Feet(Left) | 3 |
FootR | Tactosy for Feet(Right) | 3 |
GloveL | TactGlove(Left) | 8 |
GloveR | TactGlove(Right) | 8 |
tact-js also exports a PositionUtils class with static helpers for converting between the numeric positions used by getConnectedDevices and the PositionType enum:
import { PositionType, PositionUtils } from "tact-js";
PositionUtils.positionToType(9); // PositionType.GloveR
PositionUtils.enumToPosition(PositionType.Vest); // 0