๐ŸŽฎ Get a game ยท $19

๐ŸŽ๏ธ Free game 11 of 20 ยท copy & paste

Car Racing Game in HTML & JavaScript: Free Code + Demo

A top-down car racing game: change lanes to dodge the traffic as the road gets faster. The cars are drawn in code, and a lane is always left open so it stays fair. Copy the code below, save it as car-racing.html and open it in any browser.

Type
Arcade
Level
Easy
Code
134 lines, 1 file
Libraries
None
Works on
Phones & computers
You learn
Lane movement and a difficulty curve

โ–ฒ Play it right here. โ† โ†’ or A D, tap the left or right half, or swipe.

What's in this free Car Racing game

The code (134 lines, one file)

car-racing.htmlโฌ‡ Download
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Car Racing Game</title>
<!--
  Free HTML Car Racing game โ€” one file, no libraries, no images. Save it as car-racing.html and open it in a browser.
  From htmlgamescode.com (free to use in your own projects).
  Dodge the traffic. Arrow keys / A D, tap the left or right half of the road, or swipe to change lanes.
-->
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body {
    min-height: 100vh; display: flex; align-items: center; justify-content: center; background: #1b1446;
    font-family: system-ui, -apple-system, "Segoe UI", sans-serif; user-select: none; -webkit-user-select: none; touch-action: none;
  }
  canvas { display: block; width: min(96vw, 400px, calc((100vh - 16px) * 2 / 3)); aspect-ratio: 2 / 3; border-radius: 18px; background: #3aa35b; cursor: pointer; }
</style>
</head>
<body>
<canvas id="game" width="360" height="540"></canvas>

<script>
  // ---- settings: change these to make it your own ----
  const LANES = 3;               // number of lanes
  const START_SPEED = 5;         // road speed at the start
  const SPEED_UP = 0.003;        // speed added every frame
  const MAX_SPEED = 13;          // top speed
  const SPAWN_EVERY = 52;        // frames between new cars at the start
  const CAR_COLORS = ["#ffd23f", "#3fa6ff", "#b46bff", "#ff8a3d", "#ffffff"];   // traffic colors (yours is red)

  const canvas = document.getElementById("game"), ctx = canvas.getContext("2d");
  const W = canvas.width, H = canvas.height, ROAD_X = 50, ROAD_W = W - 100, LANE_W = ROAD_W / LANES, CAR_W = 44, CAR_H = 76;
  const laneX = l => ROAD_X + LANE_W * l + LANE_W / 2;
  const load = k => { try { return Number(localStorage.getItem(k)) || 0; } catch { return 0; } };
  const save = (k, v) => { try { localStorage.setItem(k, v); } catch {} };
  let player, cars, speed, road, score, frame, state, best = load("racing-best");

  function reset() {
    player = { lane: Math.floor(LANES / 2), x: laneX(Math.floor(LANES / 2)), y: H - 110 };
    cars = []; speed = START_SPEED; road = 0; score = 0; frame = 0; state = "ready";
  }

  function steer(dir) {
    if (state === "over") { if (frame > 30) reset(); return; }
    if (state === "ready") state = "play";
    player.lane = Math.max(0, Math.min(LANES - 1, player.lane + dir));
  }

  function update() {
    frame++;
    if (state !== "play") return;
    speed = Math.min(MAX_SPEED, speed + SPEED_UP);
    road = (road + speed) % 60;
    player.x += (laneX(player.lane) - player.x) * 0.25;           // glide into the lane

    if (frame % Math.max(22, Math.round(SPAWN_EVERY * START_SPEED / speed)) === 0) {
      const lane = Math.floor(Math.random() * LANES);
      // never block every lane at the same height
      const blocked = new Set(cars.filter(c => c.y < CAR_H * 1.6).map(c => c.lane));
      if (!blocked.has(lane) && blocked.size < LANES - 1) cars.push({ lane, y: -CAR_H, color: CAR_COLORS[Math.floor(Math.random() * CAR_COLORS.length)], v: 0.35 + Math.random() * 0.25 });
    }
    for (const c of cars) {
      c.y += speed * c.v + speed * 0.5;
      if (!c.passed && c.y > player.y + CAR_H) { c.passed = true; score++; }
      if (Math.abs(laneX(c.lane) - player.x) < CAR_W - 6 && Math.abs(c.y - player.y) < CAR_H - 8) {
        state = "over"; frame = 0;
        if (score > best) { best = score; save("racing-best", best); }
      }
    }
    cars = cars.filter(c => c.y < H + CAR_H);
  }

  function car(x, y, color) {
    ctx.fillStyle = "rgba(0,0,0,.25)"; ctx.fillRect(x - CAR_W / 2 + 4, y + 6, CAR_W, CAR_H);   // shadow
    ctx.fillStyle = color; ctx.beginPath();
    ctx.roundRect ? ctx.roundRect(x - CAR_W / 2, y, CAR_W, CAR_H, 10) : ctx.rect(x - CAR_W / 2, y, CAR_W, CAR_H);
    ctx.fill();
    ctx.fillStyle = "#1b1446";
    ctx.fillRect(x - CAR_W / 2 + 7, y + 14, CAR_W - 14, 14);       // windscreen
    ctx.fillRect(x - CAR_W / 2 + 7, y + CAR_H - 22, CAR_W - 14, 10);   // rear window
    ctx.fillStyle = "#222";
    [[-1, 10], [1, 10], [-1, CAR_H - 24], [1, CAR_H - 24]].forEach(([s, wy]) => ctx.fillRect(x + s * (CAR_W / 2) - (s > 0 ? 0 : 4), y + wy, 4, 14));
  }

  function draw() {
    ctx.fillStyle = "#3aa35b"; ctx.fillRect(0, 0, W, H);
    ctx.fillStyle = "#4b4b5a"; ctx.fillRect(ROAD_X, 0, ROAD_W, H);
    ctx.fillStyle = "#fff"; ctx.fillRect(ROAD_X - 6, 0, 6, H); ctx.fillRect(ROAD_X + ROAD_W, 0, 6, H);
    for (let l = 1; l < LANES; l++) for (let y = road - 60; y < H; y += 60) ctx.fillRect(ROAD_X + LANE_W * l - 3, y, 6, 32);
    cars.forEach(c => car(laneX(c.lane), c.y, c.color));
    car(player.x, player.y, "#ff4d6d");

    ctx.fillStyle = "#fff"; ctx.font = "800 18px system-ui, sans-serif"; ctx.textAlign = "left";
    ctx.fillText(`Score ${score}`, 12, 28);
    ctx.textAlign = "right"; ctx.fillText(`${Math.round(speed * 12)} km/h`, W - 12, 28);
    ctx.textAlign = "center";
    if (state !== "play") {
      ctx.fillStyle = "rgba(10,6,36,.7)"; ctx.fillRect(0, H / 2 - 90, W, 170); ctx.fillStyle = "#fff";
      ctx.font = "900 36px system-ui, sans-serif"; ctx.fillText(state === "ready" ? "Car Racing" : "Crash! ๐Ÿ’ฅ", W / 2, H / 2 - 36);
      ctx.font = "700 17px system-ui, sans-serif";
      ctx.fillText(state === "ready" ? "Tap left / right or use the arrows" : `Score ${score} ยท Best ${best}`, W / 2, H / 2 + 2);
      if (state === "ready" || frame > 30) ctx.fillText(state === "ready" ? "to change lanes" : "Tap to race again", W / 2, H / 2 + 34);
    }
  }

  let last = 0, acc = 0;
  function loop(t) {
    acc += Math.min(100, t - (last || t)); last = t;
    while (acc >= 1000 / 60) { update(); acc -= 1000 / 60; }
    draw();
    requestAnimationFrame(loop);
  }

  let sx = null;
  canvas.addEventListener("pointerdown", e => { e.preventDefault(); sx = e.clientX; });
  canvas.addEventListener("pointerup", e => {
    if (sx === null) return;
    const r = canvas.getBoundingClientRect(), dx = e.clientX - sx;
    sx = null;
    if (Math.abs(dx) > 25) steer(dx > 0 ? 1 : -1);                       // swipe
    else steer(e.clientX - r.left < r.width / 2 ? -1 : 1);               // tap left or right half
  });
  document.addEventListener("keydown", e => {
    if (e.key === "ArrowLeft" || e.key === "a") { e.preventDefault(); steer(-1); }
    if (e.key === "ArrowRight" || e.key === "d") { e.preventDefault(); steer(1); }
    if (e.key === " " && state !== "play") { e.preventDefault(); steer(0); }
  });
  reset();
  requestAnimationFrame(loop);
</script>
</body>
</html>

How to use it

  1. Click Copy code (or Download).
  2. Paste it into any text editor and save it as car-racing.html.
  3. Open the file in Chrome, Safari, Firefox or Edge. That's it, it runs offline.

How the code works

Lanes

The player car has a target lane and glides towards its x position a bit every frame, which makes the lane change smooth.

Traffic

New cars appear in a random lane, but never if that would block every lane at the same height.

Scrolling road

Only the lane markings move; their offset is the road position modulo the gap between them.

Make it your own

All the settings are at the top of the script. Change a value, save and reload:

SettingDefaultWhat it does
LANES3number of lanes
START_SPEED5road speed at the start
SPEED_UP0.003speed added every frame
MAX_SPEED13top speed
SPAWN_EVERY52frames between new cars at the start
CAR_COLORSa listtraffic colors (yours is red)

Put it on your website

Upload car-racing.html next to your page and add this where the game should appear:

<iframe src="car-racing.html" width="460" height="620" style="border:0;max-width:100%" title="Car Racing game"></iframe>

FAQ

Is this Car Racing code really free?

Yes. Copy it and use it in your own website, school project or app, change it as much as you like and publish it, even commercially. A link back to HTML Games Code is welcome but not required.

How do I run the Car Racing game?

Copy the code, paste it into a text editor, save the file as car-racing.html and double-click it. It opens in your browser and works offline; there is nothing to install.

What are the controls?

โ† โ†’ or A D, tap the left or right half, or swipe.

Does it work on phones?

Yes. It has touch controls and fits a phone screen, and it works with a keyboard and mouse on a computer.

Can I put it on my website?

Yes. Upload car-racing.html and link to it, or show it inside an iframe on any page (see "Put it on your website" above).

Can I turn it into an Android app?

Yes. Any HTML game can be packaged as an Android APK. Our HTML Games APK Builder does it for our premium games, or you can use a tool like htmltoapk.com for this file.

More free HTML games