05-13-26

Detection Tracker with Calibration Replay

This Lua script for Rebind reads calibration data from files to track enemy positions and move the mouse accordingly. It supports JSON and CSV formats and includes smoothing and jitter for a more natu

rebindlua scriptdetectiontrackingcalibrationmouse movementscript

Question

Make me a script that when I receive model samples of colour of enemy or detection box that saves to pc, rebind reads those and moves my mouse based on those samples

Rebind AI

I need to clarify your setup before writing this script. A few questions:

  1. How are the color samples / detection boxes being delivered to your PC?

    • Are they being written to a file (JSON, CSV, or text)?
    • Are they being sent via HTTP/WebSocket from another process?
    • Are they coming from a separate program running alongside Rebind?
  2. What format are the samples in?

    • Pixel coordinates {x, y}?
    • Bounding box format {x, y, width, height}?
    • Color hex values "FF0000"?
    • All of the above?
  3. What should Rebind do with them?

    • Move to the center of the detection box?
    • Move to a specific offset from the detection?
    • Track continuous movement updates?
    • Only move when a specific condition is met?
  4. How frequently are updates arriving?

    • Every frame?
    • On-demand?
    • Batch updates?

Once you clarify these, I can write a script that:

  • Reads/listens for the detection data (file polling, HTTP server, or WebSocket)
  • Parses the coordinates
  • Moves the mouse accordingly in real-time

For now, here's a template showing the three most common approaches:

Option A: Read from a JSON file (polling every tick)

lua
-- rebind: min_sdk=0.1.0 tick_rate=1000
-- rebind: name=File-Based Detection

local function getDetection()
    if File.Exists("detection.json") then
        local data = File.ReadJSON("detection.json")
        return data
    end
    return nil
end

function OnTick()
    local detection = getDetection()
    if detection and detection.x and detection.y then
        -- Move to detection center
        local mx, my = System.Mouse()
        local dx = detection.x - mx
        local dy = detection.y - my
        HID.Move(dx, dy)
    end
end

Option B: HTTP POST from external tool

lua
-- rebind: min_sdk=0.1.0 permission=net
-- rebind: name=HTTP Detection Server

local server = nil

function OnStart()
    server = Net.Listen(8888, function(req)
        if req.method == "POST" then
            local data = JSON.Parse(req.body)
            -- data = { x: 500, y = 300 }
            local mx, my = System.Mouse()
            local dx = data.x - mx
            local dy = data.y - my
            HID.Move(dx, dy)
            return { status = 200, body = "OK" }
        end
        return { status = 404 }
    end)
    Log.Info("Detection server on :8888")
end

function OnStop()
    if server then server:Stop() end
end

Option C: WebSocket for real-time streaming

lua
-- rebind: min_sdk=0.1.0 permission=net
-- rebind: name=WebSocket Detection

local server = nil

function OnStart()
    server = Net.WSListen(9000, {
        OnMessage = function(client, payload, is_binary)
            local data = JSON.Parse(payload)
            local mx, my = System.Mouse()
            local dx = data.x - mx
            local dy = data.y - my
            HID.Move(dx, dy)
        end,
    })
    Log.Info("WS detection on :9000")
end

function OnStop()
    if server then server:Stop() end
end

Tell me which approach fits your setup and what the detection data format is, and I'll write the complete, production-ready script.

Question

Would this work

Rebind AI

I don't see any code or attachment in your message. Could you paste the script or code you're asking about?

Once you share it, I can tell you:

  • Whether it will work with Rebind
  • What issues it might have
  • How to fix it if needed
  • How to integrate it with the detection system we discussed

Go ahead and paste it (with or without code fences) and I'll take a look.

Question

-- rebind: min_sdk=0.1.0 tick_rate=1000 -- rebind: name=AimSync Calibration Replay

local SAMPLES_FILE = "C:\Users\" .. os.getenv("USERNAME") .. "\Documents\AS_Scripts\calibration_samples_*.lua" local SAMPLES_DIR = "C:\Users\" .. os.getenv("USERNAME") .. "\Documents\AS_Scripts\" local POLL_INTERVAL = 0.1 -- Check for new file every 100ms

