DEV Community

Liza Bhadana
Liza Bhadana

Posted on

Why your browser game is unplayable on a phone

There is a specific way a web game fails that almost never shows up in a bug report, because the person who hit it did not think of it as a bug. They opened your game on a phone, tapped where the page told them to tap, and then found that the thing they were holding could not send the inputs your game was waiting for. They did not complain. They left.

This article measures how often that happens across a real catalogue of 200 HTML5 games, and what the page around the game does to make it worse.

The short version

We audited the developer-written control list of every game in our catalogue. Of the 155 that have one, 68 never mention touch, tapping, swiping or a phone at all, and 26 name a keyboard key as the movement control with no touch alternative — nine of those expect two people sharing one keyboard.

Meanwhile the page around the game says the opposite. On all 14 live pages for those keyboard-driven games, the copy invites the player to tap, all 14 then list a keyboard key, and none of the 14 mentions touch anywhere. And the box any on-screen controls would have to fit into measured 331 by 207 CSS pixels.

What we measured, and what these numbers are not

Everything below was measured on 5 August 2026, in Chrome on Windows, against live pages on slowden.com and against our own game registry. We are the portal in this article. That is deliberate: the interesting data here belongs to whoever runs the host page, and it is easier to publish honestly when the unflattering findings are your own.

Two separate sources, measured two different ways.

The catalogue audit. Our registry holds 200 games, of which 120 are live on the site today. Each entry can carry a controls list, transcribed from that game's own public page on Playgama, the partner catalogue SlowDen lists through. 155 entries have one; 45 have none. These are the developers' and publisher's words, not ours.

The live page measurements. Geometry was measured by loading live pages into a same-origin frame of an exact viewport size and reading real bounding boxes. Computed styles, iframe attributes and page copy were read from the live documents.

The most important caveat, stated before the numbers rather than after them: the audit measures documentation, not games. A game may accept touch perfectly well and simply not say so. Another may say so and handle it badly. Nothing here is a claim about whether any specific game works on your phone. What it does measure is what a player can find out before they commit — and that is its own problem, because a player deciding whether to start has nothing to go on except the page.

We also corrected ourselves twice while doing this, and both corrections are worth knowing if you run the same audit. Our first keyword pass classified Smartphone controls: as having no phone mention, because a word-boundary match on "phone" does not match inside "Smartphone". It also counted the ordinary English word "space" — as in "free up space in the warehouse" — as the space bar. The load-bearing figure below is therefore a list of entries read by hand, not a regular expression's output, and five entries are excluded with a written reason each.

Finding 1: 68 of 155 control lists never mention touch

Here is the whole catalogue, split by what the developer wrote about input.

Group Count Share of the 155 with a list
Documents some touch or phone input path 87 56.1%
… of which names an on-screen or virtual control 23
… of which splits "Desktop:" from "Mobile:" explicitly 30
Mentions no touch input of any kind 68 43.9%
… of which names a keyboard key as the movement control (hand-verified) 26 16.8%
Has no control list at all 45 of 200 entries

The 87 in the first row are the good news, and the shape of the good news is worth copying. Thirty of them do the single most useful thing a control list can do: they write two labelled sections, one for desktop and one for phones. "Desktop: the snake turns with the cursor. Mobile: use the joystick to rotate the snake." That is Snake 2048 by TurtleGamesStudio, and it takes eleven extra words to tell a phone player that the game is for them.

The 45 entries with no list at all are a quieter version of the same problem. A player on a phone gets nothing: not a control scheme, not a warning, not a "works best on desktop". They find out by starting.

Finding 2: 26 games hand the player a keyboard, and nine hand out two

This is the group where the absence of a touch path is not a documentation gap but a wall. Twenty-six control lists name a keyboard key as the way you move, and mention no alternative. Fourteen of those games are live on the site today; twelve are in the catalogue but not yet published.

A phone has no W, no A, no S, no D. It has no shift key to sprint with, no space bar to jump with, and no separate left and right mouse buttons to shoot and aim with. If a game's only documented path through its first thirty seconds runs through those, then for a phone player there is no path.

The concentration by genre is not random.

Category Keyboard-driven, no touch listed Entries with a control list
Horror 7 16
Block & Sandbox 6 23
Racing 4 18
Sports 3 16
.io Games 2 10
Funny 1 21
Shooting 1 16
For Girls 1 12
2 Player 1 5
Puzzle 0 18

Puzzle games score zero, and that is the useful signal in the table rather than an accident. A puzzle is usually driven by pointing at a thing, and a tap is a point. First-person horror and 3D sandbox games are driven by simultaneous movement plus a look direction plus an action, which is four fingers' worth of intent on a surface that has room for two thumbs.

The nine that cannot be ported at all as designed

