Game review

Coup Ahoo

3.8 / 10

Developer
Antti Haavikko
Publisher
Antti Haavikko (self-published — js13kGames 2024 competition entry)
Released
1 January 2024
Platforms
Web browser (desktop and mobile)
Genre
Roguelike, dice battler
Monetisation
Free, open source, no ads and no in-game purchases
Players
Single-player only

The short version

Coup Ahoo is a genuinely well-made 13-kilobyte roguelike on a desktop: roll for cargo dice, manage a bust risk that punishes greed, recruit a three-role crew, and cannon-duel your way through a hand-authored 13-ship fleet before the game opens into an infinite, scaling endgame. On a real touchscreen it does not work at all. Every action in the game is driven by a single `document.onmousedown` handler that trusts the browser to synthesise a mouse click from a tap, and the page ships with no `<meta name="viewport">` tag — so a phone browser renders it at a desktop-width layout, zooms the whole thing out to fit the screen, and the synthesised click lands at coordinates that don't correspond to anything the game drew. Ten direct taps on the visible Roll button, a real touch-and-hold, and a seven-point sweep across the entire canvas produced zero response. This is precisely the failure mode this shelf exists to catch, on a game that was js13kGames 2024's own Mobile category winner.

Biggest strengths

  • The core loop — roll cargo dice, decide whether to push your luck for a better total, then resolve combat as a straight dice-off against the next ship — creates real tension inside a handful of seconds per turn
  • Reading `scene.ts` confirms actual authored structure, not padding: a hand-tuned 13-ship fleet (`END_LEVEL = 13 * 2`) with difficulty and enemy crew scaling per encounter, followed by an infinite post-game that keeps scaling rather than just stopping
  • Three crew roles (quartermaster, cannoneer, navigator) give runs some build variety inside a tiny file size, and the "you can only take one" loot rule after a win forces real choices

Biggest weaknesses

  • Every one of those buttons is unreachable by touch. Ten direct taps on the Roll button's exact on-screen position, a real 700ms touch-and-hold through Chromium's CDP input pipeline, and a seven-point sweep across the whole visible canvas were tested on a simulated phone — none of them advanced the game past its opening line
  • The cause is traceable in the source: `index.ts`'s `resize()` reads `window.innerWidth` and `window.innerHeight` to size and CSS-transform the canvas, but the page has no viewport meta tag, so a mobile browser lays it out at roughly 980px wide and visually zooms the result down to fit the real screen — leaving the canvas positioned in a coordinate space the synthesised click coordinates never land in
  • Instrumenting the page directly showed the synthesised `mousedown` and `mousemove` do fire on every tap (so it is not simply "no touch events at all"), but they land on `<body>` and carry coordinates (336, 499 for a tap aimed at the Roll button) that fall outside the game's own 800x400 internal coordinate space — so `game.click(mouse)` runs every time, at a point that matches no button anywhere in the game

Who it's for: Desktop players who like a tight, stakes-driven dice roguelike in thirteen kilobytes

Our recommendation: Play it at a keyboard and mouse if you play it at all — do not expect it to work on your phone despite the "mobile" pedigree

3.8 / 10

Coup Ahoo asks you to roll dice for your ship's cargo, decide whether one more roll is worth the risk of busting, and then point a cannon at the next pirate dumb enough to get in your way — all inside 13 kilobytes, all before the loading spinner would normally have finished on a bigger game. It placed second overall at js13kGames 2024 and won the competition's own Mobile category outright. On a real simulated phone, tested exactly the way this shelf tests everything, it could not get past its own opening line no matter how it was tapped, held or swept. Those two facts sitting next to each other are the whole story here.

A tight, stakes-driven loop, at a keyboard

Start a run and you're rolling two dice for your ship's opening cargo, which doubles as both your hit points and your combat power. Every subsequent roll gives you the option to Keep what you have or push your luck for something better, and going too far risks landing on "the dreaded number" — a straight press-your-luck mechanic lifted from Farkle-style dice games and applied cleanly to a pirate skin. Combat resolves the same way in reverse: you and your opponent duel with dice, and the loser's ship takes the hit. Between fights, "Don't be greedy — you can only take one" loot drops force a real pick between two floating dice rather than letting you bank both, and recruiting from three crew roles — quartermaster, cannoneer, navigator, confirmed by reading ship.ts's availableRoles list — gives each run a little build identity despite the file size.

Reading scene.ts confirms this isn't just vibes: END_LEVEL is hard-set to 26 (thirteen ships, two turns each), the difficulty and enemy crew count scale explicitly per encounter, and clearing the full fleet drops you into an announced, genuinely infinite postgame ("∞ × N") that keeps raising the bar rather than looping the same fight. That's real authored structure for a competition entry built in a week, and across a dozen-plus turns on desktop every single button — Roll, Keep, Set Sail, Shoot — answered a mouse click instantly, with no missed hits and no visual glitches. As a desktop dice roguelike, this earns its placement.

The touch failure, and why it happens

None of that plays on a phone, and this was tested thoroughly rather than assumed. The game's entire input model is one line in index.ts: document.onmousedown = () => { ... game.click(mouse) }. There is no touchstart handler anywhere in the source — the developer is relying on mobile browsers to synthesise a mousedown from a tap, which real phones generally do. On the phone profile of this shelf's own testing harness — a real emulated touchscreen driving actual Chromium input, not a faked event — ten direct taps on the Roll button's exact measured position, a genuine 700-millisecond touch-and-hold dispatched through Chromium's CDP input pipeline, and a seven-point sweep across every region of the visible canvas were all tried. The game never left its opening screen ("Let's start by rolling for your cargo!") through any of it.

