05-09-26

Virtual Mouse with WebSocket for AimSync

Create a virtual mouse controlled by real mouse movement, and send the position over a WebSocket to AimSync. This Lua script uses Rebind's input and networking capabilities for hardware input automati

rebindlua scriptvirtual mousewebsocketinput automationhardware inputaimsync

Question

Write a Lua script for Rebind that tracks real mouse delta movement to control a virtual cursor constrained within a 522x522 area. Toggle with G key. Use mouse delta between frames to move the cursor. Clamp position to 0-522. Draw a crosshair at the cursor position. Do not use any game engine callbacks — this should work from raw mouse input only. The script should send the virtual cursor position to a WebSocket server at ws://localhost:9123. The server will handle drawing the crosshair.

Rebind AI

I need to clarify the limits of what Rebind can do here:

Rebind's constraints:

  • ✅ Can track mouse movement via OnMove(dx, dy) hook
  • ✅ Can store a virtual cursor position and clamp it
  • ✅ Can toggle modes with the G key
  • ✅ Can send data over WebSocket
  • Cannot draw graphics — no rendering API exists. There's no way to draw a crosshair on screen from Luau.

Rebind is an input automation tool, not a graphics framework. It can:

  • Capture and remap input
  • Send keyboard/mouse output
  • Read window state, pixel colors, and system info
  • Run macros and sequences
  • Communicate via HTTP, WebSocket, or shared memory

But it has no drawing or overlay capabilities.


What you actually need:

For a virtual cursor visualization with graphics drawing, you'd need:

  1. AutoHotkey v2 — has Gui for custom windows with drawing commands
  2. Pythonpygame or tkinter for graphics + mouse library for tracking
  3. C# — WinForms/WPF with System.Windows.Forms.Cursor
  4. Game engines — Unity, Unreal, Godot (but overkill for this)

What I can help with in Rebind:

If you want to track and constrain a virtual cursor position (just the logic, without visualization), I can write that. Then you'd pair it with a separate tool that actually draws the graphics.

For example, a Rebind script could:

  • Track mouse delta via OnMove()
  • Maintain a virtual (x, y) position clamped to 0-522
  • Send that position to an external Python script via HTTP or WebSocket
  • The Python script renders the crosshair

Would that approach help? If so, I can write both the Rebind side and a Python example.

Question

will this work local ws = websocket.connect("ws://localhost:9123")

function OnMove(dx, dy) if virtual_mouse_enabled then virtual_x = math.clamp(virtual_x + dx * 0.3, 0, 522) virtual_y = math.clamp(virtual_y + dy * 0.3, 0, 522) ws.send(virtual_x .. "," .. virtual_y) end end

Rebind AI

No, that won't work. There are several issues:

  1. websocket.connect() doesn't exist in Rebind — that's not a valid API. Rebind has Net.WSConnect() for WebSocket clients, but it's designed for async event-driven communication, not synchronous request-response in OnMove.

  2. OnMove is asynchronous — it fires every frame and returns immediately. Sending data over the network on every mouse movement would cause severe latency and timing issues. OnMove should be minimal.

  3. math.clamp() doesn't exist — use math.max(0, math.min(522, value)) instead.

  4. WebSocket .send() is asyncNet.WSConnect() returns a connection handle with async methods. You can't call it synchronously in OnMove.


What would actually work:

Option 1: Use a background timer to send updates

lua
-- rebind: min_sdk=0.1.0
-- rebind: permission=net

local virtual_x, virtual_y = 261, 261
local virtual_mouse_enabled = false
local conn = nil

function OnStart()
    conn = Net.WSConnect("ws://localhost:9123", {
        OnOpen = function()
            Log.Info("Connected to virtual mouse server")
        end,
        OnClose = function()
            Log.Info("Disconnected")
        end,
    })
    
    -- send position updates every 16ms (~60 FPS)
    Timer.Every(16, function()
        if virtual_mouse_enabled and conn then
            conn:Send(virtual_x .. "," .. virtual_y)
        end
    end)
end

function OnDown(key)
    if key == "G" then
        virtual_mouse_enabled = not virtual_mouse_enabled
        UI.Notify(virtual_mouse_enabled and "Virtual Mouse ON" or "Virtual Mouse OFF", "info")
        return false
    end
    return true
end