Nine of the 26 are two-players-on-one-keyboard games. Stickman Kombat 2D by Ernar (Tempo Play) gives Player 1 WASD plus F and G, and Player 2 IJKL plus a colon and a quote key. Escape School Duel and Catch a Fish Obby, both by gameVgames, split the board the same way. Soccer Random and Basket Random by RHM Interactive are the elegant extreme: one key each, W for Player 1 and the up arrow for Player 2.

These are not games that need touch controls added. The mode is two people at one keyboard, and a phone has one screen roughly the size of two hands. Same-device local multiplayer is genuinely one of the nicest things a browser game can do, and it is one of the few designs where "this one is for desktop" is a legitimate answer — provided somebody says so on the page. Nobody does.

Finding 3: the page says tap, then lists the keys

This is our fault, not a developer's, and it is the finding that made this article worth writing.

We fetched the live page for all 14 of those keyboard-driven games that are published today. All 14 returned HTTP 200. Then we read what each page tells a player.

What the live page does Pages
Invites the player to tap 14 of 14
Carries a Controls section 14 of 14
Names a keyboard key in it 14 of 14
Mentions touch, swipe, joystick, mobile or phone anywhere 0 of 14

Take Moto X3M by MadPuffers, a Racing game and one of the 14. The page says: "Wait for Moto X3M to finish loading in the player above. Click or tap inside the game window to start. Follow the in-game prompts — controls are listed below." Below it, the controls are: WASD or arrows, up arrow to accelerate, left and right to balance, down arrow to brake.

So the page invites a tap, and then describes a game played with four keys. A player who followed the instruction exactly did the right thing and still ended up stuck. Nothing on that page is a lie in isolation; the combination is the defect. It is generated from one template, so it is the same defect 14 times.

If you list your game on any portal, this is worth knowing for a blunt reason: the host's page copy will be written by a template that has never read your control scheme. Whatever your game needs, assume the page will not say it. The only place a phone player can reliably be told is inside your game's own first frame.

Finding 4: the box your on-screen controls have to fit into

Suppose a developer does the right thing and adds touch controls. How much room do they get? We measured the same live game page at five widths by loading it into a same-origin frame at each exact size.

Viewport (CSS px) Game box Box area Share of viewport area Page above the box
360 × 740 301 × 188 56,550 px2 21.2% 283 px
390 × 844 331 × 207 68,558 px2 20.8% 283 px
412 × 915 353 × 221 77,792 px2 20.6% 259 px
430 × 932 371 × 232 86,118 px2 21.5% 259 px
768 × 1024 707 × 442 312,494 px2 39.7% 237 px

Across every phone width the answer is remarkably stable: the game gets about one fifth of the screen. The box is width-driven with a fixed aspect ratio, so it scales with the page rather than with the space available, and the share barely moves between a small Android phone and a large iPhone.

The starker number is the page rather than the screen. At 390 by 844, the full document is 4,086 CSS pixels tall — 4.8 screenfuls — with 283 pixels of page above the game and 3,596 pixels below it. Your game is 5.07% of the height of the page it lives on.

What that means for a virtual joystick

WCAG 2.2 Success Criterion 2.5.8, Target Size (Minimum), Level AA, requires that "the size of the target for pointer inputs is at least 24 by 24 CSS pixels". That is an accessibility floor for any tappable target, not a size anyone would choose for a control held down mid-action. Using it as a floor, and counting the distinct inputs each control list names by hand:

Game (studio) Distinct inputs named At the 24 × 24 floor Share of a 331 × 207 box
Moto X3M (MadPuffers) 4 2,304 px2 3.4%
The Tall Man (Sleepless Games) 6 3,456 px2 5.0%
Escape from the Portal (Mirra Games) 12 6,912 px2 10.1%
Grand Shift Auto (oneru220) 15 8,640 px2 12.6%
Hazmob FPS: Online Shooter (Hazmob) 19 10,944 px2 16.0%

Read that table the pessimistic way round. Those percentages are the best possible case — controls shrunk to the smallest size an accessibility standard will tolerate, packed with no gaps, and every one of those pixels still sitting on top of the game the player is trying to see.

Width is the tighter constraint than area, and this next line is an illustration rather than a measurement, because we have no measured figure for a comfortable thumb zone: if you allow two 120-pixel zones for two thumbs, that is 240 of the box's 331 pixels of width and 120 of its 207 pixels of height. The controls stop being an overlay on the game and become most of it.

The conclusion we draw is not "draw smaller buttons". It is that a game with nineteen inputs does not become a touch game by growing nineteen buttons. The games that survive the move are redesigned down to one or two simultaneous inputs, which is a design decision made early and cheaply, or very late and expensively.

Finding 5: nothing on the page is protecting your gestures

Say the controls fit. There is one more thing between a swipe and your game, and it is the default behaviour of the browser.

