Bind.Remap(from, to)— a declarative one-line key-to-key remap.OnDown(key)/OnUp(key)— returntrueto let a key pass through,falseto swallow it. Send your replacement withHID.Down/HID.Up/HID.Press.Input.IsDown(key)— test whether a physical key is currently held, which is how you build chords, modifier holds, and layers.
Single-key remap
Bind.Remap takes the key you press and the key you want sent in its place — no
hooks, no state. Caps Lock becomes Escape; your real Escape is untouched.
Bind.Remap returns a handle, so you can toggle a remap at runtime with
:disable() and :enable(). Here a hotkey flips it live, and the handle reports
its state through .enabled.
OnDown / OnUp and decide per event. The remaining
recipes all use that form.
Chords and combos
A chord fires one action only when several keys are held together. Read the other keys withInput.IsDown at the moment the trigger key goes down, and swallow the
trigger so it doesn’t type on its own.
Input.IsDown("LCtrl") — and emit a keyboard
combo with the + syntax:
Modifier hold
A modifier hold gives one key two jobs: tap it for its normal value, hold it to change what other keys do. Track when the key went down and whether another key was used while it was held. On release, if nothing else fired and the hold was short, send the tap.usedAsMod prevents a stray Space when you genuinely used the hold, and TAP_MS
keeps a long, lone hold from emitting a late Space. OnBlur clears the state if
focus leaves while you’re mid-hold.
Hold-to-access layer
A layer is a modifier hold scaled up: hold one key to expose an alternate set of keys, release it to return to normal. Keep the mappings in a table so adding a key is a one-line edit.Input.IsDown(layerKey), every other key behaves
normally the instant you let go. To make it app-aware — active only in one program
— wrap the body in a System.Window() check:
Opposing-key resolution (last pressed wins)
For movement keys mapped to opposite directions — left and right, or up and down — holding both at once is ambiguous: the two presses cancel and you stop. One way to resolve it deterministically: when two opposing directions are held at once, the most recently pressed one is active. Release the newer key and the older one, still held, takes over again. Record the order in which each key of an opposing pair went down, then send only the winner and suppress its opposite.pressedAt stores when each key last went down,
activeOf picks the larger timestamp, and refresh makes the live output match.
Releasing the newer key calls refresh on the survivor so the older direction
resumes with no extra tap. OnBlur releases both outputs if focus changes mid-hold,
so a direction can never get stuck on.
Where each recipe runs
See Platforms for the full capability matrix.