Touch controls look simple: draw a button, listen for a press, set a flag. The complexity is entirely in the edge cases, and the edge cases are where players are lost.

Use Pointer Events

The Pointer Events API unifies mouse, touch and stylus into one event model. Using it means one code path rather than three and removes a class of bugs where a game works with a mouse and fails with a finger.

Four configuration details are mandatory:

  • touch-action: none on the play area, or the browser interprets drags as scrolling and swipes as navigation gestures.
  • setPointerCapture on pointer down, so a drag that leaves the element keeps delivering events instead of stopping mid-motion.
  • pointercancel handling as well as pointerup. The browser can cancel a pointer — an incoming call, a system gesture, a notification — and a game that only listens for up will leave a button stuck down.
  • Context menu suppression, or a long press opens a menu over the game.

That third item is the one that loses players. A stuck button means a character running into a wall indefinitely, and the player has no way to understand why.

Virtual button state

A virtual button sets a flag that the input layer treats identically to a held key. The implementation needs three handlers at minimum:

  1. pointerdown — set the flag, add a pressed visual state.
  2. pointerup — clear the flag, remove the visual state.
  3. pointerleave and pointercancel — clear the flag, because a finger sliding off a button should release it.

The buttons are also real DOM <button> elements with ARIA labels, and they respond to keyboard activation. That is not for keyboard players — they have real keys — but for switch access and other assistive input that operates DOM controls.

Layout: thumbs and information

Holding a phone in two hands puts your thumbs in the lower corners, with a comfortable arc covering roughly the bottom 30% of the screen.

Three rules follow:

  • Controls in the bottom corners. Movement left, action right.
  • Information at the top. Score, combo, lives and timers sit in a band out of the thumb arc and clear of the hands.
  • The middle stays clear. That is the gameplay, and a thumb must never cover it.

On this site the HUD is a DOM overlay at the top of the stage and the touch controls are a separate overlay at the bottom, which makes the separation structural rather than a matter of discipline.

Target size

WCAG 2.2 sets a minimum target size of 24 by 24 CSS pixels; the earlier enhanced criterion suggested 44. For a control pressed repeatedly under time pressure, neither is generous.

This site uses 64-pixel circular buttons, reduced to 58 on small screens. A control you have to look at to hit is a control that costs you the game, and the extra size costs nothing except screen area that is not being used for gameplay anyway.

Matching the scheme to the game

SchemeSuitsExample
Tap anywhereOne action, no directionOne-Tap Food Catcher
Drag to steerContinuous positioningFood Catcher
Two buttonsHorizontal movementRunner and rolling games
Three buttonsMovement plus jumpCupcake Jump
Four-way padFree arena movementCollection and maze games
Hold to thrustSingle continuous inputFlying games
Direct tap on targetsSelecting from a setKitchen Dash

The most common mobile mistake is adding a virtual joystick where one is not needed. A catcher game with drag-to-steer is more precise than the same game with a stick, because the drag maps to position rather than to velocity.

Latency

Touch input has measurably more latency than a key press — the touchscreen samples at a fixed rate and there is additional delay in gesture disambiguation.

The fix is not to detect the input type and change the rules, which produces two different games. It is to size timing windows generously enough for both. One-Tap Food Catcher's catch band never narrows below 7% of the track, comfortably above what touch latency requires.

Hints, briefly

Games with gesture controls rather than buttons show a hint — "Drag anywhere to steer", "Tap and hold to climb" — centred below the play area, which fades after four seconds.

Four seconds is enough to read and short enough not to become clutter. A permanent hint is a permanent obstruction, and a player who needs it after the first run can read the controls list on the page.

Try it yourself: Cupcake Jump

Climb a cupcake tower through four tiers of shrinking platforms and richer food.

Play Cupcake Jump

Key takeaways

  • Use Pointer Events with touch-action: none, pointer capture, and pointercancel handling — a stuck button loses players.
  • Virtual buttons should be real DOM buttons with ARIA labels, so assistive input can operate them.
  • Controls in the bottom corners, information at the top, middle clear for gameplay.
  • Use 58–64 px targets rather than the WCAG minimum; a control you must look at costs you the game.
  • Match the scheme to the game — drag-to-steer beats a virtual joystick for positioning games.
  • Size timing windows for touch latency rather than detecting input type and changing the rules.

References

  1. MDN Web Docs — Pointer events — developer.mozilla.org
  2. W3C — Understanding WCAG 2.2: Target Size (Minimum) — w3.org

Frequently Asked Questions

Why should browser games use Pointer Events?

Because it unifies mouse, touch and stylus into a single code path, which removes a class of bugs where a game works with a mouse and subtly fails with a finger.

Why do virtual buttons sometimes get stuck?

Because the game listens only for pointerup and not pointercancel. The browser can cancel a pointer for an incoming call or a system gesture, which leaves the flag set indefinitely.

How big should touch controls be?

58 to 64 pixels. WCAG sets 24 as a minimum and 44 as enhanced, but a control pressed repeatedly under time pressure needs to be hittable without looking at it.

Is a virtual joystick a good idea?

Usually not. For positioning games, drag-to-steer is more precise because the drag maps to position rather than to velocity. Joysticks add a layer between the finger and the result.

Do timing windows need to be different on touch?

The windows should be sized generously enough for both input types rather than changed based on detection, which would produce two different games.