Instrumenting the page ruled out the obvious explanation first: the synthesised mousedown and mousemove events genuinely fire on every tap, so this is not simply "touch produces nothing." The actual break is in where those events land. index.ts sizes its 800x400 canvas by reading window.innerWidth and window.innerHeight and applying the result as a CSS transform: scale() — but the page carries no <meta name="viewport"> tag at all. Without one, a mobile browser doesn't treat the page as fitting the device's real 390px-wide screen; it lays it out at a default desktop-ish width (roughly 980px in this case) and then visually zooms the whole rendered page down to fit the actual screen. The canvas's on-screen size and position, computed from that unzoomed 980-wide layout, ends up in a completely different coordinate space from the tap coordinates a touch event reports. Checked directly: a tap aimed at the Roll button's visible position produced a synthesised click at (336, 499), on <body> rather than the canvas, and mouse.x/mouse.y ended up holding that same (336, 499) — a point that doesn't exist anywhere inside the game's own 800x400 coordinate space, since the game only ever draws buttons within y: 0–400. game.click(mouse) still runs on every tap; it just never intersects anything, because the numbers it's given describe a location the game never uses.

This is a one-line fix — the standard <meta name="viewport" content="width=device-width, initial-scale=1"> would very likely resolve it outright by keeping the layout and visual viewports in sync — and its absence is exactly the kind of thing that's invisible if you only ever test in a desktop browser's device-toolbar preview, which typically injects a sane default rather than reproducing this quirk. It's also exactly the kind of thing a jam's "mobile category" vote tends not to catch, since it depends on the tester's specific phone and browser zoom behaviour rather than being reliably reproducible from a screenshot.

Verdict

Judged as a dice roguelike for a keyboard and mouse, Coup Ahoo is sharp, well-paced, and does more with thirteen ships and a three-role crew system than its file size has any right to. That game is not what's being reviewed here, though — this shelf exists specifically to check whether a web game can be played with thumbs, and on that test Coup Ahoo scores zero, not "awkward" or "fiddly," but a complete, reproducible failure to register a single input on a real emulated touchscreen across seventeen separate attempts and three different techniques. A game that fails that test this thoroughly does not belong on a shelf built around the promise that everything on it works on a phone, regardless of what it won in a jam that judges by different rules. The controls score reflects that directly, and it is the reason the overall score sits well below what the desktop half of this game would otherwise earn on its own.

Pros and cons

Pros

  • The core loop — roll cargo dice, decide whether to push your luck for a better total, then resolve combat as a straight dice-off against the next ship — creates real tension inside a handful of seconds per turn
  • Reading `scene.ts` confirms actual authored structure, not padding: a hand-tuned 13-ship fleet (`END_LEVEL = 13 * 2`) with difficulty and enemy crew scaling per encounter, followed by an infinite post-game that keeps scaling rather than just stopping
  • Three crew roles (quartermaster, cannoneer, navigator) give runs some build variety inside a tiny file size, and the "you can only take one" loot rule after a win forces real choices
  • Runs at a clean, stable frame rate on both desktop and the phone-sized viewport, and the hand-drawn bean-pirate art reads clearly even letterboxed down to a strip of the screen
  • Confirmed on desktop, across roughly a dozen turns, that every button — Roll, Keep, Set Sail, Shoot — responds instantly and correctly to a mouse click, with no dead zones

Cons

  • Every one of those buttons is unreachable by touch. Ten direct taps on the Roll button's exact on-screen position, a real 700ms touch-and-hold through Chromium's CDP input pipeline, and a seven-point sweep across the whole visible canvas were tested on a simulated phone — none of them advanced the game past its opening line
  • The cause is traceable in the source: `index.ts`'s `resize()` reads `window.innerWidth` and `window.innerHeight` to size and CSS-transform the canvas, but the page has no viewport meta tag, so a mobile browser lays it out at roughly 980px wide and visually zooms the result down to fit the real screen — leaving the canvas positioned in a coordinate space the synthesised click coordinates never land in
  • Instrumenting the page directly showed the synthesised `mousedown` and `mousemove` do fire on every tap (so it is not simply "no touch events at all"), but they land on `<body>` and carry coordinates (336, 499 for a tap aimed at the Roll button) that fall outside the game's own 800x400 internal coordinate space — so `game.click(mouse)` runs every time, at a point that matches no button anywhere in the game
  • The one-fix nature of this makes it more frustrating rather than less: adding a single `<meta name="viewport" content="width=device-width, initial-scale=1">` tag is the standard remedy for exactly this class of bug, and it was not present in either the author's repo or the js13kGames-hosted build
  • There is no fullscreen prompt, no orientation lock and no alternate control scheme to fall back on — if the primary mouse-emulation path fails, there is nothing else to try

Category scores

Category scores are the reviewer's read on each area. They inform the overall score but do not average into it — the number at the top of the page is a judgement, not a calculation.

  • Gameplay 7 / 10
  • Visuals 6.5 / 10
  • Performance 7 / 10
  • Originality 6 / 10
  • Replayability 6 / 10