Connectors
The first thing to do when writing a Buttplug application is figuring out how to talk to a Buttplug Server (like Intiface Central). For sake of simplicity, we'll cover websockets in this part of the manual, as this is by far the most common method of connecting clients and servers. Other connection situations and solutions (WebRTC, iroh, etc...) are covered in the cookbook section.
Websocket Connectors
Websockets are the default connector transport for Buttplug. They work as both a transport for desktop applications and web browsers, and have implementations available in most programming popular languages. As the library does not send many messages (maybe 50 per second in busy cases), the overhead of websockets isn't really an issue for the library.
Client implementations made by the Buttplug Core Team will provide a websocket connector for you. You should be able to create the connector, define the server address, and use that to connect. For using Websocket servers, you'll need to provide the user a way to pass in the server address (as this will not always exist on the same machine your software is running on), then you just create the connector object using that address.
When Buttplug's first public server came out in 2017, it used port 12345 as a test value. This stuck with the system, so now most applications that use Intiface or Buttplug use port 12345 for connection by default. Some client applications have gone as far as to hardcore the value, but this is not recommended.
This can sometimes be an issue for users running certain software that collides with the port. We have some recommended fixes in the Intiface Central Documentation.
- Rust
- C#
- Javascript (Web)
- TypeScript
- Python
use buttplug_client::{
ButtplugClient,
connector::ButtplugRemoteClientConnector,
serializer::ButtplugClientJSONSerializer,
};
use buttplug_transport_websocket_tungstenite::ButtplugWebsocketClientTransport;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// To create a Websocket Connector, you need the websocket address and some generics fuckery.
let connector = ButtplugRemoteClientConnector::<
ButtplugWebsocketClientTransport,
ButtplugClientJSONSerializer,
>::new(ButtplugWebsocketClientTransport::new_insecure_connector(
"ws://127.0.0.1:12345",
));
let client = ButtplugClient::new("Example Client");
client.connect(connector).await?;
Ok(())
}
// Buttplug C# - Remote Connector Example
//
// This example demonstrates the explicit WebSocket connector setup.
// While the ConnectAsync(string) extension method is convenient,
// creating the connector explicitly gives you more control.
using Buttplug.Client;
// Method 1: Using the convenience extension (recommended for most cases)
// This creates a WebSocket connector automatically from a URI string.
var client1 = new ButtplugClient("Simple Connection");
// Method 2: Using a Uri object
// Still uses the extension method, but allows Uri manipulation first.
var client2 = new ButtplugClient("Uri Connection");
var uri = new Uri("ws://127.0.0.1:12345");
// Method 3: Explicit connector creation
// Use this when you need custom connector configuration or
// when implementing a custom connector.
var client3 = new ButtplugClient("Explicit Connector");
var connector = new ButtplugWebsocketConnector(new Uri("ws://127.0.0.1:12345"));
// Let's actually connect using the explicit connector method
try
{
await client3.ConnectAsync(connector);
Console.WriteLine("Connected!");
Console.WriteLine($" Client name: {client3.Name}");
Console.WriteLine($" Connected: {client3.Connected}");
Console.WriteLine("\nPress Enter to disconnect...");
Console.ReadLine();
await client3.DisconnectAsync();
Console.WriteLine("Disconnected.");
}
catch (Exception ex)
{
Console.WriteLine($"Connection failed: {ex.Message}");
Console.WriteLine("\nMake sure Intiface Central is running with the server started.");
}
Console.WriteLine("\nPress Enter to exit...");
Console.ReadLine();
// Buttplug Web - Remote Websocket Connector Example
//
// This example demonstrates how to connect to a remote Buttplug server
// using the websocket connector. This is the standard way to connect
// from a browser to Intiface Central.
//
// Include Buttplug via CDN:
// <script src="https://cdn.jsdelivr.net/npm/buttplug@4.0.0/dist/web/buttplug.min.js"></script>
const runWebsocketConnectionExample = async () => {
// This is the default insecure address for Intiface Central (https://intiface.com/central).
// You can connect to it via most browsers.
const address = "ws://localhost:12345";
// Create the connector with the server address
const connector = new Buttplug.ButtplugBrowserWebsocketClientConnector(address);
const client = new Buttplug.ButtplugClient("Websocket Connection Example");
// Set up disconnect handler before connecting
client.addListener("disconnect", () => {
console.log("Server connection lost!");
});
// Now we connect. If anything goes wrong here, we'll either throw:
//
// - A ButtplugClientConnectorException if there's a problem with
// the connector, like the network address being wrong, server not
// being up, etc.
// - A ButtplugInitError if there is a client/server version mismatch.
try {
console.log(`Connecting to ${address}...`);
await client.connect(connector);
} catch (ex) {
// If our connection failed, because the server wasn't turned on, SSL/TLS
// wasn't turned off, etc, we'll just print and exit here.
//
// This could also mean our client is newer than our server, and we need to
// upgrade the server we're connecting to.
console.log("Connection failed:", ex);
return;
}
// We're connected!
console.log("Connected!");
console.log("Connection will disconnect automatically in 3 seconds...");
// Demonstrate we can use the connection
await client.startScanning();
console.log("Scanning for devices...");
// Disconnect after a delay
setTimeout(async () => {
console.log("Stopping scan...");
await client.stopScanning();
// Show any devices that were found
if (client.devices.size > 0) {
console.log("Devices found:");
for (const [index, device] of client.devices) {
console.log(` - ${device.name} (Index: ${index})`);
}
}
console.log("Disconnecting...");
await client.disconnect();
console.log("Disconnected.");
}, 3000);
};
// Buttplug TypeScript - Remote Connector Example
//
// This example demonstrates the explicit WebSocket connector setup.
// While you can create a connector inline, creating it explicitly
// gives you more control over the connection parameters.
//
// Prerequisites:
// 1. Install Intiface Central: https://intiface.com/central
// 2. Start the server in Intiface Central
// 3. Run: npx ts-node --esm remote-connector-example.ts
import {
ButtplugClient,
ButtplugNodeWebsocketClientConnector,
ButtplugBrowserWebsocketClientConnector,
} from 'buttplug';
import * as readline from 'readline';
async function waitForEnter(prompt: string): Promise<void> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise((resolve) => {
rl.question(prompt, () => {
rl.close();
resolve();
});
});
}
async function main(): Promise<void> {
// Method 1: Inline connector creation (most common)
// This is the simplest approach for most applications.
console.log('Method 1: Inline connector creation');
console.log(' const connector = new ButtplugNodeWebsocketClientConnector(url);');
console.log(' await client.connect(connector);');
console.log(' (Simple and direct)\n');
// Method 2: Explicit connector creation
// Use this when you need to reuse the connector or
// configure it before connecting.
console.log('Method 2: Explicit connector creation');
console.log(' const connector = new ButtplugNodeWebsocketClientConnector(url);');
console.log(' // ... configure connector if needed ...');
console.log(' await client.connect(connector);');
console.log(' (More control over connector lifecycle)\n');
// Note about environments:
// - Node.js: Use ButtplugNodeWebsocketClientConnector (uses 'ws' package)
// - Browser: Use ButtplugBrowserWebsocketClientConnector (uses native WebSocket)
console.log('Environment-specific connectors:');
console.log(' - Node.js: ButtplugNodeWebsocketClientConnector');
console.log(' - Browser: ButtplugBrowserWebsocketClientConnector\n');
// Let's actually connect using the explicit connector method
const client = new ButtplugClient('Remote Connector Example');
const connector = new ButtplugNodeWebsocketClientConnector(
'ws://127.0.0.1:12345'
);
console.log('Connecting using explicit connector...');
try {
await client.connect(connector);
console.log('Connected successfully!');
console.log(` Connected: ${client.connected}`);
await waitForEnter('\nPress Enter to disconnect...');
await client.disconnect();
console.log('Disconnected.');
} catch (e) {
if (e instanceof Error) {
console.log(`Connection failed: ${e.message}`);
}
console.log(
'\nMake sure Intiface Central is running with the server started.'
);
}
await waitForEnter('\nPress Enter to exit...');
}
main().catch(console.error);
"""Connection - Connect to a Buttplug server.
This is the simplest possible Buttplug example. It connects to a
Buttplug server (like Intiface Central) and shows connection status.
Prerequisites:
1. Install Intiface Central: https://intiface.com/central/
2. Start Intiface Central and click "Start Server"
3. Run this script: python connection.py
"""
import asyncio
from buttplug import ButtplugClient, ButtplugError
async def main() -> None:
# Create a client with your application's name
client = ButtplugClient("Connection Example")
try:
# Connect to the server (Intiface Central default address)
print("Connecting to server...")
await client.connect("ws://127.0.0.1:12345")
print(f"Connected to: {client.server_name}")
# Connection is established - you can now scan for devices
print("Connection successful!")
except ButtplugError as e:
# Handle connection errors
print(f"Failed to connect: {e}")
return
finally:
# Always disconnect when done
if client.connected:
await client.disconnect()
print("Disconnected.")
if __name__ == "__main__":
asyncio.run(main())
Security Considerations
Due to basically being impossible to deal with, Intiface Engine/Central does not implement SSL websockets (wss). This required self-signed certificates which rarely worked correctly and caused end-user confusion. Non-secured websockets work for Intiface Central instances running on the same host as web apps, due to localhost security exceptions. Remote browser connections (i.e. browser on desktop, intiface central on phone) may fail due to security requirements. Intiface Central provides a Repeater Mode (basically a proxy) to work with instances where web browsers on a machine other than the Intiface Central is accessing hardware.