Godot Touch Input for Mobile Web: Events, Emulation, and UI Scaling

Godot Touch Input for Mobile Web: Events, Emulation, and UI Scaling

Honesty note: this is Godot’s documented input model as of 4.x, mapped for mobile-web use. I built my earlier Godot prototype with desktop-style input and hit the touch problems this post fixes — the honest framing is “what to check before your first phone test,” not a claim of shipped mobile games.

TL;DR

  1. Godot input is unified: InputEventScreenTouch (tap) and InputEventScreenDrag (drag) are the touch events; _input()/_unhandled_input() receive them. The engine emulates mouse from touch by default (emulate_mouse_from_touch), so mouse-based code often works on mobile without changes — until multi-touch or precision matters.
  2. The three touch gotchas: (1) emulate_mouse_from_touch can fire both touch and mouse events — guard against double-handling; (2) multi-touch needs emulate_touch_from_mouse off and real touch events — a pinch/dual-tap needs InputEventScreenTouch.index; (3) touch targets must be big — a 40px button is a fat-finger miss; design for ~48px+ on mobile.
  3. Actions are the right interface: map your controls to InputMap actions (e.g. swipe_left, tap_play) once, then trigger them from both mouse and touch in one place — your game logic never cares about the input source (action-based pattern).
  4. UI scaling is the second half: Godot’s stretch modes (canvas_items with expand, or viewport keep-aspect) keep your game fitting phones and desktops; without the right stretch settings, touch coordinates and UI anchors drift apart on different screens (responsive reality).

The event model (30 seconds)

func _input(event):
    if event is InputEventScreenTouch:
        if event.pressed:
            _on_tap(event.position, event.index)   # index = which finger
    elif event is InputEventScreenDrag:
        _on_drag(event.position, event.relative, event.index)

The three gotchas (with fixes)

  1. Double-handling — with emulation on, a tap can arrive as both InputEventScreenTouch and a synthesized InputEventMouseButton. Fix: handle either mouse or touch, not both; or detect and ignore the synthetic pair (check event.device and your settings).
  2. Multi-touch — emulation is single-finger; pinch/two-thumb needs real touch events and per-finger index. Turn emulation off where multi-touch matters.
  3. Touch target size — a desktop-ported 32px button is a mobile miss. Enforce ~48px minimum targets and spacing in your UI layout (size guide).

Actions: the one interface that survives ports

# Project Settings → Input Map
#   tap_play:  mouse left + touch tap
#   swipe_left: touch drag left + arrow key (desktop testing)
func _unhandled_input(event):
    if event.is_action_pressed("tap_play"):
        _start_game()

Mapping inputs to actions once, then checking actions in game code, means your logic is input-source-agnostic — the same build works on touch mobile and mouse desktop without if event is ... forks everywhere (the pattern).

UI scaling (the second half)

Godot’s display/window settings decide how your game fits a phone:

Test on: a phone-sized window, a tablet ratio, and desktop — the same game should be playable and touchable in all three (60fps budget).

Pitfalls

  1. Trusting desktop tests — mouse works; touch needs real-device or emulation testing; enable emulate_touch_from_mouse and test in the browser.
  2. Double-firing input — emulation + manual touch handling = two actions per tap; pick one path.
  3. Small touch targets — 32px buttons are unplayable on mobile; size for fingers.
  4. No action map — if event is InputEventMouseButton forks in game logic make input-source changes a rewrite.
  5. Wrong stretch mode — the game renders off-screen or UI anchors drift on phones; set canvas_items + expand and test ratios.

Bottom line

Godot touch input for mobile web is: real touch events (ScreenTouch/ScreenDrag) under a mouse-emulation default that works for simple ports, actions as the single input interface, and stretch mode + anchors for the UI half. Test on real mobile or with emulate_touch_from_mouse, guard the double-fire, size targets for fingers — and your Godot game plays the same on a phone and a desktop (the full engine comparison).