Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Productivity recipes

Your AutoHotkey or Karabiner logic, on any USB keyboard and mouse. The Rebind engine captures your input and runs your scripts; add the Rebind Link dongle for hardware output. Core input remapping runs on Windows, macOS, and Linux; some recipes below also use Clipboard and per-app window targeting, which are Windows/macOS only (noted per recipe).

Each recipe is a complete script. Copy it into a new script in the Rebind app and adjust the config.

Text expansion

Type a short abbreviation, then press a trigger key to expand it into full text. HID.Type sends each character through the hardware; for long or multi-line blocks, paste through the clipboard instead so it lands instantly and intact.

--[[
  rebind: min_sdk=3.0.0
  rebind: name=Text Expander
--]]
local cfg = UI.Schema({
  trigger = UI.Keybind("F8", { label = "Expand Key" }),
  email = UI.Text("user@example.com", { label = "Email", maxLength = 60 }),
  address = UI.Text(
    "123 Example St\nPortland, OR 97201",
    { label = "Address", maxLength = 200 }
  ),
})

local snippets = {}

function OnStart()
  snippets = {
    ["@@"] = cfg.email,
    [";addr"] = cfg.address,
    ["/date"] = os.date("%Y-%m-%d"),
    ["/time"] = os.date("%H:%M"),
  }
end

local buffer = ""

