npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@feelrobotics/feel-apps-api

v2.1.0

Published

Real-time haptic feedback and device control library for Feel Robotics applications

Readme

@feelrobotics/feel-apps-api

JavaScript/TypeScript library for real-time haptic feedback and device control in Feel Robotics applications.

Overview

@feelrobotics/feel-apps-api synchronizes video playback with haptic subtitle events and communicates with Feel haptic devices in real time over a Socket.IO connection to the Feel Exchange Center (FEC).

Installation

npm install @feelrobotics/feel-apps-api

Quick Start

import { init, destroy, apps, subs } from '@feelrobotics/feel-apps-api'

// Initialize — tokens are provided by your backend
await init(feelSubsToken, fecToken, userId, roomName, {
  fetchToken: async () => {
    const res = await fetch('/api/refresh-fec-token')
    return (await res.json()).fec_token
  },
})

// Load subtitles for a video
await subs.load(videoId, subtitlesId, externalUserId)

// Wire up the HTML5 video element
video.addEventListener('play',       () => subs.play(video.currentTime))
video.addEventListener('timeupdate', () => subs.timeupdate(video.currentTime))
video.addEventListener('pause',      () => subs.stop())

// Clean up when done
destroy()

Modes

| Mode | Function | Use case | |---|---|---| | Full | init() | Web app with device control + haptic subtitles | | Slider | initSlider() | Direct intensity control only, no subtitle support |

API Reference

Top-level

init(feelSubsToken, fecToken, userId, roomName, tokenRefresh?): Promise<void>

Full initialization. Connects to FEC, starts device monitoring, and sets up subtitle playback once a device connects. The returned promise resolves when the FEC socket connects (not when a device joins).

initSlider(fecToken, userId, roomName, tokenRefresh?): Promise<void>

Device control without subtitle support. Use apps.playSubtitle(percent, 0, []) to send haptic intensity directly.

TokenRefreshOptions

Both init and initSlider accept an optional tokenRefresh object to keep the FEC connection alive past the token's 24 h TTL. The token is refreshed every 12 h.

| Field | Type | Required | Description | |---|---|---|---| | fetchToken | () => string \| Promise<string> | yes | Returns a fresh FEC JWT | | onTokenError | (err: Error) => void | no | Called when fetchToken fails |

setServers(servers)

Override default API endpoints. Must be called before init.

setServers({ apps: 'https://...', subs: 'https://...' })

destroy()

Disconnect the socket and reset all internal state. Call this before re-initialising.


apps module

apps.playSubtitle(percentValue, positionMsec, subtitles)

Send haptic data to connected devices.

  • Pass a non-empty subtitles array for subtitle-synced haptic playback.
  • Pass an empty array [] for direct intensity control (slider mode).
// Subtitle playback — called automatically by the subs module
apps.playSubtitle(75, 1200, subtitleEntries)

// Direct slider control
apps.playSubtitle(percent, 0, [])

apps.getMobileAppLaunchUrl(requestToken): string

Returns a deep link URL to launch the Feel mobile app for pairing.

const url = apps.getMobileAppLaunchUrl(tokens.authToken)
// "feelapp://authorize?token=..."

apps.status.subscribe(callback) / apps.status.unsubscribe(callback)

Listen for device connection status changes.

const onStatus = ({ online, devices }) => { ... }
apps.status.subscribe(onStatus)
apps.status.unsubscribe(onStatus)

apps.data.subscribe(callback) / apps.data.unsubscribe(callback)

Listen for haptic position messages sent by peer devices.

const onData = (percent, deviceName) => { ... }
apps.data.subscribe(onData)
apps.data.unsubscribe(onData)

subs module

subs.load(videoId, subtitlesId, externalUserId?, channel?, options?): Promise<void>

Fetch subtitle data for a video. Waits for a device to connect before loading if none is connected yet. Supports AbortSignal for cancellation.

const controller = new AbortController()
await subs.load(videoId, subtitleId, userId, '', { signal: controller.signal })
controller.abort() // cancel a pending load

subs.play(currentPosSec)

Start subtitle playback from the given video position (in seconds).

subs.timeupdate(currentPosSec)

Update the current video position. Call on every timeupdate event. Handles seek detection and buffering automatically.

subs.stop()

Stop playback and reset the device to neutral intensity.

subs.onSubtitleEvent(callback) / subs.offSubtitleEvent(callback)

Subscribe to subtitle fire events. The callback receives percentValue (0–100).

const onHaptic = (percent) => setIntensity(percent)
subs.onSubtitleEvent(onHaptic)
subs.offSubtitleEvent(onHaptic)

Development

npm run build      # build the library
npm run dev        # watch mode
npm test           # run tests
npm run lint       # lint with Biome
npm run check:fix  # auto-fix lint and format issues

License

MIT © Feel Robotics