marketplace/accessibility

Steady Hand

v0.1.0

@rebindfree

Filters small involuntary movements and smooths the rest, so the pointer follows your intent. Requires Rebind Link.

Needs Rebind Link or Rebind BYOH.

Gallery

Steady Hand preview

Readme

Movement smaller than a threshold is held back, and the rest is let out smoothly, so tremor does not turn into a shaking pointer. Back-and-forth tremor cancels out before it reaches the pointer, while slow, deliberate movement adds up and gets through.

Needs Rebind 3.5.0 or newer and Rebind Link on Windows or macOS, with the mouse forwarded through hardware mode. Run only one mouse-movement script at a time.

Settings

  • Smoothing: 0 to 90 percent. Higher is calmer, with a little more lag. 0 sends held movement at once.
  • Ignore movement smaller than: 1 to 20 mouse counts. Movement has to add up to this much before the pointer moves. Your mouse's sensitivity (DPI) changes how much hand movement that is.

Changes apply right away.

Limits

  • The pointer moves only as far as your hand did; nothing is dropped once a move gets through, so a small amount of tremor rides along with real movement.
  • Linux and software mode are unsupported. Live hardware use remains unverified.

Stop

Stop the package from the editor toolbar, or press Left Ctrl + Left Alt + K to stop all scripts. Stopping discards movement still held back.

Reviews

No reviews yet. Be the first, from the app.

Source

Version 0.1.0 (current)

main.luau

-- Steady Hand: hold movement back until it adds up to a real move, then let
-- it out smoothly. Tremor that goes back and forth cancels before it ever
-- reaches the pointer; slow, deliberate movement adds up and gets through.

local cfg = UI.Schema({
  smoothing = UI.Slider(50, {
    min = 0,
    max = 90,
    suffix = "%",
    label = "Smoothing",
    tooltip = "Higher is steadier, with a little more lag",
  }),
  threshold = UI.Slider(5, {
    min = 1,
    max = 20,
    suffix = " counts",
    label = "Ignore movement smaller than",
  }),
})

-- movement received but not yet sent, and whether it is flowing out
local px, py = 0, 0
local flowing = false

local function reset()
  px, py, flowing = 0, 0, false
end

-- the share of the pending movement sent this tick, at least one count
local function step(pending, share)
  if pending == 0 then
    return 0
  end
  local out = math.round(pending * share)
  if out == 0 then
    out = if pending > 0 then 1 else -1
  end
  return out
end

function OnMove(dx, dy)
  px += dx
  py += dy
  return false
end

function OnTick()
  if not flowing then
    if math.sqrt(px * px + py * py) < cfg.threshold then
      return
    end
    flowing = true
  end
  local share = 1 - cfg.smoothing / 100
  local ox, oy = step(px, share), step(py, share)
  px -= ox
  py -= oy
  if ox ~= 0 or oy ~= 0 then
    HID.Move(ox, oy)
  end
  if px == 0 and py == 0 then
    flowing = false
  end
end

function OnStop()
  reset()
end

function OnBlur()
  reset()
end