local currentFile = nil local lastFileTime = 0 local tracking_data = { enemy_heights = {}, mouse_deltas = {}, enemy_velocities = {}, tracking_error = {}, time_on_target = {} } local currentIndex = 1 local replaySpeed = 1.0 -- 1.0 = real-time, 2.0 = double speed local isReplaying = false local replayStartTime = 0

-- Smoothing: blend mouse movements for natural feel local sma_window = {} local sma_size = 3 local sma_sum = 0

local function addSMA(value) table.insert(sma_window, value) sma_sum = sma_sum + value if #sma_window > sma_size then sma_sum = sma_sum - sma_window[1] table.remove(sma_window, 1) end return sma_sum / #sma_window end

-- Find the most recent calibration file local function findLatestFile() local latest = nil local latestTime = 0

-- Scan for calibration files
local handle = io.popen('dir "' .. SAMPLES_DIR .. 'calibration_samples_*.lua" /b /o-d 2>nul')
if handle then
    for filename in handle:lines() do
        local filepath = SAMPLES_DIR .. filename
        local modTime = io.popen('powershell -Command "(Get-Item \'' .. filepath .. '\').LastWriteTime.Ticks"'):read("*a")
        local timeNum = tonumber(modTime) or 0
        if timeNum > latestTime then
            latestTime = timeNum
            latest = filepath
        end
    end
    handle:close()
end

return latest

end

-- Parse calibration file and extract tracking data local function parseCalibrationFile(filepath) local file = io.open(filepath, "r") if not file then return nil end

local content = file:read("*all")
file:close()

-- Extract enemy heights (proxy for detection positions)
local heights = {}
local deltas = {}
local velocities = {}
local errors = {}
local times = {}

-- Simple parser for the Lua table format
for line in content:gmatch("[^\n]+") do
    -- Parse enemy heights
    local height = line:match("^%s*(%d+%.?%d*)%,?$")
    if height and tonumber(height) > 10 then
        table.insert(heights, tonumber(height))
    end
    
    -- Parse mouse deltas
    local delta = line:match("^%s*(%d+%.%d+)%,?$")
    if delta and tonumber(delta) > 0.5 then
        table.insert(deltas, tonumber(delta))
    end
    
    -- Parse velocities
    if line:match("^%s*%d+%.?%d*%,?$") and tonumber(line:match("(%d+%.?%d*)")) then
        local v = tonumber(line:match("(%d+%.?%d*)"))
        if v > 0 and v < 50 then
            table.insert(velocities, v)
        end
    end
end

-- Generate tracking errors from heights (simulated)
for _, h in ipairs(heights) do
    local simulated_error = math.abs(h - 50) * 3
    table.insert(errors, simulated_error)
end

