IntifaceSync
May 16, 2026 ยท View on GitHub
A Stash plugin that syncs funscript files with your toys via Intiface Central (Buttplug.io) or The Handy over WiFi (HSSP).
Features
- ๐ฏ Auto-detect funscripts โ automatically loads the funscript matching the scene filename
- ๐ Manual funscript selection โ all
.funscriptfiles in the scene's folder are listed in a dropdown; the script matching the scene name is selected by default - โถ๏ธ Play / Pause / Seek sync โ toy follows the video player in real time
- ๐๏ธ Stroke range โ limit minimum and maximum stroke depth (per device)
- โฑ๏ธ Offset โ fine-tune timing (ms) to compensate for device or network latency
- ๐ Invert โ flip the script direction
- ๐ Dual mode โ switch between Intiface Central and The Handy (WiFi) on the fly
- ๐ Built-in tunnel โ for Handy mode, a
localhost.runSSH tunnel is started automatically so The Handy can fetch the script from your local machine
Requirements
- Stash (recent version with plugin support)
- Python 3.8+ (for the backend)
- Intiface Central running locally or on your network (only for Intiface mode)
- The Handy connection key (only for Handy WiFi mode)
- SSH client (
ssh, available by default on Linux/macOS; on Windows via OpenSSH or Git Bash) โ required for Handy mode tunneling
Python dependencies
The backend will install these automatically on first run if missing:
websocketsaiohttp
Installation
Via Plugin Source (recommended)
- In Stash go to Settings โ Plugins.
- Click Sources โ Add Source.
- Enter:
- Name:
IntifaceSync - URL: https://raw.githubusercontent.com/vib3coda/Stash-Intiface-Handy/main/index.yml
- Name:
- Install IntifaceSync from the Available tab.
- Scroll to the plugin and enter your Intiface WebSocket IP if using Intiface mode.
- Open any scene that has a funscript โ the toolbar appears and sync starts automatically.
Manual Installation
- Copy the
IntifaceSyncfolder into your Stashpluginsdirectory. - In Stash go to Settings โ Plugins and click Reload Plugins.
- Open Settings โ Plugins โ IntifaceSync and enter your Intiface WebSocket IP if using Intiface mode.
- Open any scene that has a funscript โ the toolbar appears and sync starts automatically.
Usage
- Start the backend โ go to Settings โ Tasks, scroll to the bottom, and run the Start Backend task. The backend listens on port
7880. (maybe you need to open port 7880 in your firewall or docker settings) - Open a scene โ the IntifaceSync toolbar appears below the video player.
- Choose a mode โ click Intiface or The Handy in the toolbar.
- For Handy mode, enter your connection key when prompted.
- Click Connect โ the plugin connects to your device.
- Play the scene โ playback, pause, and seek are mirrored to the device.
- Scene change โ when you switch scenes, the matching funscript is loaded automatically.
Toolbar controls
| Control | Function |
|---|---|
| Mode (Intiface / The Handy) | Switch backend mode |
| Connect / Stop | Connect or disconnect the device |
| Funscript dropdown | Select which .funscript to use |
| Offset | Timing offset in milliseconds (negative = earlier) |
| Stroke Range | Min/max stroke depth (0โ100 %) |
| Invert | Flip script direction |
| โ Settings | Open settings popup |
Settings are persisted in localStorage.
Debug logging
Backend
Create an empty file named debug in the plugin folder:
touch /path/to/stash/plugins/IntifaceSync/debug
Restart the backend (re-run Start Backend task). Logs go to:
/tmp/intiface_sync.log
Frontend
Open the browser DevTools console and run:
localStorage.setItem("intifaceSyncDebug", "1");
Reload the page. To disable:
localStorage.removeItem("intifaceSyncDebug");
How it works
- Intiface mode โ the backend connects to Intiface Central via WebSocket (Buttplug protocol v3), parses the selected funscript, and streams
LinearCmdmessages to all connected linear devices at ~50 Hz with look-ahead scheduling. - Handy WiFi mode โ the backend
- starts a local HTTP server serving the selected funscript,
- opens an SSH tunnel via
localhost.runto expose it publicly, - uploads the script URL + SHA-256 to The Handy via the HSSP API,
- drives play / pause / seek through the Handy v2 REST API with server-time synchronization.
Troubleshooting
If something doesn't work, please collect logs before opening an issue.
Common issues
- Toolbar doesn't appear โ make sure the backend is running, port
7880is open (check your firewall and/or docker settings) and reload the scene page. - No devices found (Intiface) โ verify Intiface Central is running, the server is started, and your toy is connected and listed as a linear device.
- Handy upload fails โ check that
sshis installed (should install automatically) and the connection key is correct. Check/tmp/intiface_sync.logfor tunnel errors. - Sync drifts โ adjust the Offset value in the toolbar.
Reporting bugs
Please include:
- Stash version
- Browser + OS
- Browser console output (with debug logging enabled)
intiface_sync.logcontents (see /tmp foldere)- A short description of what you did and what happened
Privacy & Security
- Intiface mode โ no credentials involved. The backend connects directly to your local Intiface Central instance via WebSocket. Nothing leaves your machine.
- Handy WiFi mode โ your connection key is entered in the browser toolbar and sent only to the local Python backend via the WebSocket on port
7880. From there it is used exclusively to communicate with the official Handy API. It is not stored on disk. - The funscript is served temporarily over a
localhost.runSSH tunnel so The Handy can fetch it. The tunnel is only active while the script is being uploaded and is torn down automatically. The tunnel URL is a random subdomain and is never shared or logged. - The WebSocket backend binds to
0.0.0.0:7880โ if you are on a shared or untrusted network, consider firewalling this port.
License
MIT โ see LICENSE.
Author
vib3coda โ https://github.com/vib3coda/Stash-Intiface-Handy
Disclaimer
This is an unofficial, community-made plugin. Not affiliated with or endorsed by TheHandy or Intiface. This plugin was largely created with the help of AI (vibecoded). I've tested it thoroughly on my end and it works well, but any feedback or code improvements from experienced devs are highly welcome!
This plugin is provided as-is, without any warranty. The author is not responsible for any damage to devices, data loss, or any other issues arising from the use of this software. Use at your own risk.