Frogger
Seven lanes of traffic between you and the far bank. Get across as many times as you can on three lives.
- Arcade
- Reflex
The biggest of the four ports, and the one whose central mechanic was quietly broken.
The collisions were being checked against the past
The game loop looked like this:
useEffect(() => {
const gameLoop = setInterval(() => {
setVehicles(prev => prev.map(/* move everything */));
checkCollisions();
}, 50);
return () => clearInterval(gameLoop);
}, [gameStarted, frogPos]);
checkCollisions reads vehicles from the closure it was created in. That
closure is only rebuilt when the effect re-runs — and the effect’s
dependencies are gameStarted and frogPos. Vehicle positions aren’t in
there.
So the traffic moved sixty times a second while the collision check kept testing the frog against where the cars were the last time the frog moved. Stand still and you were immortal. Hop once and you were briefly checked against a stale frame.
There was a second bug stacked on it: the check was a forEach with no early
exit, so a single frame overlapping three vehicles subtracted three lives at
once — the whole game, gone in one step.
The port runs movement and collision in the same requestAnimationFrame pass
against live positions, and stops at the first hit.
Added, because it needed it
A short window of invulnerability after respawning. With the collisions actually working, landing back on the start line under a passing bus meant an instant second death, and sometimes a third. Seven hundred milliseconds is enough to be fair without being exploitable — and the same grace applies after a successful crossing.
Kept
The 800×480 board, 40px grid, seven road lanes with alternating direction, randomised vehicle count and speed per lane, and the three-heart budget are all the original’s. So are the vehicle SVGs, which are the nicest thing about it — seven silhouettes that read instantly at 40 pixels.
Arcade



