TVS Intiface Bridge
July 17, 2026 · View on GitHub
A BepInEx plugin for The Villain Simulator that drives an Intiface / Buttplug.io toy — a vibrator or a linear stroker — from what's actually happening in the game: thrust depth, arousal, and hits. No game files are modified; the plugin reads gameplay state at runtime, so it's fully reversible and survives game updates.
- Works with any Buttplug-supported device — no Lovense hardware or Lovense Connect required.
- Drives both device kinds from the same signal: a stroker mirrors position, a vibrator's intensity tracks the same value.
- Reads real gameplay — live penetration depth, arousal, and whip/hit strikes — not the game's built-in 1-D Lovense scalar.
- Works in desktop and VR mode.
Installation
1. Install BepInEx (one time)
This mod needs BepInEx 5.4.x, x64, Mono build (The Villain Simulator is a Unity Mono game).
- Download
BepInEx_win_x64_5.4.x.zipfrom the BepInEx releases. - Extract it into your game folder — the one containing
TheVillainSimulator.exe. You should now havewinhttp.dlland aBepInEx/folder next to the exe. - Launch the game once, then quit. This makes BepInEx generate its
BepInEx/plugins/folder.
Steam / Proton (Linux): add a launch option so the BepInEx loader is picked up:
WINEDLLOVERRIDES="winhttp=n,b" %command%
2. Install this mod
- Download
TVS-Intiface-vX.Y.Z.zipfrom this repo's Releases. - Extract it into your game folder (same place as
TheVillainSimulator.exe). It placesTVSIntiface.dllintoBepInEx/plugins/. (Or just copyTVSIntiface.dllintoBepInEx/plugins/manually.) - Launch the game once so the plugin writes its config file
(
BepInEx/config/com.peter.tvs.intiface.cfg).
Using it
- Install and run Intiface Central (or
intiface-engine). Start its server — the default address isws://127.0.0.1:12345(shown under Server Status). - Connect your toy in Intiface Central and confirm it's listed.
- Launch the game and play. The log line
[TVSIntiface] v2.0.0 active -> ws://127.0.0.1:12345inBepInEx/LogOutput.logconfirms the bridge started.
The toy now reacts live to the scene. Nothing else to configure for a basic setup.
What drives the toy
Each frame the plugin produces one signal and sends it to every connected device (stroker position + vibrator intensity), in priority order:
- Hit / whip → one short ~50% pulse, then nothing. (Stimulation like fingering or vibrators does not count as a hit — only actual strikes.)
- Penetration → live thrust depth. The stroker mirrors the real on-screen position; the vibrator's intensity tracks depth. If several holes are filled at once (ass + vagina + mouth), the most recent penetration wins. Depth is auto-calibrated per penetration — the plugin learns the travel range as you thrust, so it works across toys, holes and characters without tuning.
- Arousal (vibrator-in-her / passive drift / aphrodisiac serum) → the vibrator's intensity equals the arousal level; the stroker gently oscillates, faster as arousal rises.
- Orgasm / anything else → idle (off).
By default everything is read from the captive heroine (her holes and her
arousal). Set BaseOnHeroine = false to drive from the player's own
dick + arousal instead.
Configuration
BepInEx/config/com.peter.tvs.intiface.cfg (created on first launch):
| Section | Key | Default | Meaning |
|---|---|---|---|
General | Enabled | true | Master switch for the bridge. |
Intiface | ServerUrl | ws://127.0.0.1:12345 | Intiface server address (see Server Status in Intiface Central). |
Signal | BaseOnHeroine | true | Drive from the captive heroine's holes + arousal (true), or the player's own dick + arousal (false). |
Signal | InvertDepth | false | Flip stroke direction if the stroker moves opposite to the on-screen thrust. |
Signal | StrokeAmplitude | 1.0 | Stroke length (0–1) when oscillating in arousal mode. |
Signal | OscMinHz | 0.3 | Stroke frequency (Hz) at zero arousal, in arousal-oscillation mode. |
Signal | OscMaxHz | 2.0 | Stroke frequency (Hz) at full arousal. |
Signal | HitPulseLevel | 0.5 | Level (0–1) of the brief hit/whip pulse. |
Signal | HitPulseMs | 150 | Duration (ms) of the hit/whip pulse. |
Signal | MinDepthSpan | 0.03 | Auto-calibration: minimum observed thrust travel before depth drives the stroker. |
Output | LinearRateHz | 20 | How often stroker position commands are streamed (Hz). |
Output | MaxIntensity | 1.0 | Upper cap on vibrator intensity (0–1). |
Stroker moving the wrong way? Flip InvertDepth.
Limitations
- One signal for all devices. Every connected toy gets the same logical value — the plugin doesn't route different activities to different devices.
- Depth needs a stroke to calibrate. At the start of a new penetration the
stroker rests at the bottom until it has seen enough travel to learn the range
(
MinDepthSpan); it then tracks live. - If a scene ends abruptly without a clean withdrawal, the plugin prunes the penetration on the next frame and falls back to arousal/idle.
Uninstall
Delete TVSIntiface.dll from BepInEx/plugins/ (and its config file). To remove
BepInEx too, delete winhttp.dll, doorstop_config.ini, and the BepInEx/
folder from the game directory.
Building from source
Requires the .NET SDK and a game install that has BepInEx (for the reference assemblies — they are not redistributed here).
dotnet build src/TVSIntiface.csproj -c Release -p:GameDir="/path/to/TheVillainSimulator-48a"
(Or set the TVS_GAME_DIR environment variable instead of -p:GameDir.) The
output TVSIntiface.dll is written to src/bin/Release/, and also copied into
the game's BepInEx/plugins/ if that folder exists.
How it works
The plugin talks the Buttplug protocol directly over a tiny hand-rolled
RFC-6455 WebSocket (MiniWebSocket.cs) and JSON parser (Json.cs) — no extra
DLLs to ship and no reliance on the Unity Mono System.Net.WebSockets stack.
Gameplay is read via Harmony patches on the game's own methods:
ZNEDickToyBase.InsertionBegin/InsertionEndregister and drop ongoing penetrations (timestamped so the youngest wins). Depth is the toy tip's Z in the hole-entrance frame, auto-ranged per penetration.ZNEHeroStatusController.makeCharacterArousalReactionForDamageisolates actual strikes for the hit pulse.- Arousal is read live from the heroine's status controller (or the player dick controller).
Because this game disables the BepInEx manager object, a plugin's own Update
never runs — so the signal loop lives on its own DontDestroyOnLoad
GameObject that the plugin creates on startup.
License
MIT.