MDN's reference for the CSS touch-action property puts it plainly: the initial value is auto, and "by default, panning (scrolling) and pinching gestures are handled exclusively by the browser". Setting touch-action: none is what hands those gestures to the page instead.

We read the computed touch-action of every element on a live game page. The count of elements restricting touch gestures in any way was zero. The root element, the body, the embed container and the frame itself are all auto. And directly below that container sits 3,596 pixels of scrollable page.

So a vertical drag that starts on the game and is not claimed by the game is a drag the browser is entitled to read as a scroll — and the page will happily oblige by moving the game off the screen.

This one you have to fix yourself, and that is not a complaint about hosts. A portal cannot reach inside a cross-origin frame to set touch-action on your canvas — the isolation that stops a host reading your game also stops it helping. So the responsibility genuinely is the developer's. Two lines:

/* in your game, on whatever surface receives play input */
canvas, .game-surface { touch-action: none; }
Enter fullscreen mode Exit fullscreen mode
// and register listeners so preventDefault is actually allowed
el.addEventListener('touchstart', onTouch, { passive: false });
el.addEventListener('touchmove',  onMove,  { passive: false });
Enter fullscreen mode Exit fullscreen mode

Chrome treats touchstart and touchmove listeners on the document as passive by default, which means preventDefault() inside them is ignored unless you opt out explicitly. A game that calls preventDefault() and still gets scrolled is usually hitting exactly this.

For completeness, the host side of this particular page is not doing anything hostile: the frame carries allowfullscreen and an allow attribute with four tokens including fullscreen, and there is no sandbox attribute. The one genuinely mobile-friendly control on the page is a Play full screen link measuring 170 by 44 CSS pixels, which comfortably clears the 24-pixel floor and does sit above the fold at 390 by 844. It opens the game in a new tab rather than calling the Fullscreen API — requestFullscreen appears nowhere in the page's source — but for a phone player the practical effect is the right one: the game gets the whole tab instead of a fifth of the screen. It is also, by some distance, the least prominent thing on the page.

The counter-example, and it is not one of ours either

The Cube is the open-source Rubik's-cube project by Boris Sehovac (bsehovac/the-cube, built with Three.js; no licence is stated in the repository, so we host it with credit and link the source). It is not a SlowDen game and we do not claim it. It is here because it gets everything in this article right by construction.

We read its whole game file — js.js, 101,851 bytes, re-fetched with cache: 'no-store' — and counted its input handling.

Handler Occurrences
touchstart / touchmove / touchend 4 / 4 / 2
mousedown / mousemove / mouseup 3 / 2 / 2
keydown 0
Reads touches and changedTouches yes

Zero keyboard handlers. Both pointer paths bound, in parallel, with the touch path reading real touch lists rather than hoping a mouse event will be synthesised for it. Its opening prompt is Double tap to start — an instruction that is true on a phone and true on a laptop, which is a harder thing to write than it looks.

And because it is self-hosted rather than embedded, its page is the game: the canvas measured 1,536 by 695, the document height equals the viewport height exactly, the body has overflow: hidden, and the page is not scrollable. There is no page below it to scroll to. The same game inside a portal embed would have got about a fifth of the screen and 3,596 pixels of page underneath it.

That contrast is the whole argument in one comparison. Nothing about touch support requires a big screen. It requires that somebody decided which inputs the game has before deciding how many buttons to draw.

Eight things to change, roughly in order of payoff

  1. Open your build on an actual phone and finish the first thirty seconds without a keyboard. Not a narrow desktop window — that still delivers key events and a mouse. If you cannot get through the first level, neither can a fifth of the people who find you.
  2. Count your inputs, then cut. Write down every distinct action your first level needs. If the list is longer than two simultaneous inputs, the phone version is a redesign, not a control overlay. Deciding that in week one is cheap.
  3. Set touch-action: none on your play surface and register touch listeners with { passive: false }. Otherwise the browser keeps the pan gesture and the host page scrolls out from under your player.
  4. Handle pointer events, not mouse events. A single pointerdown / pointermove path covers mouse, touch and pen. If you prefer explicit touch handlers, bind both and read changedTouches rather than assuming a synthesised mouse event will arrive.
  5. Design your first frame for 331 by 207 CSS pixels. Not as a stretch goal — as the default. Set your editor to that size once and look at your title screen in it. Most title screens do not survive the experience, and it is better to find that out yourself.
  6. Say what the game needs, inside the game. One line on the title screen: "drag to steer, tap to jump", or honestly, "this one needs a keyboard". Do not rely on the host page — we have just shown ours saying the opposite 14 times.
  7. Make every target at least 24 by 24 CSS pixels, and much larger for anything held down. The 24-pixel figure is WCAG 2.2's minimum, not a target.
  8. If your game really is desktop-only, say so proudly rather than quietly. Two players sharing one keyboard is a genuinely good design. It just needs a label, so a phone player finds out before they start rather than after.

