Back to Blog
controlsuxgame feelaccessibility

Control Schemes That Don't Fight the Player: Bindings, Buffers and Tolerances

Players report that controls feel 'floaty' but never say why. Write down your action list, binding table and input tolerances — buffer, coyote time, deadzones — so you can tune real numbers instead of guessing at vibes.

GameDesignerX TeamSeptember 11, 20266 min read

A playtester picks up your platformer, holds right, and presses jump a few frames before landing. Nothing happens. They fall into the pit, shrug, and say "the controls feel floaty." You spend the next week tweaking gravity and air acceleration, because that is what "floaty" sounds like. The actual problem was that you never implemented a jump buffer, and the tester's input was discarded 60 milliseconds before it would have been valid.

This is the recurring tragedy of control design: players report feelings, not frames. They can tell you something is wrong, almost never what. The fix is to decide your input behaviour deliberately, write it down with numbers, and test against those numbers instead of against vibes.

Controls are a design document, not an implementation detail

Most small teams treat input as something that emerges from the character controller. Someone wires up a jump, someone else adds a dash, a third person adds a gamepad because a playtester asked, and six months later nobody can answer basic questions: What is the dash on a controller? Is it the same button as dodge? What happens if you press it during a cutscene? Can a left-handed player remap it?

A control scheme document answers those before they become bugs. At minimum it holds three things:

  1. An action list — the abstract verbs your game has, independent of any device.
  2. A binding table — how each verb maps to keyboard, gamepad, and any other device you support.
  3. A feel spec — the timing and tolerance values that make those bindings forgiving.

The first two are bookkeeping. The third is where the game actually lives.

Start with verbs, not keys

Write the action list as verbs the player would use: Jump, Attack Light, Attack Heavy, Interact, Cancel, Open Map. Never name an action after a key. The moment you have an action called SpaceBarAction in your codebase, you have guaranteed a painful refactor when you add a gamepad, and a worse one when a localisation or accessibility pass asks you to remap.

Keep the list visible and count it. A useful gut check for a small team: if your action list is longer than the number of comfortable buttons on a standard gamepad — roughly 14 including the face buttons, bumpers, triggers and stick clicks — you are about to invent modifier chords, and modifier chords are where accessibility complaints come from. Either cut verbs, or design a context system where the same button means different things in clearly separated states.

The binding table

Make it a real table, not prose. This is the single artefact that stops arguments in a code review:

Action Keyboard Gamepad Context Hold / tap
Move WASD Left stick World, Menu Analogue
Jump Space A / Cross World Tap; hold = higher jump
Interact E X / Square World, near prompt Tap
Attack Light Left mouse RB / R1 Combat Tap
Attack Heavy Right mouse RT / R2 Combat Hold 0.25s
Dodge Shift B / Circle Combat Tap
Cancel / Back Esc B / Circle Menu Tap
Map M Touchpad / View World Tap

Two things fall out of a table like this the moment you write it. First, Dodge and Cancel share B on a controller — fine, because the contexts never overlap, but you now know that and can test it. Second, Attack Heavy is a hold, which means it needs a visible windup or players will think the game dropped their input.

If you are tracking this in the GameDesignerX control schemes module rather than a loose spreadsheet, the win is that each action links to the mechanic that owns it, so when the design of Dodge changes the binding table is not quietly left behind.

The feel spec: the numbers that make controls forgiving

This is the part almost nobody writes down, and it is the part that decides whether your game is described as "tight" or "floaty." These are tolerances — small windows where the game is generous with the player's timing.

  • Input buffer. If the player presses an action while it is unavailable, remember it briefly and fire it as soon as it becomes legal. Around 100–150 ms is a common working range; longer starts to feel like the game is playing itself.
  • Coyote time. Allow jumping for a few frames after walking off a ledge. 5–8 frames at 60 fps is a typical band. Without it, precise platforming reads as broken rather than hard.
  • Stick deadzone. Use a radial deadzone, not per-axis, or diagonals will feel wrong. Around 0.15–0.25 of stick range is normal; worn controllers need the upper end.
  • Hold threshold. The time before a press counts as a hold rather than a tap — 0.20–0.30 s. Under 0.15 s and players trigger holds by accident.
  • Double-tap window. 0.25–0.35 s. Anything relying on double-tap should also have a single-button alternative in the remap screen.
  • Repeat delay and rate. For menu navigation: roughly 0.4 s before repeat begins, then one step every 0.1 s.

Put your chosen values in the document as ranges with a current setting, and expose them as tunable variables in the engine. The point is not that these specific numbers are correct for your game — it is that they are decisions, visible to everyone, and testable.

Testing controls without asking "does it feel good?"

"Does it feel good" produces useless data. Ask questions that isolate a tolerance:

  • Give the player a stretch of platforming with three ledge jumps and count failed jumps that occurred within 8 frames of leaving the ground. Those are coyote-time failures, not skill failures.
  • Have them fight one enemy and count inputs pressed during recovery animations that produced no action. Those are buffer failures.
  • Ask them to remap two actions before they start playing, and watch. If they cannot find the screen in 30 seconds, your options menu is the problem.
  • Hand them a gamepad mid-session. Any hesitation about which button does what is a discoverability gap, usually a missing on-screen prompt swap.

Log the counts in your playtest notes alongside the tolerance values that were active. When you change a number, you can see the count move. That is the whole loop.

A shipping checklist

Before you call controls done:

  • Every action is rebindable, including movement.
  • Rebinding detects and warns about conflicts rather than silently overwriting.
  • On-screen prompts switch between keyboard and gamepad glyphs automatically when the device changes.
  • There is a "reset to defaults" button.
  • Hold actions have a tap alternative, or a toggle option (hold-to-crouch vs toggle-crouch).
  • Deadzone and sensitivity are player-adjustable.
  • Menus are fully navigable with the gamepad alone, and with the keyboard alone.
  • Nothing critical requires a chord, a double-tap, or rapid mashing without an accessibility alternative.
  • The binding table in your design doc matches what the build actually does.

That last item is the one that rots fastest. Schedule a five-minute check against the document at every milestone; it costs nothing and it is the difference between a control scheme you designed and one that merely accumulated.