05-03-26
Multi-PC Input Mirroring with WebSocket
Mirror your keyboard and mouse input across multiple PCs using Lua scripts and hardware input with Rebind. This guide provides WebSocket-based solutions for low-latency input replication.
Question
Can I set up a multi-boxing setup in Rebind where all mouse movements and keystrokes are mirrored to other PCs?
Rebind AI
Yes, you can set up a multi-boxing configuration in Rebind. Here are a few approaches:
Option 1: WebSocket Server (Real-time, Low-Latency)
Set up a Rebind script on your "master" PC that streams all input to one or more "slave" PCs via WebSocket. The slave PCs receive commands and replay them.
Master PC script:
-- rebind: name=Multibox Master
-- rebind: permission=net
local cfg = UI.Schema({
slave_ips = UI.Text("127.0.0.1:9001", {
label = "Slave IPs",
placeholder = "comma-separated: 127.0.0.1:9001, 192.168.1.100:9001"
}),
})
local clients = {}
function OnStart()
for ip in cfg.slave_ips:gmatch("[^,]+") do
ip = ip:match("^%s*(.-)%s*$") -- trim
table.insert(clients, ip)
end
Log.Info("Master ready. Slaves: " .. table.concat(clients, ", "))
end
local function broadcast(msg)
for _, ip in ipairs(clients) do
pcall(function()
Net.Post("http://" .. ip .. "/cmd", JSON.Stringify(msg), { timeout = 1000 })
end)
end
end
function OnDown(key)
broadcast({ t = "down", key = key })
return true
end
function OnUp(key, duration)
broadcast({ t = "up", key = key })
return true
end
function OnMove(dx, dy)
broadcast({ t = "move", dx = dx, dy = dy })
return true
end
function OnScroll(delta)
broadcast({ t = "scroll", delta = delta })
return true
endSlave PC script:
-- rebind: name=Multibox Slave
-- rebind: permission=net
local cfg = UI.Schema({
port = UI.Slider(9001, { min = 8000, max = 9999, label = "Listen Port" }),
})
local server = nil
function OnStart()
server = Net.Listen(cfg.port, function(req)
if req.method == "POST" then
local cmd = JSON.Parse(req.body)
if cmd.t == "down" then
HID.Down(cmd.key)
elseif cmd.t == "up" then
HID.Up(cmd.key)
elseif cmd.t == "move" then
HID.Move(cmd.dx, cmd.dy)
elseif cmd.t == "scroll" then
HID.Scroll(cmd.delta)
end
return { status = 200, body = "OK" }
end
return { status = 404, body = "Not Found" }
end)
Log.Info("Slave listening on port " .. cfg.port)
end
function OnStop()
if server then server:Stop() end
endPros:
- Simple, works over network
- Easy to set up multiple slaves
- Works from anywhere (LAN or internet)
Cons:
- ~10-20ms latency per command (HTTP overhead)
- Not ideal for high-frequency actions (gaming, fast clicking)
Option 2: WebSocket (Persistent Connection, Better Latency)
Lower latency than HTTP polling. Master opens a persistent WebSocket connection to each slave.
Master:
-- rebind: name=Multibox Master (WebSocket)
-- rebind: permission=net
local cfg = UI.Schema({
slave_urls = UI.Text("ws://127.0.0.1:9001", {
label = "Slave WebSocket URLs",
placeholder = "ws://192.168.1.100:9001, ws://192.168.1.101:9001"
}),
})
local conns = {}
function OnStart()
for url in cfg.slave_urls:gmatch("[^,]+") do
url = url:match("^%s*(.-)%s*$")
pcall(function()
local conn = Net.WSConnect(url, {})
table.insert(conns, conn)
Log.Info("Connected to " .. url)
end)
end
end
local function broadcast(msg)
for _, conn in ipairs(conns) do
pcall(function()
conn:Send(JSON.Stringify(msg))
end)
end
end
function OnDown(key)
broadcast({ t = "down", key = key })
return true
end
function OnUp(key)
broadcast({ t = "up", key = key })
return true
end
function OnMove(dx, dy)
broadcast({ t = "move", dx = dx, dy = dy })
return true
end
function OnScroll(delta)
broadcast({ t = "scroll", delta = delta })
return true
end
function OnStop()
for _, conn in ipairs(conns) do
pcall(function() conn:Close() end)
end
endSlave:
-- rebind: name=Multibox Slave (WebSocket)
-- rebind: permission=net
local cfg = UI.Schema({
port = UI.Slider(9001, { min = 8000, max = 9999, label = "Listen Port" }),
})
local server = nil
function OnStart()
server = Net.WSListen(cfg.port, {
OnMessage = function(client, payload)
local cmd = JSON.Parse(payload)
if cmd.t == "down" then
HID.Down(cmd.key)
elseif cmd.t == "up" then
HID.Up(cmd.key)
elseif cmd.t == "move" then
HID.Move(cmd.dx, cmd.dy)
elseif cmd.t == "scroll" then
HID.Scroll(cmd.delta)
end
end,
})
Log.Info("WebSocket slave listening on port " .. cfg.port)
end
function OnStop()
if server then server:Stop() end
endPros:
- ~1-2ms round-trip latency (per SDK benchmarks)
- Persistent connection, lower overhead
- Better for fast sequences
Cons:
- Requires manual connection setup
- More complex
Option 3: Shared Memory (Local Network Only, Fastest)
Use Pipe for ultra-low latency on the same PC. For network multibox, you'd need external tools.
Option 4: Macro Streaming + Network
Record a macro on master, stream it to slaves via Macro.Stream():
Bind("F9", function()
local macro = Macro.Record()
-- ... record input ...
local m = Macro.Finish()
for _, ip in ipairs(slaves) do
Net.Post("http://" .. ip .. "/play", JSON.Stringify(m))
end
end)Slaves receive and play the macro. Good for synchronized sequences but not real-time input.
Recommendation
For true multiboxing (real-time mirroring): Use WebSocket (Option 2). It gives you:
- ~1-2ms latency (acceptable for most games)
- Persistent connection (no reconnect overhead)
- Scalable to many slaves
- Works over LAN or internet
For accessibility/casual use: HTTP (Option 1) is simpler to set up.
The WebSocket server performance metrics from the SDK are excellent — the relay can handle ~10,000 req/s sustained, which covers even very high-frequency input mirroring.