Run this on your own listing

Paste this into the console on any portal page that embeds your game. It reports the geometry your game actually gets and whether anything on the page is reserving touch gestures for it.

(() => {
  const f = document.querySelector('iframe');
  if (!f) return console.log('no iframe on this page');
  const r  = f.getBoundingClientRect();
  const vw = innerWidth, vh = innerHeight;
  const doc = document.documentElement.scrollHeight;
  const restricted = [...document.querySelectorAll('*')]
    .filter(e => getComputedStyle(e).touchAction !== 'auto').length;
  console.table({
    'viewport':                 vw + ' x ' + vh,
    'your frame':               Math.round(r.width) + ' x ' + Math.round(r.height),
    'frame area':               Math.round(r.width * r.height) + ' px2',
    '% of viewport area':       ((r.width * r.height) / (vw * vh) * 100).toFixed(1) + '%',
    'page above the frame':     Math.round(r.top) + ' px',
    'page below the frame':     Math.round(doc - r.bottom) + ' px',
    'frame as % of page':       ((r.height / doc) * 100).toFixed(2) + '%',
    'elements reserving touch': restricted,
    'allowfullscreen':          f.hasAttribute('allowfullscreen'),
    'sandboxed':                f.hasAttribute('sandbox')
  });
})();
Enter fullscreen mode Exit fullscreen mode

If elements reserving touch comes back as 0 and page below the frame is in the thousands, then a swipe your game does not claim will scroll your game off the screen. That is the host's layout and your touch-action, together.

Two ways this measurement tried to fool us

Both worth knowing if you automate any of this, because both produced a confident wrong answer.

A hidden element that every DOM check called visible. We tried to test whether The Cube starts from touch input by watching its Double tap to start prompt disappear. After a real double-click the prompt was plainly gone from the screen — and every programmatic check still reported it present. Its computed display was block, its visibility was visible, checkVisibility() returned true, and innerText still contained the string. The element was faded to opacity: 0.000107. Only the rendered pixels told the truth. If you are asserting on game state from the DOM, assert on something the game sets, never on whether text looks gone.

Synthetic events did not drive the game, and the control proved it. Our dispatched TouchEvent pairs did nothing. That looks like evidence the game ignores touch — until you run the control, which we did: synthetic MouseEvent pairs did nothing either, on the canvas and on the document, while a real trusted double-click started the game immediately. So the failure was our dispatch method, not the game's input handling. The touch test is therefore inconclusive and we are reporting it as inconclusive. The reliable evidence for The Cube's touch support is the handler count read out of its source, above — not our test.

A negative result from an automated harness is worth roughly nothing without a positive control run the same way.

Where these numbers came from, and their limits

  • Documentation, not games. Said once more because it bounds everything: the 87 / 68 / 26 split describes what developers wrote about their controls. It is not a test of any game, and no game named here is being called broken.
  • The 26 is a hand-read list, and its judgement calls are written down. Five entries were deliberately excluded, each with a reason — one control list is actually an FAQ that names no input at all; one says "click on the arrows", where the arrows are objects in the puzzle rather than keys; three are click-driven with a key as a secondary extra, so a tap substitutes.
  • Phone rows are layout measurements, not device tests. A same-origin frame at 390 by 844 gives real CSS-pixel geometry, but no mobile user agent, no device pixel ratio, no collapsing address bar and no real finger. Treat them as the layout a phone would get, not as a device report.
  • Source versus live, labelled. The registry holds 200 entries; 120 are live on the site today. Of the 26 keyboard-driven games, 14 are live and 12 are not yet published. The 14-of-14 page-copy finding was measured against live pages only.
  • One browser. Chrome on Windows. Safari in particular handles touch defaults and viewport units differently; do not assume these figures transfer without testing.
  • The 120-pixel thumb zone is an illustration, not a measurement, and is labelled as such where it appears. The 24-by-24 figure is not an illustration — it is quoted from WCAG 2.2 SC 2.5.8.
  • Credits. Every game named is credited to its studio as recorded on its own public listing. All of them are partner titles listed on SlowDen through Playgama; none was made by SlowDen. The Cube is by Boris Sehovac, open source, self-hosted with credit and a link to the repository. Playgama, Chrome, Three.js, W3C and MDN are named for accuracy only; no affiliation or endorsement is implied.

If you make browser games

SlowDen lists browser games and takes submissions from developers. If you have a playable web build, you can send it to us — and if it needs a keyboard, tell us, because we would rather write that on the page than let a player find out.

Slow Cook is our creative challenge for developers: pick a challenge, build a small game around it, and submit it before the round closes on 31 August 2026. Selected entries may receive a shoutout on SlowDen's social accounts and a chance to be featured — nothing is guaranteed, and every entry is read by a person.

Related reading

Top comments (0)