You Might Also Like
Frog Crossing Game — Free HTML CSS JS Snippet
Frog Crossing Game · Games · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Frog Crossing — Grid Hops, Traffic Lanes and a River You Have to Ride

Frogger is the definitive grid-hop game, and its brilliance is that it has *two opposite* hazard rules on the same board: on the road, touching a moving object kills you, and on the river, *not* touching one kills you. Teaching a player to read which rule applies to the row they're on — while every row scrolls at a different speed and direction — is the whole game, and it maps perfectly onto a four-way D-pad of discrete hops. This snippet builds it on one HTML5 <canvas>: hop up through four road lanes and four river lanes, ride the logs across the water, and fill all four homes at the top.
Discrete grid hops, not continuous movement
Unlike a runner or a platformer, the frog doesn't move smoothly — each button press is one hop of exactly one tile, fired on pointerdown through a tiny tap() helper. The frog tracks a grid row and a pixel x; hopping up or down changes the row, hopping left or right shifts x by one tile clamped to the board. Keyboard arrows and WASD call the same hop() function. Because movement is discrete, timing your hop into a gap is the core skill — you commit to a tile and can't take it back.
Two collision rules, chosen by row type
Every row carries a type — road, water, safe or goal — and update() branches on the frog's current row. On a road row, the frog dies if its rectangle overlaps any car (standard AABB overlap). On a water row, the logic inverts: the frog dies *unless* its centre is on a log, and when it is on one, the log's velocity is added to the frog's x every frame so it rides along — and if a log carries it off the edge of the screen, it drowns. That single inversion is the entire risk/reward of the river.
Independently scrolling lanes with wrap-around
Each moving row is configured with a signed speed, a piece length and a gap, and buildLanes() fills it with evenly spaced items that scroll and wrap: an item leaving one side re-enters on the other so the traffic and log flow is continuous and endless. Alternating lane directions and varied speeds create the staggered timing windows that make the crossing a puzzle rather than a straight sprint.
Homes, lives and scoring
The top row has four home slots. Landing the frog in an *empty* home fills it and resets the frog to the start; landing on the bank between homes, or on an already-filled one, costs a life. Filling all four wins the round. The frog has three lives, deaths reset it to the bottom, and the score rewards forward hops, reaching a home, and completing the board. The best score persists to localStorage, and all motion is delta-time scaled so lane speeds are consistent across refresh rates.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Paste this snippet's HTML, CSS and JS into an AI assistant like Claude and ask it to explain the row-typed collision branching, the road-kills / water-saves inversion, and the log-riding velocity transfer. It is a strong base to extend: ask for diving turtles that submerge on a cycle, crocodiles that occupy some homes, a per-life countdown timer, bonus flies that appear in a home for extra points, or additional lanes for a taller board. You could also ask it to add swipe-gesture controls as an alternative to the D-pad, or to port the loop into a React component with useRef and useEffect. Treat it as a working prototype to question and rebuild.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a mobile-friendly Frogger-style grid-crossing game on an HTML5 canvas in plain HTML, CSS and JavaScript — no frameworks.
Requirements:
- A frog that moves in discrete single-tile hops. Provide an on-screen four-way cross D-pad for touch AND keyboard arrows/WASD, both calling the same hop function. Each press is one committed hop (fired on pointerdown) — no held movement.
- Lay out the board in rows with types: a goal row with home slots, several water rows, a safe median, several road rows, and a start bank. Branch collision on the row type: on road rows the frog dies if it overlaps any car; on water rows the frog drowns UNLESS its centre is on a log, and while on a log the log's velocity is added to the frog each frame (and it drowns if carried off-screen).
- Give each moving lane a signed speed, piece length and gap, filled with evenly spaced items that scroll and wrap around the screen edges seamlessly.
- Put four home slots in the top row; landing in an empty home scores and resets the frog, landing on the bank between homes or on a filled home costs a life. Filling all four wins the round.
- Give three lives with death-resets, score forward hops / homes / completion, and use requestAnimationFrame with delta-time scaling so lane speeds are frame-rate-independent. Persist the high score in localStorage with defensive try/catch, and show start / game-over overlays.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Hop inTap "Hop in" to call startGame(), which builds the scrolling lanes, places the frog on the safe bank at the bottom, and starts the delta-time loop.
- 2Move one tile per tapTap ▲ ◀ ▶ ▼ (or arrow keys / WASD) to hop exactly one tile. Movement is discrete — each press commits you to a tile, so time your hops into the gaps rather than holding a direction.
- 3Cross the roadOn the dark road lanes, cars are deadly on contact. Wait on a safe tile, read the gap in the next lane, and hop through it. Each lane scrolls at a different speed and direction.
- 4Ride the riverOn the blue water lanes the rule flips: the river drowns you unless you are standing on a log. Hop onto a log and it carries you sideways — but if it carries you off the screen edge, you drown, so hop off in time.
- 5Fill all four homesLand the frog in one of the four empty home slots at the top to score and reset to the start. Landing on the bank between homes, or on a filled home, costs a life. Fill all four to win the round.
- 6Beat your bestYou have three lives; the score rewards forward progress, reaching homes, and completing the board, and persists in localStorage under frog-crossing-best. Tune lane speeds, gaps and piece lengths in the LANES config.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Frogger is a grid game: each press is a single, committed hop of exactly one tile, fired on pointerdown. The frog stores a grid row and a pixel x; up/down changes the row, left/right shifts x by one tile clamped to the board. This discreteness is the source of the tension — you cannot nudge or cancel a hop, so reading the gap before you commit is the whole skill.
The two hazard types have opposite rules, chosen by the row the frog is on. On a road row the frog dies if it overlaps any car. On a water row the logic inverts: the frog drowns unless its centre is on a log. When it is on a log, the log's velocity is added to the frog every frame so it rides along. That inversion is intentional and is the defining mechanic of the game.
While the frog is on a water row and standing on a log, the game adds that lane's signed speed to the frog's x position every frame, so it drifts with the log. If the drift carries the frog past the edge of the screen it drowns, which is why you must hop off a log before it reaches the far side rather than riding it all the way.
Each moving lane is built with evenly spaced items and a configured speed, length and gap. As items scroll, any that leave one side of the screen have the total lane span added or subtracted so they re-enter on the opposite side. This wrap-around recycling keeps a fixed number of items per lane while producing a continuous, endless flow of cars and logs.
Yes. The best score is stored in localStorage under frog-crossing-best and read on load, so it survives reloads and browser restarts on the same browser and origin. The read and write are wrapped in try/catch so the game still runs in sandboxed or private contexts where storage access can throw.