Describe it.Rebind builds it.

Rebind is an input platform for your computer. Tell your AI agent what your keyboard, mouse and controller should do, and it writes the script for you.

❯ in figma only, make caps lock flip between the hand and move tools
● Write(figma-tools/main.luau)
     -- rebind: process=Figma
     local hand = false
     rb.bind("CapsLock", rb.async(function()
       hand = not hand
       rb.hid.press(if hand then "H" else "V")
     end))
● Bash(rebind check figma-tools)
  ⎿  ok  manifest / modeline / require graph (1 module) / compile / permissions
● Done. Caps Lock flips Hand and Move in Figma only.
❯ works great, can you save it to autostart for me?
● rebind - screenshot_window (MCP)(title: "Rebind")
  ⎿  Scripts > figma-tools > Options, Auto-start off
● rebind - click (MCP)(x: 1184, y: 412)
  ⎿  landed on Auto-start; it now reads on
● Autostart is on, so figma-tools now loads every time Rebind starts. It only acts while Figma is your active window.
❯ watch me play this fishing game, then write a rebind script that fishes in a loop
● rebind - observe_start (MCP)
  ⎿  recording keys, clicks and frames
● rebind - observe_events (MCP)
  ⎿  3 casts: hold Mouse1 1.2s, wait for the dip, click to reel
● rebind - observe_frame (MCP)(file: crops/000041.png)
  ⎿  the bobber dips, a splash where it sat
● Write(fishing-loop/main.luau)
● Bash(rebind run fishing-loop)
● fishing-loop casts, waits for the dip, reels in and casts again. F8 stops it.
❯ which tool is active in figma?
● rebind - list_windows (MCP)(filter: "Figma")
  ⎿  Figma  process Figma  "Design system"  1440x900
● rebind - screenshot_window (MCP)(title: "Figma")
  ⎿  1440x900, toolbar in view
● Move is active: V is lit in the toolbar.
❯ publish figma-tools to the rebind marketplace
● Bash(rebind pack figma-tools)
  ⎿  packaged figma-tools.rbp (3 files, 850 B)
● Bash(rebind publish figma-tools)
  ⎿  submitted figma-tools v0.0.1 for review
● It's on its way to the marketplace. You wrote no code.

Rebind is a crowbar for the USB stack.

  • Your firmware has opinions. Your Luau scripts outrank them.
  • Vendor software lets you select behavior. Rebind lets you write it.
  • The defaults came preinstalled. Authority did not.

Scripts run on your computer and decide what every key, click and button does. With Rebind Link, their output leaves a real USB device.

remap.luau
-- left stick moves the mouse (hardware mode)rb.on("axis", function(axis, value)  -- tilt -1 to 1 moves up to 10 px per event  local d = math.round(value * 10)  if axis == "LeftX" then rb.hid.move(d, 0) end  if axis == "LeftY" then rb.hid.move(0, d) endend)-- Ctrl+B types a message, one key at a timerb.bind("Ctrl+B", rb.async(function()  -- rb.async lets typewriter wait between keys  rb.hid.typewriter("brb, two minutes", 90)end))

Left stick moves the mouse

How it works

Your devices go through Rebind first.

With Rebind Link, your USB devices are forwarded into a hypervisor instead of your OS. Rebind decodes every event in Rust, runs your script on it, and Link sends the result to your computer as real USB.

  1. Mice, keyboards, controllers

    As many as you already own, plugged in as usual.
  2. Processed in a hypervisor

    Rebind hands your devices to a hypervisor, which reads their raw input before your OS can.
  3. Decoded in Rust

    Every key, click, axis and button becomes an event your Luau script can read, change or drop.
  4. Out as real USB

    Link sends the result to your computer as a USB keyboard, mouse or gamepad.

Without Link, software mode reads input through your OS instead, and mouse movement can't be blocked there.

Luau SDK

Any button can become any key, click or stick.

Scripts are Luau, the fast, typed Lua from Roblox. Think of an AutoHotkey macro that runs on Windows and macOS, sees input before your apps do, and can drive real USB hardware.

-- rebind: min_sdk=3.9.0
-- rebind: name=Push to Talk
-- rebind: version=1.0.0

rb.on("down", function(key)
  if key == "Mouse4" then
    rb.hid.down("F13")
    return false
  end
  return true
end)

rb.on("up", function(key)
  if key == "Mouse4" then
    rb.timer.after(250, function()
      rb.hid.up("F13")
    end)
    return false
  end
  return true
end)

Hold the side button to talk. Let go and the mic stays open a quarter second longer, so your last word never gets clipped.

Your peripherals.Your rules.

Rebind is free. Install it, start from an example, and write the behavior you want.