Lane Runner Endless Game — Free HTML CSS JS Snippet

Lane Runner Endless Game · Games · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Four discrete on-screen buttons (lane left/right, jump, slide) fired on pointerdown, plus a matching keyboard map — no held-input edge cases
Logical lane index for collision separated from an interpolated visual x-position for smooth lane changes
Three obstacle types, each requiring a different response: jump a barrier, slide under a gate, switch lanes past a block
Timed jump and slide windows so mistiming fails realistically, with a squash/lift animation on the runner
Continuously rising speed and tightening spawn gap driven by distance travelled
Delta-time scaled movement for frame-rate-independent speed, with scrolling lane dashes for a sense of motion
Distance-based live scoring and a persisted best via localStorage with defensive try/catch
Self-contained canvas rendering with start and game-over overlays — no assets or dependencies

About this UI Snippet

Lane Runner — An Endless Three-Lane Runner Built for Thumb Controls

Screenshot of the Lane Runner Endless Game snippet rendered live

Endless runners are the most touch-native game genre there is: the whole game is a handful of instant decisions — left, right, jump, slide — made against an obstacle stream that never stops speeding up. That maps onto four on-screen buttons with nothing left over, which makes this an ideal demonstration of *discrete-action* touch controls. This snippet builds a complete three-lane runner on a single HTML5 <canvas>, with an on-screen ◀ ▶ pair to change lanes and JUMP / SLIDE buttons for the two timed dodges, plus a full keyboard mapping.

Every control is a discrete tap, not a held state

Unlike a platformer where you *hold* to run, every input here is a one-shot event. Changing lanes nudges a target lane index; jumping and sliding start a fixed-duration timer. So all four on-screen buttons fire on pointerdown via a tiny tap() helper — there is no press-and-hold to manage and no stuck-input edge case, because nothing depends on the button still being down. The keyboard handlers call the exact same moveLane(), doJump() and doSlide() functions, so the two input methods are guaranteed to behave identically.

Lanes as an index, with smooth interpolation

The runner's lane is just an integer 0–2. moveLane() clamps it, and the runner's on-screen x-position eases toward the target lane centre every frame with a simple interpolation (curX += (targetX - curX) * k) rather than snapping — so a lane change reads as a quick slide, not a teleport. This split between the logical lane (used for collision) and the visual x (used for drawing) is a clean, reusable pattern for any grid-snapped movement that should still look fluid.

Three obstacle types that each demand a different verb

The obstacle stream teaches three distinct reactions. A green barrier must be jumped; an amber overhead gate must be slid under; a red block fills every lane but one, so it must be *avoided* by switching lanes. Collision is deliberately simple and readable: an obstacle only matters if it is in the runner's lane and inside the runner's vertical band, and then a jumpT/slideT timer being active is what lets you pass a barrier or gate. Any other case is a crash. Because the jump and slide are timed windows, mistiming them fails exactly as a real runner does — jumping too early lands you before the barrier.

A rising speed curve and distance scoring

Speed is a continuous function of distance travelled, and the gap between spawned obstacle rows shrinks as distance grows, so the game gets relentlessly faster and denser. The score is the distance travelled, shown live, and the best is persisted to localStorage. Movement is delta-time scaled so the runner covers the same ground per second on any refresh rate.

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 all-taps control model, the split between the logical lane index and the interpolated visual x, and the timed jump/slide collision windows. It is a strong base to extend: ask for collectible coins with a magnet power-up, a shield that absorbs one hit, a combo multiplier for near-misses, curved or forking tracks, sprite-sheet run/jump/slide animation, or a difficulty that ramps in named stages. You could also ask it to add haptic feedback on mobile via the Vibration API, or to port the loop into a React component using useRef and useEffect for the animation-frame lifecycle. Treat it as a working prototype to question and rebuild rather than a finished game.

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:

text
Build a mobile-friendly endless three-lane runner game on an HTML5 canvas in plain HTML, CSS and JavaScript — no frameworks.

