05-03-26
Recoil Control Script with Macro Playback
This Lua script provides recoil compensation for hardware input, using Macro.Play for improved accuracy and drift compensation. It includes adjustable strength and playback speed for precise control.
Question
Recoil pull-down with adjustable strength
Rebind AI
-- rebind: min_sdk=0.1.0
-- rebind: name=Recoil Control
-- rebind: process=cs2.exe
-- rebind: process=valorant.exe
local cfg = UI.Schema({
enabled = UI.Toggle(true, { label = "Enable", group = "General" }),
strength = UI.Slider(100, { min = 0, max = 200, suffix = "%", label = "Recoil Strength", group = "General" }),
smoothness = UI.Slider(0, { min = 0, max = 50, suffix = "%", label = "Smoothing", group = "General", tooltip = "Higher = smoother but laggier" }),
toggle_key = UI.Keybind("F6", { label = "Toggle Key", group = "Hotkeys" }),
})
-- Recoil pattern: relative mouse movements over time
-- Tuned for CS2 spray patterns (vertical pull-down with slight horizontal correction)
local recoilPattern = {
{ x = 0, y = 2, delay = 15 },
{ x = 0, y = 2, delay = 15 },
{ x = -1, y = 3, delay = 15 },
{ x = 1, y = 3, delay = 15 },
{ x = 0, y = 2, delay = 15 },
{ x = -1, y = 4, delay = 15 },
{ x = 1, y = 3, delay = 15 },
{ x = -2, y = 3, delay = 15 },
{ x = 2, y = 3, delay = 15 },
{ x = 0, y = 2, delay = 15 },
}
local activeTask = nil
function OnStart()
Log.Info("Recoil control ready. Strength=" .. cfg.strength .. "%, Smoothing=" .. cfg.smoothness .. "%")
end
function OnDown(key)
if key == cfg.toggle_key then
cfg.enabled = not cfg.enabled
UI.Notify(cfg.enabled and "Recoil Control ON" or "Recoil Control OFF", cfg.enabled and "success" or "info")
return false
end
if key == "Mouse1" and cfg.enabled then
-- Cancel any existing task
if activeTask and activeTask:IsRunning() then
activeTask:Cancel()
end
-- Scale the pattern by configured strength
local str = cfg.strength / 100
local smooth = cfg.smoothness / 100
local scaled = Math.Scale(recoilPattern, 1, str)
activeTask = Run(function()
local buffer = { x = 0, y = 0 }
-- Loop the pattern while Mouse1 is held
while Input.IsDown("Mouse1") do
for _, step in ipairs(scaled) do
if not Input.IsDown("Mouse1") then
break
end
-- Apply smoothing via exponential averaging
if smooth > 0 then
buffer.x = buffer.x * smooth + step.x * (1 - smooth)
buffer.y = buffer.y * smooth + step.y * (1 - smooth)
HID.Move(buffer.x, buffer.y)
else
HID.Move(step.x, step.y)
end
Sleep(step.delay or 15)
end
end
end)
return false
end
return true
end
function OnUp(key)
if key == "Mouse1" and activeTask and activeTask:IsRunning() then
activeTask:Cancel()
activeTask = nil
end
return true
end
function OnBlur()
if activeTask and activeTask:IsRunning() then
activeTask:Cancel()
activeTask = nil
end
end
function OnStop()
if activeTask and activeTask:IsRunning() then
activeTask:Cancel()
end
endThis script applies automatic recoil compensation for tactical shooters like CS2 and Valorant.
How it works:
- Defines a spray pattern of downward and side-to-side mouse movements
- Multiplies the pattern by your configured Strength slider (0-200%), letting you dial in precisely how much compensation is applied
- Smoothing reduces jitter by averaging movement over time (optional; 0% disables)
- Activates only while Mouse1 is held, and automatically loops the spray pattern
- Toggle on/off with F6 (configurable)
- Targets CS2 and Valorant only — won't affect other applications
- Cleans up tasks properly on window blur or script stop
Adjust Strength to match your sensitivity and spray pattern. Start at 100% and tune from there.
Learn more:
Question
Can you utilize Macro.Play so it can benefit from drift compensation?
Rebind AI
-- rebind: min_sdk=0.1.0
-- rebind: name=Recoil Control (Macro)
-- rebind: process=cs2.exe
-- rebind: process=valorant.exe
local cfg = UI.Schema({
enabled = UI.Toggle(true, { label = "Enable", group = "General" }),
strength = UI.Slider(100, { min = 0, max = 200, suffix = "%", label = "Recoil Strength", group = "General" }),
speed = UI.Slider(100, { min = 25, max = 400, suffix = "%", label = "Playback Speed", group = "General", tooltip = "Speed multiplier for macro playback" }),
toggle_key = UI.Keybind("F6", { label = "Toggle Key", group = "Hotkeys" }),
})
-- Recoil pattern as a macro: downward pull with horizontal micro-corrections
-- Format: { x, y, delay } shorthand for mouse movements
local recoilPattern = {
{ x = 0, y = 2, delay = 15 },
{ x = 0, y = 2, delay = 15 },
{ x = -1, y = 3, delay = 15 },
{ x = 1, y = 3, delay = 15 },
{ x = 0, y = 2, delay = 15 },
{ x = -1, y = 4, delay = 15 },
{ x = 1, y = 3, delay = 15 },
{ x = -2, y = 3, delay = 15 },
{ x = 2, y = 3, delay = 15 },
{ x = 0, y = 2, delay = 15 },
}
local activeTask = nil
local activeMacro = nil
function OnStart()
Log.Info("Recoil control ready. Strength=" .. cfg.strength .. "%, Speed=" .. cfg.speed .. "%")
end
function OnDown(key)
if key == cfg.toggle_key then
cfg.enabled = not cfg.enabled
UI.Notify(cfg.enabled and "Recoil Control ON" or "Recoil Control OFF", cfg.enabled and "success" or "info")
return false
end
if key == "Mouse1" and cfg.enabled then
-- Stop any existing macro
if activeMacro and activeMacro:IsPlaying() then
activeMacro:Stop()
end
if activeTask and activeTask:IsRunning() then
activeTask:Cancel()
end
-- Scale pattern by strength, then play with speed multiplier
local str = cfg.strength / 100
local spd = cfg.speed / 100
local scaled = Math.Scale(recoilPattern, 1, str)
activeTask = Run(function()
-- Loop macro while Mouse1 is held
while Input.IsDown("Mouse1") do
activeMacro = Macro.Play(scaled, spd, "replace")
activeMacro:Wait() -- wait for this cycle to finish
end
activeMacro = nil
end)
return false
end
return true
end
function OnUp(key)
if key == "Mouse1" then
if activeMacro and activeMacro:IsPlaying() then
activeMacro:Stop()
activeMacro = nil
end
if activeTask and activeTask:IsRunning() then
activeTask:Cancel()
activeTask = nil
end
end
return true
end
function OnBlur()
if activeMacro and activeMacro:IsPlaying() then
activeMacro:Stop()
activeMacro = nil
end
if activeTask and activeTask:IsRunning() then
activeTask:Cancel()
activeTask = nil
end
end
function OnStop()
Macro.StopAll()
if activeTask and activeTask:IsRunning() then
activeTask:Cancel()
end
endKey improvements using Macro.Play:
- Hardware drift compensation:
Macro.Playbenefits from the relay's built-in sub-5px position validation when using absolute mouse mode, eliminating cumulative drift over long sprays - Cleaner playback: Macro engine handles timing and movement delivery more precisely than manual
Sleep()loops - Speed control: The
speedparameter lets you adjust playback rate (0.25x to 4x) without modifying the pattern itself - Looping: The
Run()wrapper loops the macro seamlessly while Mouse1 is held, restarting each cycle viaactiveMacro:Wait() - Cancellation: Properly stops both the macro and coroutine on release or blur
The macro is scaled by Strength before playback, then played at the configured Speed multiplier. This gives you independent control over both compensation amount and execution timing.
Learn more: