TLbind is a system which turns a mass of keyboard/joystick input into easy-to-use game-oriented controls. Making this divide also makes it easy to rebind controls, enable or disable keyboard, mouse, or joystick input at will, and make digital and analogue input work easily together. It's goal is to make it simple to add professional-style configurable "2 sets of keys + optional joystick" controls to Love2D games.

It's under the ZLIB license.


Direct from Dropbox



Procedural style

  1. Add TLbind.lua to your game's folder
  2. In love.load, add the code TLbind,control = love.filesystem.load("TLbind.lua")()
  3. In love.update(), add the code TLbind:update()

Object-oriented style

  1. Add TLbind.lua to your game's folder
  2. Assuming your player object is called "player", and you want its controls to be listed in player.control, use the code player.binds, player.control = love.filesystem.load("TLbind.lua")()
  3. In your player object's update function (or otherwise once per frame), call player.binds:update()
  4. TLbind itself acts like an object, so you can give each player their own separate TLbind (using TLbind.giveInstance()), if you so desire (could be handy for splitscreen)


Q) How do I know when a control is pressed?
A) Whenever a control is pressed or held, control[control name] will be true. You could use it with a statement like if control.up then...
Q) How do I change a control?
A) To bind a "fire" control to the third button of joystick 1, for example, you could use TLbind.joyBtns[1][3]="fire". From then on, would be true whenever Joy1 button 3 is pressed.
Q) How do I know if the player has simply pressed the button, instead of holding it? What if I want to know when it's released?
A) When any control is first pressed, control.tap[control name] will be true for one frame. Likewise, when it's released, control.released[control name] will be true for one frame. These would be used the same way you'd use a normal control - if control.tap.jump then. You can also use the control.controlPressed and control.controlReleased callbacks (see below).
Q) How does TLbind handle mouse input?
A) Clicking is bound to digital controls, like a joystick button. Cursor position is an analogue pair where the center of the screen is (0,0), the top-left corner is (-1,-1), and the bottom-right corner is (1,1). If the mouse position is bound to the same control as joystick axes, then the control will only use the mouse position when the joystick axes are centered. If you can't seem to move the mouse axes, ensure your joystick is centered (both axes are 0).
Q) What functionality does TLbind have?
A) It separates controls (things your game cares about "jump", "left", etc.) and input (spacebar, joystick coordinates, etc.). This allows a game's controls to be easily configured at any time; lets a game have multiple inputs per control (or controls per input); and makes coding simpler. It makes it easy to bridge analogue and digital control; disable keyboard or joystick input easily; detection of taps instead of holding (no more "hold jump to bounce over and over the second you touch the ground"); and can apply analogue "deadzones" (fixes the "drifting when not touching the stick" problem) and normalizing (preventing the all-too-common "you run faster diagonally" bug).
Q) Is it hard to use?
A) I tried to make it as simple as possible, and list example binds below. With six lines of code, you can give your game industry-standard WASD + Arrows + Gamepad controls.
Q) What's the difference between digital and analogue controls, and how do you make them work together?
A) In TLbind, digital controls are keys or buttons, and are represented as either true or false. Analogue controls take input from joystick axes or balls, and are represented as a decimal between -1 and 1, with 0 being centered. TLbind lets you map an analogue control to two digital controls, which makes the analogue input trigger the digital controls and the digital input push the analogue control to -1 or 1. As an example: If using the example binds below, pressing the joystick a little to the left might make control.horiz = -0.22191, control.left = true, and control.right = false.




This updates the controls, and thus should be called once each frame. Please note that it has a colon, not a period.


TLbind.giveInstance( binds )

This will create a new set of controls from the given binds table. Returns the new instance's binds, control, control.tap, and control.release. Good for OO-style programming, multiplayer controls, etc..

table binds
A table of binds, as explained below.



This callback works just like love.keypressed, except for controls (meaning it'll work properly with binding changes and joystick input). It gets called exactly once when a control is activated.

string c
The name of the control that was pressed.



This callback works just like love.keyreleased, except for controls.

string c
The name of the control that was released.

Input types and configuration

Bindings are managed under TLbind (procedural example / default) or player.binds (OOP example). A list of bindings is a table, grouped together by input, joystick number, etc.. For clarity, please refer to the following terminology:

  • Control - an abstract, game-centric variable which will either equal true/false (for digital controls) or a number between -1 and 1 (analogue controls). Controls can have any name you want to give them, but each name must be unique.
  • Input - the signal a keyboard or joystick gives off when manipulated. Love2D defines these as KeyConstants, MouseConstants, JoystickConstants, joystick numbers, and joystick button, axis, hat, and ball numbers.
  • Bind - the link between input and controls. "When this input is triggered, set that control". Each input can be bound to only one control, but a control may be bound to many inputs.
  • Map - links an analogue control to two digital controls. Mapping makes it simpler to work with analogue controls since you don't have to worry about whether or not the player is using analogue or digital input.


This is for keyboard keys. It's a table where the keys are KeyConstants and the values are the names of controls. In other words, you make bindings in the format TLbind.keys.KeyConstant = "control". See

TLbind.keys = {
	w="up", a="left", s="down", d="right", [" "]="jump", lctrl="attack", escape="menu",
	up="up", left="left", down="down", right="right", z="jump", rctrl="attack",


This is a table that binds the mouse x and y coordinates. It's in the form of TLbind.mouseAxes[1 for x or 2 for y] = "control".

TLbind.mouseAxes = { "horiz", "vert" }


This lists mouse buttons, in the form of TLbind.mouseBtns.MouseConstant = "control".

TLbind.mouseBtns = { l="target", r="shoot" }


This is the list of joystick axes (the stick itself or gamepad thumbstick(s), but may also include rudders, etc.). Using bind.deadzoneAxes on axis pairs is highly recommended. It's in the form of TLbind.joyAxes[joystick#][axis#] = "control".

TLbind.joyAxes = { {"horiz", "vert"} }


This lists joystick buttons, in the form of TLbind.joyBtns[joystick#][button#] = "control"

TLbind.joyBtns = { {"jump", "attack", [8]="menu"} }


This is for any ball device a joystick may be sporting. Since these are rare, it's advisable to not make your game depend on using one. Their binds are in form of TLbind.joyBalls[joystick#][ball#] = {"x control", "y control"}

TLbind.joyBalls = { { {"horiz","vert"}, {"yaw","pitch"} } }


This table is for joystick hats (those mini-thumbstick things seen on some joysticks). Please note that they're digital, not analogue. They're bound in the form of TLbind.joyHats[joystick#][hat#] = {"l control", "r control", "u control", "d control"}

TLbind.joyHats = { {{"left","right","up","down"}} }


This table lists the analogue controls that should be mapped to and from digital controls. Using this, you can easily code for one type of input without having to worry about coding for the other type. They're created in the form of TLbind.maps[analogue] = {"negative digital", "positive digital"}

TLbind.maps = { horiz={"left","right"}, vert={"up","down"} }


This feature applies scaled radial deadzones to a pair of analogue controls. This prevents "stick drift" on old/cheap/uncalibrated devices. It also restricts the analogue pair to the inside of a circle, rather than the default square. Using this, you can avoid the classic "you move faster diagonally" bug. Pairs will reach about 0.7 when at a perfect 45-degree angle (but still reach a full 1 when straight). It's made in the form of TLbind.deadzoneAxes[entry#] = {"analogue 1", "analogue 2"}

TLbind.deadzoneAxes = { {"horiz", "vert"} }

A note about Xbox controllers

Microsoft has decided that Xbox controllers are the official gamepad for Windows, and as such, are the most common controller a player may have. Therefore, it's advisable to tailor your game's controls to work with them.

One important caveat about Xbox controllers: Both of their triggers (L2 and R2 for those of you more familiar with Playstations) are treated as one axis, rather than two buttons! Consider using the following binds to make up for it:

joyAxes = { {[3]="triggers"} }			-- r trigger is -1!
maps = { triggers={"rtrig","ltrig"} }	-- ltrig and rtrig are the digital controls

Other Languages