function OnDown(key)
  if key == cfg.trigger then
    for abbr, expansion in pairs(snippets) do
      if buffer:sub(-#abbr) == abbr then
        Run(function()
          for i = 1, #abbr do
            HID.Press("Backspace")
            Sleep(20)
          end
          -- multi-line text pastes intact; short text can use HID.Type
          if expansion:find("\n") then
            Clipboard.Set(expansion)
            HID.Press("LCtrl+V")
          else
            HID.Type(expansion, 15)
          end
        end)
        buffer = ""
        return false
      end
    end
    return false
  end

  -- track typed characters so we can match abbreviations
  if #key == 1 then
    buffer = buffer .. key
    if #buffer > 20 then
      buffer = buffer:sub(-20)
    end
  elseif key == "Backspace" and #buffer > 0 then
    buffer = buffer:sub(1, -2)
  elseif key == "Space" or key == "Enter" then
    buffer = ""
  end

  return true
end

Tuning. Add more entries to the snippets table. Raise the HID.Type char delay (the 15) if an app drops characters; lower it for faster output. Multi-line blocks route through Clipboard.Set automatically.

Per-app shortcuts

A modeline scopes a script to one application by window title (window=) or executable (process=). The same physical key means one thing in your editor and nothing elsewhere — no profile switching, zero overhead when the app isn’t focused.

--[[
  rebind: min_sdk=3.0.0
  rebind: name=Editor Shortcuts
  rebind: process=Code.exe
--]]

function OnDown(key)
  -- F1 opens the command palette, only inside the editor
  if key == "F1" then
    Run(function()
      HID.Press("LCtrl+LShift+P")
    end)
    return false
  end
  -- Mouse4 = undo, Mouse5 = redo
  if key == "Mouse4" then
    Run(function()
      HID.Press("LCtrl+Z")
    end)
    return false
  end
  if key == "Mouse5" then
    Run(function()
      HID.Press("LCtrl+LShift+Z")
    end)
    return false
  end
  return true
end

Tuning. Add multiple modelines to cover several apps; anything outside the match passes through untouched. (Windows/macOS only — see Platforms.) Full modeline semantics are in the SDK reference.

Clipboard transform

Read the clipboard, transform it in Luau, and paste the result. This example trims and lowercases the selection, but any string operation works — strip formatting, wrap in quotes, convert case, run a regex.

--[[
  rebind: min_sdk=3.0.0
  rebind: name=Clipboard Transform
--]]
local cfg = UI.Schema({
  hotkey = UI.Keybind("F9", { label = "Transform + Paste" }),
})

Bind(
  cfg.hotkey,
  Async(function()
    -- copy the current selection first
    HID.Press("LCtrl+C")
    Sleep(50)

    local text = Clipboard.Get()
    if not text or text == "" then
      UI.Notify("Clipboard empty", "warning")
      return
    end

    -- transform: trim whitespace and lowercase
    local cleaned = text:gsub("^%s+", ""):gsub("%s+$", ""):lower()

    Clipboard.Set(cleaned)
    HID.Press("LCtrl+V")
  end)
)

Tuning. Replace the cleaned line with your own transform. The Sleep(50) gives the foreground app time to populate the clipboard after the copy; raise it if the read comes back stale. Clipboard access is available on Windows and macOS.

Multi-step macro

Replay a fixed sequence of keystrokes on demand. Run plus Sleep controls each step and its timing; task:Cancel() aborts a long sequence mid-flight.

--[[
  rebind: min_sdk=3.0.0
  rebind: name=Macro Sequence
--]]
local cfg = UI.Schema({
  play_key = UI.Keybind("F9", { label = "Play" }),
  stop_key = UI.Keybind("F10", { label = "Stop" }),
})

local task = nil

function OnDown(key)
  if key == cfg.play_key then
    task = Run(function()
      HID.Press("LCtrl+A") -- select all
      Sleep(100)
      HID.Press("LCtrl+C") -- copy
      Sleep(100)
      HID.Press("End") -- jump to end
      HID.Press("Enter")
      HID.Press("LCtrl+V") -- paste below
      UI.Notify("Done", "success")
    end)
    return false
  end

  if key == cfg.stop_key and task and task:IsRunning() then
    task:Cancel()
    UI.Notify("Cancelled", "info")
    return false
  end

  return true
end

function OnStop()
  if task and task:IsRunning() then
    task:Cancel()
  end
end

Tuning. Edit the body of the Run block to define your sequence. Each Sleep is in milliseconds — tighten the delays for speed, widen them if a step fires before the previous one lands. To capture a sequence live instead of hand-writing it, see the macro recorder below.

Record and replay a macro

Capture a live sequence of keystrokes and mouse moves, then play it back with exact timing. Macro.Record starts capturing, Macro.Finish returns the recorded macro, and Macro.Play replays it — at any speed.

--[[
  rebind: min_sdk=3.0.0
  rebind: name=Macro Recorder
--]]
local cfg = UI.Schema({
  record_key = UI.Keybind("F9", { label = "Record / Stop" }),
  play_key = UI.Keybind("F10", { label = "Play" }),
  speed = UI.Slider(100, { min = 25, max = 400, suffix = "%" }),
})

local recording = false
local macro = nil

function OnDown(key)
  if key == cfg.record_key then
    if recording then
      macro = Macro.Finish()
      recording = false
      UI.Notify("Recorded " .. #macro .. " actions", "success")
    else
      Macro.Record({ ignore_mouse = false })
      recording = true
      UI.Notify("Recording — press again to stop", "info")
    end
    return false
  end

  if key == cfg.play_key and macro then
    Macro.Play(macro, cfg.speed / 100)
    return false
  end

  return true
end

Tuning. speed scales playback — 100 is the recorded speed, 200 plays it twice as fast. Pass { ignore_mouse = true } to Macro.Record to record keystrokes only. For frame-accurate, device-side replay use Macro.Stream(macro) instead of Macro.Play. The recorded action format and the macro transforms (Math.Scale, Math.Resample) are in the SDK reference.

Scroll to zoom

Hold a modifier and scroll to zoom in browsers, editors, and image viewers — the standard Ctrl + scroll gesture, bound to any key you prefer.

--[[
  rebind: min_sdk=3.0.0
  rebind: name=Scroll Zoom
--]]
local cfg = UI.Schema({
  modifier = UI.Keybind("RAlt", { label = "Modifier Key" }),
})

function OnScroll(delta)
  if Input.IsDown(cfg.modifier) then
    HID.Down("LCtrl")
    HID.Scroll(delta)
    HID.Up("LCtrl")
    return false
  end
  return true
end

Tuning. Change modifier to any key. To slow the zoom, scale delta before passing it to HID.Scroll (for example, HID.Scroll(delta // 2)). Returning false swallows the original scroll so only the zoom fires.

Set your mouse output rate

A live demonstration of the 8,000 Hz loop: capture every raw mouse movement and re-emit it at exactly the rate you choose — effectively setting your mouse’s output polling rate. mouse_block=true hands OnMove control of every event (this requires a Rebind device), and an OnTick running at 8 kHz decides when to flush the accumulated motion.

--[[
  rebind: min_sdk=3.0.0
  rebind: name=Mouse Rate Limiter
  rebind: mouse_block=true
  rebind: tick_rate=8000
--]]

local cfg = UI.Schema({
  rate = UI.Slider(
    125,
    { min = 60, max = 1000, suffix = "Hz", label = "Output Rate" }
  ),
})

local accX, accY = 0, 0
local startTime = nil
local lastEmitTime = nil

function OnMove(dx, dy)
  accX = accX + dx
  accY = accY + dy
  return false
end

function OnTick()
  local now = System.Time()

  if not startTime then
    startTime = now
    lastEmitTime = now
    return
  end

  local interval = 1000 / cfg.rate
  local elapsed = now - startTime
  local expectedEmits = math.floor(elapsed / interval)
  local actualEmits = math.floor((lastEmitTime - startTime) / interval)

  if expectedEmits > actualEmits then
    lastEmitTime = now
    if accX ~= 0 or accY ~= 0 then
      HID.Move(accX, accY)
      accX, accY = 0, 0
    end
  end
end

Tuning. rate is the output rate in Hz — lower it to throttle a high-polling-rate mouse, raise it toward the device’s own rate. No motion is lost: OnMove accumulates the raw deltas and OnTick flushes them on schedule, so the cursor’s path is preserved and only its timing changes. This needs mouse_block=true (so OnMove can hold each event, hardware mode only) and a high tick_rate for an accurate output clock — see the scripting guide.

Mouse acceleration

Add a speed-dependent acceleration curve to any mouse: slow movements stay close to 1:1 for precision, fast flicks get multiplied so you cross the screen with less travel. mouse_block=true hands OnMove every raw event (requires a Rebind device), so the script rescales each delta and re-emits it with HID.Move — curve, threshold, and ceiling are all live in the config panel.

--[[
  rebind: min_sdk=3.0.0
  rebind: name=Mouse Acceleration
  rebind: mouse_block=true
--]]

local cfg = UI.Schema({
  enabled = UI.Toggle(true, { label = "Enable Acceleration" }),
  toggle_key = UI.Keybind("F7", { label = "Toggle Key" }),
  curve = UI.Select(
    "Quadratic",
    { "Linear", "Quadratic", "Cubic", "Exponential" },
    { label = "Curve Type" }
  ),
  multiplier = UI.Slider(
    2.0,
    { min = 1.0, max = 5.0, step = 0.1, suffix = "x", label = "Max Multiplier" }
  ),
  threshold = UI.Slider(
    5,
    { min = 1, max = 30, suffix = "px", label = "Accel Threshold" }
  ),
  sensitivity = UI.Slider(
    100,
    { min = 25, max = 200, suffix = "%", label = "Base Sensitivity" }
  ),
})

local function getSpeed(dx, dy)
  return math.sqrt(dx * dx + dy * dy)
end

local function applyAccel(speed)
  local base = cfg.sensitivity / 100
  local threshold = cfg.threshold

  if speed <= threshold then
    return base
  end

  local excess = speed - threshold
  local maxMult = cfg.multiplier
  local factor

  if cfg.curve == "Linear" then
    factor = 1 + (excess / 20) * (maxMult - 1)
  elseif cfg.curve == "Quadratic" then
    factor = 1 + (excess / 20) ^ 2 * (maxMult - 1)
  elseif cfg.curve == "Cubic" then
    factor = 1 + (excess / 20) ^ 3 * (maxMult - 1)
  elseif cfg.curve == "Exponential" then
    factor = 1 + (math.exp(excess / 20) - 1) * (maxMult - 1) / (math.exp(1) - 1)
  else
    factor = 1
  end

  factor = math.min(factor, maxMult)
  return base * factor
end

function OnDown(key)
  if key == cfg.toggle_key then
    cfg.enabled = not cfg.enabled
    UI.Notify(cfg.enabled and "Acceleration ON" or "Acceleration OFF", "info")
    return false
  end
  return true
end

function OnMove(dx, dy)
  if not cfg.enabled then
    return true
  end

  local speed = getSpeed(dx, dy)
  if speed == 0 then
    return false
  end

  local factor = applyAccel(speed)
  HID.Move(dx * factor, dy * factor)
  return false
end

Tuning. curve controls how aggressively the multiplier ramps once a movement passes threshold — Linear is gentle, Exponential is sharp. multiplier caps the fastest flicks; sensitivity scales everything, slow moves included. Anything under threshold px stays at base sensitivity for pixel-precise aiming, and the bound key toggles the whole effect on the fly. Like the rate limiter, this needs mouse_block=true so OnMove can swallow the raw event and emit the accelerated one — hardware mode only (see the scripting guide).