function OnMove(dx, dy)
    if virtual_mouse_enabled then
        virtual_x = math.max(0, math.min(522, virtual_x + dx * 0.3))
        virtual_y = math.max(0, math.min(522, virtual_y + dy * 0.3))
    end
    return true
end

function OnStop()
    if conn then conn:Close() end
end

Option 2: Simpler — just track position, no networking

If you just want to track the virtual cursor position locally and log it:

lua
-- rebind: min_sdk=0.1.0
-- rebind: name=Virtual Mouse Tracker

local virtual_x, virtual_y = 261, 261
local virtual_mouse_enabled = false

function OnDown(key)
    if key == "G" then
        virtual_mouse_enabled = not virtual_mouse_enabled
        UI.Notify(virtual_mouse_enabled and "Tracking ON" or "Tracking OFF", "info")
        return false
    end
    return true
end

function OnMove(dx, dy)
    if virtual_mouse_enabled then
        virtual_x = math.max(0, math.min(522, virtual_x + dx * 0.3))
        virtual_y = math.max(0, math.min(522, virtual_y + dy * 0.3))
        Log.Debug(string.format("Virt cursor: %.0f, %.0f", virtual_x, virtual_y))
    end
    return true
end

The key difference: Keep OnMove fast (just update position), use Timer.Every() to batch network sends separately.

Does this clarify? Would you like me to flesh out a complete server + client example?

Question

what about this -- rebind: min_sdk=0.1.0 -- rebind: permission=net

local virtual_x, virtual_y = 261, 261 local virtual_mouse_enabled = false local conn = nil

function OnStart() conn = Net.WSConnect("ws://localhost:9123", { OnOpen = function() Log.Info("Virtual mouse connected") end, OnClose = function() Log.Info("Virtual mouse disconnected") end, })

Timer.Every(16, function()
    if virtual_mouse_enabled and conn then
        conn:Send(string.format("%.0f,%.0f", virtual_x, virtual_y))
    end
end)

end

function OnDown(key) if key == "G" then virtual_mouse_enabled = not virtual_mouse_enabled UI.Notify(virtual_mouse_enabled and "Virtual Mouse ON" or "Virtual Mouse OFF", "info") return false end return true end

function OnMove(dx, dy) if virtual_mouse_enabled then virtual_x = math.max(0, math.min(522, virtual_x + dx * 0.3)) virtual_y = math.max(0, math.min(522, virtual_y + dy * 0.3)) end return true end

function OnStop() if conn then conn:Close() end end

Rebind AI

Yes, this will work!

This is a solid implementation. Here's what you did right:

  1. Net.WSConnect() with async handlers — correct API for Rebind
  2. Timer.Every(16) for batched sends — keeps OnMove lightweight, sends updates at ~60 FPS
  3. string.format() for position — clean formatting
  4. math.max/min clamping — correct constraint logic
  5. Toggle with G key — returns false to block the physical key
  6. Cleanup in OnStop() — closes the connection

Minor suggestions:

1. Return value in OnMove

You're not explicitly returning, which means the move passes through (default). If you want to block real mouse movement while in virtual mode, add:

lua
function OnMove(dx, dy)
    if virtual_mouse_enabled then
        virtual_x = math.max(0, math.min(522, virtual_x + dx * 0.3))
        virtual_y = math.max(0, math.min(522, virtual_y + dy * 0.3))
        return false  -- block real movement
    end
    return true  -- pass through when disabled
end

2. Connection check before sending

The timer already checks conn, but you could also verify it's actually connected:

lua
Timer.Every(16, function()
    if virtual_mouse_enabled and conn then
        -- optional: only send if connection is open
        conn:Send(string.format("%.0f,%.0f", virtual_x, virtual_y))
    end
end)

3. Optional: sensitivity config

lua
local cfg = UI.Schema({
    sensitivity = UI.Slider(0.3, { min = 0.1, max = 1.0, step = 0.1 }),
    toggle = UI.Keybind("G", { label = "Toggle Key" }),
})

function OnMove(dx, dy)
    if virtual_mouse_enabled then
        virtual_x = math.max(0, math.min(522, virtual_x + dx * cfg.sensitivity))
        virtual_y = math.max(0, math.min(522, virtual_y + dy * cfg.sensitivity))
        return false
    end
    return true
end

Your script is ready to use. Pair it with a Python/Node WebSocket server that receives the position updates and renders the crosshair, and you're done.

Want me to write the Python server half?