Games › Dev log

One script that makes every game work on a phone

· 5 min read

We expect a lot of PLAYPILE to be played on phones, and PLAYPILE's games were not all written with a phone in mind. The small arcade games share an engine that was touch-first from day one. The big ones were not: APEX and NASCAR are driven with the arrow keys, DUNGEON QUEST wants WASD plus six more keys, and on a phone those games simply sat there. This post is about how we fixed that for the whole site at once, without opening most of the games.

Where we started

The quick games run on a small shared engine. Each draws at a fixed virtual resolution and the canvas is scaled to fit the window, so "the paddle is 90 wide" means the same thing on a big monitor and a phone held upright, and a mouse press, a finger and the space bar are the same event. That covered the small games. Everything else had its own input code, written for its own reasons. Fixing them one at a time would have meant touching dozens of folders, each a chance to break something that works on a desktop.

One helper, loaded first

Instead, the build step that assembles the site now adds one line to every game's page: a script tag for a shared file, mobile.js, placed at the very top of the head so it runs before the game's own code. The first thing the helper does is check whether it is on a touch device with a coarse pointer. On a desktop it stops there and does nothing at all, so the desktop versions of the games are exactly what they were.

On a phone or tablet it does four jobs.

1. Stop the page fighting the game

A game in a browser is still a web page, and a web page wants to zoom when you double-tap, scroll when you drag, and bounce when you reach the edge. The helper sets the viewport so there is no pinch or double-tap zoom, turns off overscroll and scroll-chaining, stops text from being selected under a thumb, and removes the grey flash that phones draw over a tapped element.

2. Draw fewer pixels

Modern phones report a device pixel ratio of 3 or more, and most 3D games honour it, which means rendering nine times as many pixels as the screen's nominal size. The helper caps the ratio the game sees at 1.5. On a 3x phone that is a quarter of the pixels, and on a small screen the difference is hard to spot.

It also changes two things about every WebGL context a game creates: it turns off multisample antialiasing, and it reports a maximum texture size of 2048. Most of our 3D games are built on three.js, which reads that limit and clamps its shadow maps and large textures to fit. The result is lighter shadows and less memory, without a single line changed in any game. The trade is real: shadow edges are softer on a phone than on a desktop.

3. A pad for the keyboard games

Five keyboard-driven games get an on-screen pad. It is a d-pad on the left and a few round buttons on the right, and each button sends the same keyboard events the game already listens for.

  • APEX and NASCAR: arrows to drive, GEAR+ and GEAR- for E and Q, and a pause button.
  • EXPOSURE: arrows, FIRE, MATCH and OK.
  • PENGUIN DASH: arrows, JUMP, OK and pause.
  • DUNGEON QUEST: a WASD pad, plus attack, jump, block, special, use and bag.

Two keyboard-and-mouse games are deliberately left out. HIGHRISE and KICKBACK aim with the mouse all the time, and a d-pad cannot stand in for that, so we would rather say plainly that they are desktop games than ship a pad that makes them worse.

Two details took the most care. Older game code often reads keyCode rather than the newer code property, and a synthetic keyboard event does not fill that in by default, so the helper defines it on each event by hand. And a button has to let go of its key when your thumb slides off it, not only when you lift the thumb. Otherwise a driver whose thumb drifted off the accelerator would keep accelerating into a wall. The buttons are 58 pixels across for the d-pad, 64 for actions and 44 for pause.

4. Ask for the right orientation

The pad games need landscape: two thumbs, a pad each side, and a wide view of the road. In portrait they show a full-screen prompt, "Turn your phone sideways to play", which goes away by itself once you do. The helper counts the screen as portrait when its height is more than 1.1 times its width.

Some games are happier upright. STACK, HELIX DROP, WORD BLITZ, SUBWAY RUN, TUNNEL DIVE and twenty others are on a list of 25 that get no hint at all. Everything else gets a small tip at the bottom of the screen, "turn your phone sideways for a bigger view", which fades after four seconds and never blocks play.

Rotating a phone also changes the window size in stages, as the browser's address bar slides in and out. Games re-measure on resize, but the last resize event can arrive before the browser has settled. So after a rotation the helper waits 250 milliseconds and fires one more resize.

Why one file and not ninety

The obvious alternative was to add touch controls to each game. We did that where it mattered most: DRIFTER and SPIKE were reworked for touchscreens on 7 October, and HOOPS got a landscape layout in September. But a shared helper fixes every game on the same day, leaves the desktop games exactly as they were tested, and turns the next thing we learn about phones into one change in one file.

What it does not do

An on-screen d-pad makes a keyboard game playable on a phone. It does not make it a phone game. Steering an F1 car with a thumb on glass is harder than with a key. The games that are best on a phone are still the ones designed around one finger from the start.

The site around the games is checked on phones too. Our readiness script loads the home page at 390 and 360 pixels wide, roughly a current iPhone and a common Android, and fails if the page can be scrolled sideways by more than two pixels.

More from the dev log