Requirements:
- A runner that auto-advances down a three-lane track. The player changes lanes and performs a jump and a slide. Provide on-screen buttons for lane-left, lane-right, JUMP and SLIDE, AND keyboard controls (arrows + Space). Every control is a discrete single tap fired on pointerdown — do not use press-and-hold. Both input methods must call the same action functions.
- Represent the current lane as an integer used for collision, but draw the runner at a separate x-position that eases toward the target lane centre each frame so lane changes animate smoothly instead of snapping.
- Include three obstacle types: a barrier that must be jumped, an overhead gate that must be slid under, and a solid block that fills all lanes but one and must be avoided by changing lanes. Jump and slide are timed windows (fixed-duration timers) so mistiming fails; only one of jump/slide can be active at a time.
- Use requestAnimationFrame with delta-time scaling. Make the game speed and obstacle spawn density both increase continuously with distance travelled.
- Score by distance travelled, shown live, and persist the best score in localStorage with defensive try/catch. Show start and game-over overlays, scrolling lane dividers for a sense of motion, and a squash/lift animation on the runner for slide and jump.

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

  1. 1
    Start runningTap "Run" to call startGame(), which resets the runner to the centre lane, clears the obstacle stream, and starts the delta-time requestAnimationFrame loop. The runner auto-advances; you only steer and dodge.
  2. 2
    Switch lanesTap ◀ or ▶ (or the Left/Right arrow keys) to change lanes. Each tap nudges the lane index; the runner eases smoothly to the new lane rather than snapping.
  3. 3
    Jump the green barriersTap JUMP (or ↑ / Space) as a low green barrier approaches. Jump is a timed window (a jumpT countdown), so timing matters — jump too early and you land before clearing it.
  4. 4
    Slide under the amber gatesTap SLIDE (or ↓) for the overhead amber gates. The runner squashes for the slide window; you can only jump or slide one at a time.
  5. 5
    Dodge the red blocksRed blocks fill every lane but one and cannot be jumped or slid — you must switch to the open lane in time. This is what forces lane changes into the rhythm.
  6. 6
    Beat your distanceThe score is the distance travelled and rises continuously; the game speeds up and obstacles pack tighter as you go. Your best persists in localStorage under lane-runner-best. Tune speed, JUMP_MS, SLIDE_MS and the spawn gap in the JS.

Real-world uses

Common Use Cases

Discrete-tap touch control template
The all-taps control scheme (lane nudges plus timed jump/slide) fired on pointerdown is a reusable pattern for any endless runner or rhythm-style touch game, with no press-and-hold state to manage.
Instantly playable casual game for entertainment sites
An endless runner is learnable in seconds and has infinite replayability — a strong embeddable demo for a games portal, blog, or app landing page with no sign-up or download.
Engaging empty-state, loading or 404 distraction
With no network dependency it starts instantly, making it a lively filler for an error page or slow-loading screen, like the Dodge the Falling Blocks Game.
Teaching lane-snap movement and timed-window mechanics
The separation of a logical lane index from an interpolated visual position, and the timer-based jump/slide windows, are compact references for two common game-feel techniques.
Starting point for a fuller endless runner
The obstacle stream and speed curve generalise well: add coins and power-ups, a shield, curved roads, sprite art, or a combo multiplier without touching the delta-time loop.
Canvas motion and parallax reference
The scrolling dashed lane dividers, runner shadow, and squash/lift animation are directly reusable techniques for conveying speed and weight in any canvas scene.

Got questions?

Frequently Asked Questions

Every action in an endless runner is instantaneous: a lane change nudges an index, and jump/slide start a fixed-duration timer. None of them depend on the button staying pressed, so each on-screen button simply fires its function on pointerdown. That removes any need to track button release and eliminates the "stuck input" edge case entirely, while the keyboard handlers call the identical functions.

The lane is a logical integer used for collision, but the runner is drawn at a separate x-position that eases toward the target lane centre each frame with curX += (targetX - curX) * k. That interpolation turns an instant index change into a visible quick slide. Keeping the logical lane and the visual position separate is a clean pattern for any movement that snaps to a grid but should still animate.

Jump and slide are timed windows, not toggles. Pressing JUMP starts a jumpT countdown, and you only clear a barrier if that timer is still active when the barrier reaches the runner band. Pressing too early means the window has expired by the time you reach the obstacle. Red blocks additionally cannot be jumped or slid at all — the only way past them is to be in the open lane.

Both the speed and the spawn density scale with distance travelled. Speed is speed = 3.4 + distance * 0.0009, so the runner accelerates continuously, and the gap between spawned obstacle rows shrinks toward a floor as distance grows. Together they make later stages both faster and busier, which is what gives an endless runner its escalating pressure.

Yes. The best distance score is stored in localStorage under lane-runner-best and read on load, so it persists across 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.