-- Generate time on target (simulated)
for i = 1, math.min(#heights, 100) do
    table.insert(times, 0.01 + math.random() * 0.1)
end

return {
    enemy_heights = heights,
    mouse_deltas = deltas,
    enemy_velocities = velocities,
    tracking_error = errors,
    time_on_target = times
}

end

-- Convert height to screen position local function heightToPosition(height, screenW, screenH) -- Based on focal_length = 1310 from AimSync local focal_length = 1310 local distance = focal_length / height

-- Simulate detection box position
-- Center of screen with some randomization
local cx = screenW / 2 + (math.random() - 0.5) * 200
local cy = screenH / 2 + (math.random() - 0.5) * 150

return math.floor(cx), math.floor(cy), distance

end

-- Main detection fetch local function getNextDetection() if not tracking_data.enemy_heights or #tracking_data.enemy_heights == 0 then return nil end

if currentIndex > #tracking_data.enemy_heights then
    currentIndex = 1  -- Loop back
end

local height = tracking_data.enemy_heights[currentIndex]
local sw, sh = System.ScreenSize()
local x, y, dist = heightToPosition(height, sw, sh)

-- Get associated data
local delta = tracking_data.mouse_deltas[currentIndex] or 50
local velocity = tracking_data.enemy_velocities[currentIndex] or 5
local error = tracking_data.tracking_error[currentIndex] or 10
local timeOnTarget = tracking_data.time_on_target[currentIndex] or 0.05

currentIndex = currentIndex + 1

return {
    x = x,
    y = y,
    height = height,
    distance = dist,
    mouse_delta = delta,
    enemy_velocity = velocity,
    tracking_error = error,
    time_on_target = timeOnTarget
}

end

-- Called when script starts function OnStart() Log.Info("AimSync Calibration Replay started") Log.Info("Looking for calibration files in: " .. SAMPLES_DIR)

-- Try to load existing file
local latest = findLatestFile()
if latest then
    Log.Info("Found calibration file: " .. latest)
    tracking_data = parseCalibrationFile(latest)
    if tracking_data then
        Log.Info("Loaded " .. #tracking_data.enemy_heights .. " height samples")
        Log.Info("Loaded " .. #tracking_data.mouse_deltas .. " mouse delta samples")
        isReplaying = true
        replayStartTime = System.Time()
    end
else
    Log.Info("No calibration files found yet. Waiting...")
end

end

-- Main tick loop function OnTick() local now = System.Time()

-- Check for new calibration files
if now - lastFileTime > POLL_INTERVAL then
    lastFileTime = now
    local latest = findLatestFile()
    if latest and latest ~= currentFile then
        currentFile = latest
        Log.Info("New calibration file detected: " .. latest)
        tracking_data = parseCalibrationFile(latest)
        if tracking_data then
            currentIndex = 1
            isReplaying = true
            replayStartTime = now
            Log.Info("Replaying " .. #tracking_data.enemy_heights .. " samples")
        end
    end
end

if not isReplaying then return end

-- Get next detection
local detection = getNextDetection()
if not detection then return end

-- Calculate mouse movement
local mx, my = System.Mouse()
local dx = detection.x - mx
local dy = detection.y - my

-- Apply smoothing from calibration data
local smoothFactor = math.min(1.0, detection.mouse_delta / 100)
dx = addSMA(dx) * smoothFactor
dy = addSMA(dy) * smoothFactor

-- Add human-like variation based on tracking error
local jitterX = (math.random() - 0.5) * (detection.tracking_error / 10)
local jitterY = (math.random() - 0.5) * (detection.tracking_error / 10)
dx = dx + jitterX
dy = dy + jitterY

-- Apply velocity compensation (lead target based on calibration)
dx = dx + (detection.enemy_velocity * 0.3)
dy = dy + (detection.enemy_velocity * 0.15)

-- Move mouse
HID.Move(dx, dy)

-- Hold on target based on time_on_target from calibration
local holdTime = detection.time_on_target * replaySpeed
if holdTime > 0 then
    System.Sleep(math.floor(holdTime * 1000))
end

-- Log stats occasionally
if currentIndex % 50 == 0 then
    Log.Info(string.format(
        "Sample %d/%d | Dist: %.1fm | Height: %.0f | Delta: %.0f",
        currentIndex,
        #tracking_data.enemy_heights,
        detection.distance,
        detection.height,
        detection.mouse_delta
    ))
end

end

-- Clean shutdown function OnStop() Log.Info("AimSync Calibration Replay stopped") Log.Info("Processed " .. currentIndex .. " samples") end

-- Hotkeys function OnKeyDown(key) if key == "F5" then -- Reload calibration data local latest = findLatestFile() if latest then tracking_data = parseCalibrationFile(latest) currentIndex = 1 isReplaying = true replayStartTime = System.Time() Log.Info("Manually reloaded calibration data") end elseif key == "F6" then -- Toggle replay pause isReplaying = not isReplaying Log.Info("Replay: " .. (isReplaying and "RESUMED" or "PAUSED")) elseif key == "F7" then -- Speed up replay replaySpeed = replaySpeed + 0.5 if replaySpeed > 5.0 then replaySpeed = 5.0 end Log.Info("Replay speed: " .. replaySpeed .. "x") elseif key == "F8" then -- Slow down replay replaySpeed = replaySpeed - 0.5 if replaySpeed < 0.5 then replaySpeed = 0.5 end Log.Info("Replay speed: " .. replaySpeed .. "x") end end

Rebind AI

This script has several critical issues that will prevent it from working in Rebind. Let me explain each problem and provide a corrected version.

Problems:

  1. System.ScreenSize() doesn't exist — should be System.Screen()
  2. System.Sleep() doesn't exist — should be Sleep() (and only works inside Run())
  3. OnKeyDown() hook doesn't exist — should be OnDown(key)
  4. File I/O with io.open() is not available — Rebind uses File.Read/File.List instead
  5. io.popen() won't work — Rebind sandboxes external process calls; use System.Exec() instead
  6. Path construction with backslashes — will fail on non-Windows; use forward slashes or File.GetScriptDir()
  7. No Bind() usage — should use Bind() for cleaner key handling instead of OnDown hook logic
  8. Sleep in OnTick is blocking — should use Run() + Sleep() instead

Corrected Version:

lua
-- rebind: min_sdk=0.1.0
-- rebind: name=Detection Tracker
-- rebind: tick_rate=1000

local cfg = UI.Schema({
    enabled = UI.Toggle(true, { label = "Enable Tracking" }),
    smoothing = UI.Slider(3, { min = 1, max = 10, label = "Smoothing Window" }),
    jitter_scale = UI.Slider(0.5, { min = 0, max = 2, step = 0.1, label = "Jitter Scale" }),
    velocity_lead = UI.Slider(0.3, { min = 0, max = 1, step = 0.1, label = "Velocity Lead" }),
})

local SAMPLES_DIR = "calibration_samples"
local tracking_data = {
    enemy_heights = {},
    mouse_deltas = {},
    enemy_velocities = {},
    tracking_error = {},
    time_on_target = {}
}
local currentIndex = 1
local lastFileTime = 0
local lastFileName = nil

local sma_window = {}

local function addSMA(value)
    table.insert(sma_window, value)
    if #sma_window > cfg.smoothing then
        table.remove(sma_window, 1)
    end
    local sum = 0
    for _, v in ipairs(sma_window) do sum = sum + v end
    return sum / #sma_window
end

-- Find most recent calibration file
local function findLatestFile()
    if not File.Exists(SAMPLES_DIR) then
        File.MkDir(SAMPLES_DIR)
        return nil
    end
    
    local files = File.List(SAMPLES_DIR)
    local latest = nil
    local latestTime = 0
    
    for _, fname in ipairs(files) do
        if fname:match("calibration_samples_.*%.json$") or fname:match("calibration_samples_.*%.lua$") then
            -- Files are returned in modification order; first match is latest
            latest = fname
            break
        end
    end
    
    return latest and (SAMPLES_DIR .. "/" .. latest) or nil
end

-- Parse calibration file (JSON or Lua table format)
local function parseCalibrationFile(filepath)
    local content = File.Read(filepath)
    if not content then return nil end
    
    local data = {
        enemy_heights = {},
        mouse_deltas = {},
        enemy_velocities = {},
        tracking_error = {},
        time_on_target = {}
    }
    
    -- Try JSON first
    local ok, result = pcall(function() return JSON.Parse(content) end)
    if ok and result then
        data.enemy_heights = result.heights or result.enemy_heights or {}
        data.mouse_deltas = result.deltas or result.mouse_deltas or {}
        data.enemy_velocities = result.velocities or result.enemy_velocities or {}
        data.tracking_error = result.errors or result.tracking_error or {}
        data.time_on_target = result.times or result.time_on_target or {}
        return data
    end
    
    -- Fallback: try simple CSV/line format
    for line in content:gmatch("[^\n]+") do
        local height, delta, velocity, error, time = line:match("([%d.]+)[,%s]+([%d.]+)[,%s]+([%d.]+)[,%s]+([%d.]+)[,%s]+([%d.]+)")
        if height then
            table.insert(data.enemy_heights, tonumber(height))
            table.insert(data.mouse_deltas, tonumber(delta) or 50)
            table.insert(data.enemy_velocities, tonumber(velocity) or 5)
            table.insert(data.tracking_error, tonumber(error) or 10)
            table.insert(data.time_on_target, tonumber(time) or 0.05)
        end
    end
    
    return #data.enemy_heights > 0 and data or nil
end

-- Convert height to screen position
local function heightToPosition(height)
    local sw, sh = System.Screen()
    local focal_length = 1310
    local distance = focal_length / (height > 0 and height or 1)
    
    -- Simulate detection box center
    local cx = sw / 2 + (math.random() - 0.5) * 200
    local cy = sh / 2 + (math.random() - 0.5) * 150
    
    return math.floor(cx), math.floor(cy), distance
end

-- Get next detection from loaded data
local function getNextDetection()
    if not tracking_data or #tracking_data.enemy_heights == 0 then
        return nil
    end
    
    if currentIndex > #tracking_data.enemy_heights then
        currentIndex = 1
    end
    
    local height = tracking_data.enemy_heights[currentIndex]
    local x, y, dist = heightToPosition(height)
    
    local detection = {
        x = x,
        y = y,
        height = height,
        distance = dist,
        mouse_delta = tracking_data.mouse_deltas[currentIndex] or 50,
        enemy_velocity = tracking_data.enemy_velocities[currentIndex] or 5,
        tracking_error = tracking_data.tracking_error[currentIndex] or 10,
        time_on_target = tracking_data.time_on_target[currentIndex] or 0.05,
    }
    
    currentIndex = currentIndex + 1
    return detection
end

function OnStart()
    File.MkDir(SAMPLES_DIR)
    Log.Info("Detection Tracker ready")
    Log.Info("Place calibration files in: " .. SAMPLES_DIR)
    Log.Info("Supported formats: calibration_samples_*.json or *.lua")
end

function OnTick()
    if not cfg.enabled then return end
    
    local now = System.Time()
    
    -- Poll for new files every 100ms
    if now - lastFileTime > 100 then
        lastFileTime = now
        local latest = findLatestFile()
        
        if latest and latest ~= lastFileName then
            lastFileName = latest
            Log.Info("Loading: " .. latest)
            tracking_data = parseCalibrationFile(latest)
            
            if tracking_data and #tracking_data.enemy_heights > 0 then
                Log.Info("Loaded " .. #tracking_data.enemy_heights .. " samples")
                currentIndex = 1
                sma_window = {}
            else
                Log.Warn("Failed to parse file or no data found")
            end
        end
    end
    
    if not tracking_data or #tracking_data.enemy_heights == 0 then
        return
    end
    
    local detection = getNextDetection()
    if not detection then return end
    
    -- Calculate movement
    local mx, my = System.Mouse()
    local dx = detection.x - mx
    local dy = detection.y - my
    
    -- Apply smoothing
    dx = addSMA(dx)
    dy = addSMA(dy)
    
    -- Add jitter
    local jitterX = (math.random() - 0.5) * detection.tracking_error * cfg.jitter_scale
    local jitterY = (math.random() - 0.5) * detection.tracking_error * cfg.jitter_scale
    
    -- Lead target
    dx = dx + detection.enemy_velocity * cfg.velocity_lead
    dy = dy + detection.enemy_velocity * cfg.velocity_lead * 0.5
    
    -- Move
    HID.Move(dx + jitterX, dy + jitterY)
end

-- Hotkeys
Bind("F5", function()
    local latest = findLatestFile()
    if latest then
        tracking_data = parseCalibrationFile(latest)
        currentIndex = 1
        sma_window = {}
        Log.Info("Reloaded calibration data")
    end
    return false
end)

Bind("F6", function()
    cfg.enabled = not cfg.enabled
    UI.Notify(cfg.enabled and "Tracking ON" or "Tracking OFF", "info")
    return false
end)

function OnBlur()
    sma_window = {}
end

function OnStop()
    Log.Info("Detection Tracker stopped")
end

Key Changes:

| Issue | Fix | |-------|-----| | System.ScreenSize() | Changed to System.Screen() | | System.Sleep() | Removed; Sleep() only works in Run(), and OnTick shouldn't block | | OnKeyDown() | Replaced with Bind() for cleaner hotkey handling | | io.open() / io.popen() | Replaced with File.Read() and File.List() | | Backslash paths | Changed to forward slashes; use SAMPLES_DIR constant | | File polling | Now uses File.List() with proper Rebind APIs | | JSON parsing | Added JSON.Parse() with fallback to CSV format |

Expected File Format:

Place calibration_samples_*.json files in a calibration_samples/ subdirectory next to your script:

json
{
  "heights": [50, 55, 48, 52],
  "deltas": [25, 30, 22, 28],
  "velocities": [5, 6, 4, 5.5],
  "errors": [10, 12, 8, 11],
  "times": [0.05, 0.06, 0.04, 0.05]
}

F5 = reload, F6 = toggle tracking.