
80s-style arcade racing games
Prompt
Create a fun arcade racer in JS. The game should be set in a city and be fast-paced
A system prompt was added to support web rendering
Here is a fun, fast-paced arcade racing game. Use the **Left** and **Right** arrow keys to dodge traffic. The longer you survive, the faster you go and the higher your score!
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>City Arcade Racer</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #1a1a2e;
color: #e0e0e0;
font-family: 'Press Start 2P', cursive;
overflow: hidden;
}
#game-container {
position: relative;
box-shadow: 0 0 30px rgba(0, 255, 255, 0.5);
border: 3px solid #00ffff;
background-color: #0d0d1e;
}
canvas {
display: block;
background-color: #222; /* Road color */
}
.ui-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
cursor: pointer;
backdrop-filter: blur(5px);
}
.ui-overlay h1 {
font-size: 2.5em;
color: #00ffff;
text-shadow: 0 0 15px #00ffff;
margin-bottom: 20px;
}
.ui-overlay p {
font-size: 1.2em;
margin-top: 20px;
animation: blink 1.5s linear infinite;
}
#score-display {
position: absolute;
top: 15px;
left: 15px;
font-size: 1.5em;
color: #ff00ff;
text-shadow: 0 0 10px #ff00ff;
}
.hidden {
display: none;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="gameCanvas"></canvas>
<div id="score-display">SCORE: 0</div>
<div id="startScreen" class="ui-overlay">
<h1>Neon Racer</h1>
<p>Click to Start</p>
<p style="font-size: 0.8em; margin-top: 40px; animation: none;">Use Arrow Keys to Dodge</p>
</div>
<div id="gameOverScreen" class="ui-overlay hidden">
<h1>Game Over</h1>
<p id="finalScore"></p>
<p>Click to Restart</p>
</div>
</div>
<script>
// --- Game Setup ---
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreDisplay = document.getElementById('score-display');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const finalScoreDisplay = document.getElementById('finalScore');
const gameWidth = 400;
const gameHeight = 600;
canvas.width = gameWidth;
canvas.height = gameHeight;
let gameState = 'start'; // 'start', 'playing', 'gameOver'
let keys = {};
let score = 0;
let gameSpeed = 3;
// --- Audio Setup (Web Audio API for no external files) ---
let audioCtx;
let engineSound, engineGain;
const createAudio = () => {
if (audioCtx) return;
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
// Engine Sound
engineSound = audioCtx.createOscillator();
engineGain = audioCtx.createGain();
engineSound.type = 'sawtooth';
engineSound.frequency.setValueAtTime(50, audioCtx.currentTime);
engineGain.gain.setValueAtTime(0, audioCtx.currentTime);
engineSound.connect(engineGain).connect(audioCtx.destination);
engineSound.start();
};
const playCrashSound = () => {
if (!audioCtx) return;
const crashGain = audioCtx.createGain();
crashGain.gain.setValueAtTime(0.5, audioCtx.currentTime);
crashGain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 1);
const noise = audioCtx.createBufferSource();
const buffer = audioCtx.createBuffer(1, audioCtx.sampleRate * 1, audioCtx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < data.length; i++) {
data[i] = Math.random() * 2 - 1;
}
noise.buffer = buffer;
noise.connect(crashGain).connect(audioCtx.destination);
noise.start();
}
// --- Game Objects ---
const player = {
width: 40,
height: 60,
x: gameWidth / 2 - 20,
y: gameHeight - 80,
speed: 5,
color: '#00ffff',
trail: []
};
let obstacles = [];
let roadLines = [];
let buildings = [];
// --- Event Listeners ---
window.addEventListener('keydown', (e) => { keys[e.key] = true; });
window.addEventListener('keyup', (e) => { keys[e.key] = false; });
window.addEventListener('click', () => {
// Audio context must be started by user interaction
createAudio();
if (gameState === 'start') {
gameState = 'playing';
startScreen.classList.add('hidden');
scoreDisplay.classList.remove('hidden');
engineGain.gain.linearRampToValueAtTime(0.1, audioCtx.currentTime + 0.5);
gameLoop();
} else if (gameState === 'gameOver') {
resetGame();
gameState = 'playing';
gameOverScreen.classList.add('hidden');
scoreDisplay.classList.remove('hidden');
engineGain.gain.linearRampToValueAtTime(0.1, audioCtx.currentTime + 0.5);
gameLoop();
}
});
// --- Helper Functions ---
const random = (min, max) => Math.random() * (max - min) + min;
function resetGame() {
score = 0;
gameSpeed = 3;
player.x = gameWidth / 2 - player.width / 2;
player.trail = [];
obstacles = [];
roadLines = [];
buildings = [];
initScenery();
}
function initScenery() {
for (let i = 0; i < gameHeight / 50; i++) {
createRoadLine(i * 60);
}
for (let i = 0; i < gameHeight / 80; i++) {
createBuilding(i * 100);
}
}
function isColliding(rect1, rect2) {
return rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.y + rect1.height > rect2.y;
}
// --- Object Creation ---
function createObstacle() {
const laneWidth = gameWidth / 4;
const lane = Math.floor(random(0, 4));
const carWidth = 40;
const carHeight = 60;
const colors = ['#ff00ff', '#ff8c00', '#ffff00', '#7cfc00'];
obstacles.push({
width: carWidth,
height: carHeight,
x: lane * laneWidth + (laneWidth - carWidth)/2,
y: -carHeight,
color: colors[Math.floor(random(0, colors.length))]
});
}
function createRoadLine(yPos) {
roadLines.push({
x: gameWidth / 2 - 2.5,
y: yPos,
width: 5,
height: 30
});
}
function createBuilding(yPos) {
const width = random(40, 80);
const height = random(80, 200);
const side = Math.random() > 0.5 ? 'left' : 'right';
buildings.push({
x: side === 'left' ? -10 : gameWidth - width + 10,
y: yPos,
width: width,
height: height
});
}
// --- Update Logic ---
function update() {
// Player movement
if (keys.ArrowLeft && player.x > 0) player.x -= player.speed;
if (keys.ArrowRight && player.x < gameWidth - player.width) player.x += player.speed;
// Player trail
player.trail.push({ x: player.x, y: player.y, width: player.width, opacity: 1 });
player.trail.forEach(t => t.opacity -= 0.05);
player.trail = player.trail.filter(t => t.opacity > 0);
// Update road lines and buildings (scrolling)
[...roadLines, ...buildings].forEach(item => {
item.y += gameSpeed;
});
if (roadLines[0] && roadLines[0].y > gameHeight) {
roadLines.shift();
createRoadLine(-30);
}
if (buildings[0] && buildings[0].y > gameHeight) {
buildings.shift();
createBuilding(-100);
}
// Update obstacles
obstacles.forEach(obs => {
obs.y += gameSpeed * 0.8; // Make obstacles slightly slower for better reaction time
});
obstacles = obstacles.filter(obs => obs.y < gameHeight);
// Spawn new obstacles
if (Math.random() < 0.01 + gameSpeed * 0.005) {
createObstacle();
}
// Collision detection
obstacles.forEach(obs => {
const playerHitbox = { ...player, x: player.x + 5, width: player.width - 10 }; // Make hitbox slightly smaller
if (isColliding(playerHitbox, obs)) {
gameState = 'gameOver';
playCrashSound();
engineGain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 0.1);
}
});
// Update score and speed
score += Math.floor(gameSpeed);
gameSpeed += 0.003;
if (audioCtx) {
engineSound.frequency.linearRampToValueAtTime(50 + gameSpeed * 10, audioCtx.currentTime + 0.1);
}
}
// --- Drawing Logic ---
function draw() {
// Clear canvas & draw road
ctx.clearRect(0, 0, gameWidth, gameHeight);
ctx.fillStyle = '#111'; // Sidewalk color
ctx.fillRect(0, 0, gameWidth, gameHeight);
ctx.fillStyle = '#222'; // Road color
ctx.fillRect(40, 0, gameWidth - 80, gameHeight);
// Draw Buildings
buildings.forEach(b => {
ctx.fillStyle = '#0d0d1e';
ctx.fillRect(b.x, b.y, b.width, b.height);
ctx.strokeStyle = '#ff00ff';
ctx.lineWidth = 2;
ctx.strokeRect(b.x, b.y, b.width, b.height);
});
// Draw Road Lines
ctx.fillStyle = '#666';
roadLines.forEach(line => {
ctx.fillRect(line.x, line.y, line.width, line.height);
});
// Draw Obstacles
obstacles.forEach(obs => {
ctx.fillStyle = obs.color;
ctx.fillRect(obs.x, obs.y, obs.width, obs.height);
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)'; // Windshield
ctx.fillRect(obs.x + 5, obs.y + 10, obs.width - 10, 15);
});
// Draw Player Trail
player.trail.forEach(t => {
ctx.fillStyle = `rgba(0, 255, 255, ${t.opacity * 0.5})`;
ctx.beginPath();
ctx.rect(t.x, t.y + player.height / 2, t.width, player.height / 2);
ctx.fill();
});
// Draw Player
ctx.fillStyle = player.color;
ctx.fillRect(player.x, player.y, player.width, player.height);
// Add some detail to player car
ctx.fillStyle = '#fff'; // Windshield
ctx.fillRect(player.x + 5, player.y + 10, player.width - 10, 15);
ctx.fillStyle = '#ff4444'; // Tail lights
ctx.fillRect(player.x + 4, player.y + player.height - 8, 8, 6);
ctx.fillRect(player.x + player.width - 12, player.y + player.height - 8, 8, 6);
// Update Score Display
scoreDisplay.textContent = `SCORE: ${score}`;
}
function updateUI() {
if (gameState === 'gameOver') {
// Screen shake effect
canvas.style.animation = 'shake 0.5s';
setTimeout(() => canvas.style.animation = '', 500);
gameOverScreen.classList.remove('hidden');
scoreDisplay.classList.add('hidden');
finalScoreDisplay.textContent = `Final Score: ${score}`;
}
}
// --- Main Game Loop ---
function gameLoop() {
if (gameState !== 'playing') {
updateUI();
return;
}
update();
draw();
requestAnimationFrame(gameLoop);
}
// --- Initial Start ---
resetGame();
draw(); // Draw initial state
</script>
</body>
</html>
```Here's a fun, fast-paced arcade-style racer set in a city. This HTML5 game uses Canvas for rendering and includes simple controls (arrow keys) to dodge traffic:
```html
<!DOCTYPE html>
<html>
<head>
<title>City Arcade Racer</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #000;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: Arial, sans-serif;
}
canvas {
border: 2px solid #444;
box-shadow: 0 0 20px #00f;
}
#gameOver {
position: absolute;
color: white;
font-size: 48px;
text-align: center;
display: none;
}
#score {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-size: 24px;
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div id="gameOver">GAME OVER<br><span style="font-size:24px">Press R to restart</span></div>
<div id="score">Score: 0</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gameOverDiv = document.getElementById('gameOver');
const scoreDiv = document.getElementById('score');
// Set canvas size
canvas.width = 400;
canvas.height = 600;
// Game variables
let score = 0;
let gameSpeed = 3;
let gameRunning = true;
let roadWidth = canvas.width * 0.6;
let roadX = (canvas.width - roadWidth) / 2;
// Player car
const player = {
x: canvas.width / 2,
y: canvas.height - 100,
width: 30,
height: 50,
speed: 5,
color: '#3498db'
};
// Opponent cars
let cars = [];
const carColors = ['#e74c3c', '#f1c40f', '#2ecc71', '#9b59b6', '#1abc9c'];
// Road markers
let markers = [];
for (let i = -50; i < canvas.height; i += 100) {
markers.push(i);
}
// Background buildings
let buildings = [];
for (let i = 0; i < 10; i++) {
buildings.push({
x: Math.random() < 0.5 ? 0 : canvas.width - 50,
y: Math.random() * canvas.height,
width: 50,
height: 100 + Math.random() * 200,
color: `rgb(${Math.floor(50 + Math.random() * 100)}, ${Math.floor(50 + Math.random() * 100)}, ${Math.floor(50 + Math.random() * 100)})`
});
}
// Controls
const keys = {
ArrowLeft: false,
ArrowRight: false,
ArrowUp: false,
ArrowDown: false
};
window.addEventListener('keydown', (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = true;
}
if (e.key === 'r' && !gameRunning) {
resetGame();
}
});
window.addEventListener('keyup', (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = false;
}
});
function resetGame() {
score = 0;
gameSpeed = 3;
gameRunning = true;
cars = [];
player.x = canvas.width / 2;
gameOverDiv.style.display = 'none';
}
function spawnCar() {
if (Math.random() < 0.02) {
const lane = Math.random() < 0.5 ? roadX + 20 : roadX + roadWidth - 50;
cars.push({
x: lane,
y: -50,
width: 30,
height: 50,
speed: 2 + Math.random() * gameSpeed,
color: carColors[Math.floor(Math.random() * carColors.length)]
});
}
}
function update() {
if (!gameRunning) return;
// Update player position
if (keys.ArrowLeft && player.x > roadX + 10) {
player.x -= player.speed;
}
if (keys.ArrowRight && player.x < roadX + roadWidth - player.width - 10) {
player.x += player.speed;
}
// Update road markers
for (let i = 0; i < markers.length; i++) {
markers[i] += gameSpeed;
if (markers[i] > canvas.height) {
markers[i] = -50;
}
}
// Update buildings
for (let i = 0; i < buildings.length; i++) {
buildings[i].y += gameSpeed / 2;
if (buildings[i].y > canvas.height) {
buildings[i].y = -buildings[i].height;
}
}
// Update cars
for (let i = cars.length - 1; i >= 0; i--) {
cars[i].y += cars[i].speed;
// Check collision
if (
player.x < cars[i].x + cars[i].width &&
player.x + player.width > cars[i].x &&
player.y < cars[i].y + cars[i].height &&
player.y + player.height > cars[i].y
) {
gameRunning = false;
gameOverDiv.style.display = 'block';
}
// Remove cars that are off screen
if (cars[i].y > canvas.height) {
cars.splice(i, 1);
score++;
scoreDiv.textContent = `Score: ${score}`;
// Increase difficulty
if (score % 5 === 0) {
gameSpeed += 0.2;
}
}
}
spawnCar();
}
function draw() {
// Clear canvas
ctx.fillStyle = '#2c3e50';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw buildings
ctx.fillStyle = '#34495e';
for (let i = 0; i < buildings.length; i++) {
const b = buildings[i];
ctx.fillStyle = b.color;
ctx.fillRect(b.x, b.y, b.width, b.height);
// Windows
ctx.fillStyle = '#ecf0f1';
for (let wy = b.y + 10; wy < b.y + b.height; wy += 20) {
for (let wx = b.x + 5; wx < b.x + b.width; wx += 15) {
if (Math.random() > 0.3) {
ctx.fillRect(wx, wy, 8, 8);
}
}
}
}
// Draw road
ctx.fillStyle = '#7f8c8d';
ctx.fillRect(roadX, 0, roadWidth, canvas.height);
// Draw road markers
ctx.fillStyle = '#f1c40f';
for (let i = 0; i < markers.length; i++) {
ctx.fillRect(canvas.width / 2 - 5, markers[i], 10, 30);
}
// Draw cars
for (let i = 0; i < cars.length; i++) {
const car = cars[i];
ctx.fillStyle = car.color;
ctx.fillRect(car.x, car.y, car.width, car.height);
// Car details
ctx.fillStyle = '#000';
ctx.fillRect(car.x + 5, car.y + 10, 20, 10);
ctx.fillRect(car.x + 5, car.y + 30, 20, 10);
}
// Draw player
ctx.fillStyle = player.color;
ctx.fillRect(player.x, player.y, player.width, player.height);
// Player car details
ctx.fillStyle = '#000';
ctx.fillRect(player.x + 5, player.y + 10, 20, 10);
ctx.fillRect(player.x + 5, player.y + 30, 20, 10);
ctx.fillStyle = '#f1c40f';
ctx.fillRect(player.x + 10, player.y + 5, 10, 5); // headlights
ctx.fillRect(player.x + 10, player.y + 40, 10, 5); // taillights
}
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
gameLoop();
</script>
</body>
</html>
```
This game features:
1. A city backdrop with randomly generated buildings
2. Fast-paced racing where you dodge incoming traffic
3. Increasing difficulty as your score goes up
4. Simple controls (left/right arrows to move)
5. Colorful opponent cars
6. Score tracking
7. Game over screen with restart option (press 'R')
The game gets progressively faster as you score points by successfully dodging cars. The buildings scroll by to give a sense of speed, and the road markers help with depth perception.
You can copy this entire code into an HTML file and open it in any modern browser to play.# Arcade City Racer
I'll create a fast-paced arcade-style racing game set in a city environment with colorful graphics, dynamic movement, and increasing difficulty.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Arcade City Racer</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
font-family: 'Arial Rounded MT Bold', 'Arial', sans-serif;
overflow: hidden;
}
.game-container {
position: relative;
width: 800px;
height: 600px;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.6);
border-radius: 10px;
overflow: hidden;
}
canvas {
background: #111;
display: block;
}
.ui-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
.title {
position: absolute;
top: 20px;
left: 0;
width: 100%;
text-align: center;
font-size: 48px;
font-weight: bold;
color: #ffcc00;
text-shadow: 0 0 10px rgba(255, 204, 0, 0.7),
0 0 20px rgba(255, 204, 0, 0.5),
3px 3px 0 #ff3300;
letter-spacing: 2px;
transform: perspective(500px) rotateX(20deg);
animation: title-pulse 2s infinite alternate;
}
.score {
position: absolute;
top: 90px;
left: 20px;
font-size: 24px;
color: #fff;
text-shadow: 0 0 5px #00ccff;
}
.speed {
position: absolute;
top: 90px;
right: 20px;
font-size: 24px;
color: #fff;
text-shadow: 0 0 5px #ff3300;
}
.message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 36px;
color: #ffcc00;
text-shadow: 0 0 10px rgba(255, 204, 0, 0.7);
text-align: center;
opacity: 0;
transition: opacity 0.3s;
}
.controls {
position: absolute;
bottom: 20px;
left: 0;
width: 100%;
text-align: center;
color: #fff;
font-size: 18px;
text-shadow: 0 0 5px #00ccff;
}
.start-button {
position: absolute;
top: 60%;
left: 50%;
transform: translate(-50%, -50%);
padding: 15px 40px;
font-size: 24px;
background: linear-gradient(to bottom, #ff3300, #cc0000);
color: white;
border: none;
border-radius: 50px;
cursor: pointer;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
pointer-events: auto;
transition: all 0.2s;
}
.start-button:hover {
transform: translate(-50%, -50%) scale(1.05);
box-shadow: 0 7px 20px rgba(0, 0, 0, 0.6);
}
.start-button:active {
transform: translate(-50%, -50%) scale(0.95);
}
.city-skyline {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 150px;
background: linear-gradient(to top, #333, #222);
z-index: 1;
}
.building {
position: absolute;
bottom: 0;
background: #222;
width: 40px;
height: 100px;
}
.building:before {
content: '';
position: absolute;
top: -20px;
left: 5px;
width: 30px;
height: 20px;
background: #333;
clip-path: polygon(50% 0%, 0% 100%, 100% 100%);
}
@keyframes title-pulse {
0% { text-shadow: 0 0 10px rgba(255, 204, 0, 0.7), 0 0 20px rgba(255, 204, 0, 0.5), 3px 3px 0 #ff3300; }
100% { text-shadow: 0 0 15px rgba(255, 204, 0, 0.9), 0 0 30px rgba(255, 204, 0, 0.7), 5px 5px 0 #ff5500; }
}
@keyframes road-move {
0% { background-position: 0 0; }
100% { background-position: 0 40px; }
}
</style>
</head>
<body>
<div class="game-container">
<canvas id="gameCanvas" width="800" height="600"></canvas>
<div class="ui-overlay">
<div class="title">ARCADE CITY RACER</div>
<div class="score">SCORE: <span id="scoreValue">0</span></div>
<div class="speed">SPEED: <span id="speedValue">0</span> MPH</div>
<div class="message" id="message">GAME OVER</div>
<button class="start-button" id="startButton">START RACE</button>
<div class="controls">CONTROLS: ← → ARROWS TO MOVE</div>
</div>
</div>
<script>
// Game variables
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreValue = document.getElementById('scoreValue');
const speedValue = document.getElementById('speedValue');
const message = document.getElementById('message');
const startButton = document.getElementById('startButton');
let gameRunning = false;
let score = 0;
let speed = 0;
let maxSpeed = 0;
let playerX = canvas.width / 2;
let roadOffset = 0;
let obstacles = [];
let lastObstacleTime = 0;
let animationId;
let roadLines = [];
let particles = [];
let gameTime = 0;
// Initialize road lines
for (let i = 0; i < 20; i++) {
roadLines.push({
x: canvas.width / 2 - 5,
y: i * 40,
width: 10,
height: 20
});
}
// Player car
const playerCar = {
x: playerX,
y: canvas.height - 100,
width: 50,
height: 90,
color: '#ff3300',
draw: function() {
// Car body
ctx.fillStyle = this.color;
ctx.fillRect(this.x - this.width/2, this.y - this.height/2, this.width, this.height);
// Car details
ctx.fillStyle = '#1a1a1a';
ctx.fillRect(this.x - this.width/2 + 5, this.y - this.height/2 + 5, this.width - 10, 20);
ctx.fillRect(this.x - this.width/2 + 5, this.y + this.height/2 - 25, this.width - 10, 20);
// Windows
ctx.fillStyle = '#aaccff';
ctx.fillRect(this.x - this.width/2 + 10, this.y - this.height/2 + 30, this.width - 20, 30);
// Headlights
ctx.fillStyle = '#ffff00';
ctx.fillRect(this.x - this.width/2 + 5, this.y + this.height/2 - 10, 8, 5);
ctx.fillRect(this.x + this.width/2 - 13, this.y + this.height/2 - 10, 8, 5);
// Wheels
ctx.fillStyle = '#333';
ctx.fillRect(this.x - this.width/2 - 5, this.y - this.height/2 + 10, 5, 20);
ctx.fillRect(this.x + this.width/2, this.y - this.height/2 + 10, 5, 20);
ctx.fillRect(this.x - this.width/2 - 5, this.y + this.height/2 - 30, 5, 20);
ctx.fillRect(this.x + this.width/2, this.y + this.height/2 - 30, 5, 20);
}
};
// Obstacle cars
function createObstacle() {
const lane = Math.floor(Math.random() * 3); // 0 = left, 1 = center, 2 = right
let x;
if (lane === 0) x = canvas.width / 2 - 100;
else if (lane === 1) x = canvas.width / 2;
else x = canvas.width / 2 + 100;
const colors = ['#00ccff', '#ff00ff', '#00ff99', '#ffcc00'];
const color = colors[Math.floor(Math.random() * colors.length)];
obstacles.push({
x: x,
y: -100,
width: 50,
height: 90,
color: color,
speed: 5 + Math.random() * 3
});
}
// Create particle effect
function createParticles(x, y, color, count) {
for (let i = 0; i < count; i++) {
particles.push({
x: x,
y: y,
size: Math.random() * 8 + 2,
speedX: (Math.random() - 0.5) * 10,
speedY: (Math.random() - 0.5) * 10,
color: color,
life: 30
});
}
}
// Draw road
function drawRoad() {
// Road
ctx.fillStyle = '#444';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Road markings
ctx.fillStyle = '#ffff00';
for (let line of roadLines) {
ctx.fillRect(line.x - line.width/2, line.y - roadOffset, line.width, line.height);
}
// Road sides
ctx.fillStyle = '#333';
ctx.fillRect(0, 0, 100, canvas.height);
ctx.fillRect(canvas.width - 100, 0, 100, canvas.height);
// Road side markings
ctx.fillStyle = '#ff3300';
ctx.fillRect(95, 0, 5, canvas.height);
ctx.fillRect(canvas.width - 100, 0, 5, canvas.height);
}
// Draw buildings in background
function drawBuildings() {
ctx.fillStyle = '#1a1a1a';
for (let i = 0; i < 20; i++) {
const height = 50 + Math.random() * 150;
ctx.fillRect(i * 50, canvas.height - height, 40, height);
// Windows
ctx.fillStyle = '#333366';
for (let w = 0; w < 4; w++) {
for (let h = 0; h < Math.floor(height / 20); h++) {
if (Math.random() > 0.3) {
ctx.fillRect(i * 50 + 5 + w * 10, canvas.height - height + 10 + h * 20, 8, 15);
}
}
}
ctx.fillStyle = '#1a1a1a';
}
}
// Check collision
function checkCollision(rect1, rect2) {
return rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.y + rect1.height > rect2.y;
}
// Game loop
function gameLoop() {
if (!gameRunning) return;
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update game time
gameTime++;
// Update road offset for moving effect
roadOffset += speed / 5;
if (roadOffset >= 40) roadOffset = 0;
// Draw road
drawRoad();
// Draw buildings
drawBuildings();
// Update and draw obstacles
for (let i = obstacles.length - 1; i >= 0; i--) {
const obstacle = obstacles[i];
obstacle.y += obstacle.speed + speed / 10;
// Draw obstacle car
ctx.fillStyle = obstacle.color;
ctx.fillRect(obstacle.x - obstacle.width/2, obstacle.y - obstacle.height/2, obstacle.width, obstacle.height);
// Car details
ctx.fillStyle = '#1a1a1a';
ctx.fillRect(obstacle.x - obstacle.width/2 + 5, obstacle.y - obstacle.height/2 + 5, obstacle.width - 10, 20);
ctx.fillRect(obstacle.x - obstacle.width/2 + 5, obstacle.y + obstacle.height/2 - 25, obstacle.width - 10, 20);
// Windows
ctx.fillStyle = '#aaccff';
ctx.fillRect(obstacle.x - obstacle.width/2 + 10, obstacle.y - obstacle.height/2 + 30, obstacle.width - 20, 30);
// Wheels
ctx.fillStyle = '#333';
ctx.fillRect(obstacle.x - obstacle.width/2 - 5, obstacle.y - obstacle.height/2 + 10, 5, 20);
ctx.fillRect(obstacle.x + obstacle.width/2, obstacle.y - obstacle.height/2 + 10, 5, 20);
ctx.fillRect(obstacle.x - obstacle.width/2 - 5, obstacle.y + obstacle.height/2 - 30, 5, 20);
ctx.fillRect(obstacle.x + obstacle.width/2, obstacle.y + obstacle.height/2 - 30, 5, 20);
// Remove obstacles that are off screen
if (obstacle.y > canvas.height + 100) {
obstacles.splice(i, 1);
score += 10;
scoreValue.textContent = score;
}
// Check collision
const playerRect = {
x: playerCar.x - playerCar.width/2,
y: playerCar.y - playerCar.height/2,
width: playerCar.width,
height: playerCar.height
};
const obstacleRect = {
x: obstacle.x - obstacle.width/2,
y: obstacle.y - obstacle.height/2,
width: obstacle.width,
height: obstacle.height
};
if (checkCollision(playerRect, obstacleRect)) {
createParticles(playerCar.x, playerCar.y, playerCar.color, 30);
createParticles(obstacle.x, obstacle.y, obstacle.color, 30);
gameOver();
}
}
// Create new obstacles
if (gameTime - lastObstacleTime > 60 - Math.min(speed, 50)) {
createObstacle();
lastObstacleTime = gameTime;
}
// Draw player car
playerCar.draw();
// Update and draw particles
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.speedX;
p.y += p.speedY;
p.life--;
ctx.fillStyle = p.color;
ctx.globalAlpha = p.life / 30;
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fill();
if (p.life <= 0) {
particles.splice(i, 1);
}
}
ctx.globalAlpha = 1;
// Increase speed over time
if (gameTime % 100 === 0 && speed < 120) {
speed += 2;
speedValue.textContent = speed;
if (speed > maxSpeed) maxSpeed = speed;
}
// Add score for speed
if (gameTime % 10 === 0) {
score += Math.floor(speed / 10);
scoreValue.textContent = score;
}
animationId = requestAnimationFrame(gameLoop);
}
// Game over function
function gameOver() {
gameRunning = false;
cancelAnimationFrame(animationId);
message.textContent = `GAME OVER! SCORE: ${score}`;
message.style.opacity = '1';
startButton.style.display = 'block';
}
// Start game function
function startGame() {
gameRunning = true;
score = 0;
speed = 40;
maxSpeed = 40;
playerX = canvas.width / 2;
obstacles = [];
particles = [];
gameTime = 0;
lastObstacleTime = 0;
message.style.opacity = '0';
startButton.style.display = 'none';
scoreValue.textContent = '0';
speedValue.textContent = '40';
gameLoop();
}
// Event listeners
startButton.addEventListener('click', startGame);
document.addEventListener('keydown', function(e) {
if (!gameRunning) return;
if (e.key === 'ArrowLeft' && playerX > canvas.width / 2 - 100) {
playerX -= 100;
}
if (e.key === 'ArrowRight' && playerX < canvas.width / 2 + 100) {
playerX += 100;
}
playerCar.x = playerX;
});
// Initialize player position
playerCar.x = playerX;
</script>
</body>
</html>
```
## Game Features
1. **Fast-Paced Racing Action**: The game starts at 40 MPH and gradually increases speed up to 120 MPH.
2. **City Environment**:
- Dynamic road with moving markings
- Building skyline in the background
- Neon-style color scheme
3. **Gameplay**:
- Control your car with left/right arrow keys
- Avoid colorful obstacle cars
- Score increases as you survive longer and go faster
- Collisions create particle explosion effects
4. **Visual Elements**:
- Animated road for speed effect
- Particle explosions on collisions
- Detailed car designs
- Glowing UI elements
5. **Game States**:
- Start screen with "START RACE" button
- In-game score and speed display
- Game over screen with final score
To play, simply open the HTML file in a browser and click "START RACE". Use the left and right arrow keys to dodge the oncoming traffic!```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>City Rush - Arcade Racer</title>
<style>
body {
margin: 0;
padding: 0;
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
font-family: 'Arial', sans-serif;
overflow: hidden;
}
#gameContainer {
position: relative;
width: 100vw;
height: 100vh;
background: linear-gradient(180deg, #87CEEB 0%, #98FB98 50%, #228B22 100%);
}
canvas {
display: block;
background: #333;
}
#ui {
position: absolute;
top: 20px;
left: 20px;
color: white;
font-size: 24px;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
z-index: 10;
}
#controls {
position: absolute;
bottom: 20px;
left: 20px;
color: white;
font-size: 16px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
z-index: 10;
}
#gameOver {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(0,0,0,0.9);
color: white;
padding: 40px;
border-radius: 20px;
text-align: center;
font-size: 24px;
display: none;
z-index: 20;
}
button {
background: #ff6b6b;
color: white;
border: none;
padding: 15px 30px;
font-size: 18px;
border-radius: 10px;
cursor: pointer;
margin-top: 20px;
transition: background 0.3s;
}
button:hover {
background: #ff5252;
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas"></canvas>
<div id="ui">
<div>Speed: <span id="speed">0</span> mph</div>
<div>Score: <span id="score">0</span></div>
<div>Distance: <span id="distance">0</span> m</div>
</div>
<div id="controls">
Use ARROW KEYS or WASD to steer and accelerate/brake
</div>
<div id="gameOver">
<h2>Game Over!</h2>
<div>Final Score: <span id="finalScore">0</span></div>
<div>Distance Traveled: <span id="finalDistance">0</span> m</div>
<button onclick="restartGame()">Play Again</button>
</div>
</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Set canvas size
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Game variables
let game = {
player: {
x: canvas.width / 2,
y: canvas.height - 150,
width: 30,
height: 60,
speed: 0,
maxSpeed: 15,
acceleration: 0.3,
friction: 0.95,
turnSpeed: 0
},
camera: {
x: 0,
y: 0
},
cars: [],
buildings: [],
roadLines: [],
particles: [],
score: 0,
distance: 0,
gameRunning: true,
keys: {},
roadWidth: 400,
laneCount: 4
};
// Input handling
document.addEventListener('keydown', (e) => {
game.keys[e.key.toLowerCase()] = true;
});
document.addEventListener('keyup', (e) => {
game.keys[e.key.toLowerCase()] = false;
});
// Initialize game objects
function initGame() {
// Create buildings
for (let i = 0; i < 50; i++) {
createBuilding(i);
}
// Create initial traffic
for (let i = 0; i < 15; i++) {
createCar();
}
// Create road lines
for (let i = 0; i < 100; i++) {
game.roadLines.push({
x: canvas.width / 2 - 20,
y: i * 80 - 2000
});
}
}
function createBuilding(index) {
const side = Math.random() > 0.5 ? 'left' : 'right';
const x = side === 'left' ?
Math.random() * (canvas.width / 2 - game.roadWidth / 2 - 100) :
canvas.width / 2 + game.roadWidth / 2 + Math.random() * (canvas.width / 2 - game.roadWidth / 2 - 100);
game.buildings.push({
x: x,
y: index * 200 - 1000,
width: 80 + Math.random() * 120,
height: 100 + Math.random() * 200,
color: `hsl(${200 + Math.random() * 60}, 30%, ${30 + Math.random() * 30}%)`
});
}
function createCar() {
const lanes = [];
const laneWidth = game.roadWidth / game.laneCount;
for (let i = 0; i < game.laneCount; i++) {
lanes.push(canvas.width / 2 - game.roadWidth / 2 + laneWidth / 2 + i * laneWidth);
}
game.cars.push({
x: lanes[Math.floor(Math.random() * lanes.length)],
y: -Math.random() * 2000 - 100,
width: 25,
height: 50,
speed: 2 + Math.random() * 4,
color: `hsl(${Math.random() * 360}, 70%, 50%)`
});
}
function createParticle(x, y, color) {
for (let i = 0; i < 5; i++) {
game.particles.push({
x: x + Math.random() * 20 - 10,
y: y + Math.random() * 20 - 10,
vx: Math.random() * 6 - 3,
vy: Math.random() * 6 - 3,
life: 30,
color: color
});
}
}
function updatePlayer() {
// Handle input
if (game.keys['arrowup'] || game.keys['w']) {
game.player.speed = Math.min(game.player.speed + game.player.acceleration, game.player.maxSpeed);
}
if (game.keys['arrowdown'] || game.keys['s']) {
game.player.speed = Math.max(game.player.speed - game.player.acceleration * 2, -game.player.maxSpeed / 2);
}
if (game.keys['arrowleft'] || game.keys['a']) {
game.player.turnSpeed = -8;
} else if (game.keys['arrowright'] || game.keys['d']) {
game.player.turnSpeed = 8;
} else {
game.player.turnSpeed = 0;
}
// Apply friction
game.player.speed *= game.player.friction;
// Move player
game.player.x += game.player.turnSpeed;
// Keep player on road
const roadLeft = canvas.width / 2 - game.roadWidth / 2;
const roadRight = canvas.width / 2 + game.roadWidth / 2;
game.player.x = Math.max(roadLeft + game.player.width / 2,
Math.min(roadRight - game.player.width / 2, game.player.x));
// Update camera and distance
if (game.player.speed > 0) {
game.camera.y -= game.player.speed;
game.distance += game.player.speed * 0.1;
game.score += Math.floor(game.player.speed);
}
// Create speed particles
if (game.player.speed > 8) {
createParticle(game.player.x, game.player.y + game.player.height, '#ffff00');
}
}
function updateCars() {
game.cars.forEach((car, index) => {
car.y += car.speed + game.player.speed;
// Remove cars that are off screen
if (car.y > canvas.height + 100) {
game.cars.splice(index, 1);
createCar(); // Add a new car
}
// Check collision with player
if (checkCollision(game.player, car)) {
createParticle(car.x, car.y, '#ff0000');
endGame();
}
});
}
function updateBuildings() {
game.buildings.forEach(building => {
building.y += game.player.speed;
if (building.y > canvas.height + 200) {
building.y -= 10000;
}
});
}
function updateRoadLines() {
game.roadLines.forEach(line => {
line.y += game.player.speed;
if (line.y > canvas.height + 50) {
line.y -= 8000;
}
});
}
function updateParticles() {
game.particles.forEach((particle, index) => {
particle.x += particle.vx;
particle.y += particle.vy + game.player.speed;
particle.life--;
if (particle.life <= 0) {
game.particles.splice(index, 1);
}
});
}
function checkCollision(rect1, rect2) {
return rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.y + rect1.height > rect2.y;
}
function draw() {
// Clear canvas with sky gradient
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
gradient.addColorStop(0, '#87CEEB');
gradient.addColorStop(1, '#228B22');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw buildings
game.buildings.forEach(building => {
ctx.fillStyle = building.color;
ctx.fillRect(building.x, building.y, building.width, building.height);
// Add windows
ctx.fillStyle = '#ffff99';
for (let i = 0; i < building.width / 15; i++) {
for (let j = 0; j < building.height / 20; j++) {
if (Math.random() > 0.7) {
ctx.fillRect(building.x + i * 15 + 5, building.y + j * 20 + 10, 8, 8);
}
}
}
});
// Draw road
ctx.fillStyle = '#333';
ctx.fillRect(canvas.width / 2 - game.roadWidth / 2, 0, game.roadWidth, canvas.height);
// Draw road lines
ctx.fillStyle = '#fff';
game.roadLines.forEach(line => {
ctx.fillRect(line.x, line.y, 3, 40);
for (let i = 1; i < game.laneCount; i++) {
ctx.fillRect(canvas.width / 2 - game.roadWidth / 2 + i * (game.roadWidth / game.laneCount), line.y, 2, 30);
}
});
// Draw cars
game.cars.forEach(car => {
ctx.fillStyle = car.color;
ctx.fillRect(car.x - car.width / 2, car.y - car.height / 2, car.width, car.height);
// Add car details
ctx.fillStyle = '#fff';
ctx.fillRect(car.x - car.width / 2 + 2, car.y - car.height / 2 + 2, car.width - 4, 8);
ctx.fillStyle = '#ff0000';
ctx.fillRect(car.x - car.width / 2 + 2, car.y + car.height / 2 - 8, car.width - 4, 6);
});
// Draw player
ctx.fillStyle = '#ff6b6b';
ctx.fillRect(game.player.x - game.player.width / 2, game.player.y - game.player.height / 2,
game.player.width, game.player.height);
// Add player details
ctx.fillStyle = '#fff';
ctx.fillRect(game.player.x - game.player.width / 2 + 2, game.player.y - game.player.height / 2 + 5,
game.player.width - 4, 8);
ctx.fillStyle = '#ffff00';
ctx.fillRect(game.player.x - game.player.width / 2 + 2, game.player.y + game.player.height / 2 - 8,
game.player.width - 4, 6);
// Draw particles
game.particles.forEach(particle => {
ctx.fillStyle = particle.color;
ctx.globalAlpha = particle.life / 30;
ctx.fillRect(particle.x, particle.y, 3, 3);
ctx.globalAlpha = 1;
});
}
function updateUI() {
document.getElementById('speed').textContent = Math.floor(game.player.speed * 10);
document.getElementById('score').textContent = Math.floor(game.score);
document.getElementById('distance').textContent = Math.floor(game.distance);
}
function endGame() {
game.gameRunning = false;
document.getElementById('finalScore').textContent = Math.floor(game.score);
document.getElementById('finalDistance').textContent = Math.floor(game.distance);
document.getElementById('gameOver').style.display = 'block';
}
function restartGame() {
// Reset game state
game.player.x = canvas.width / 2;
game.player.speed = 0;
game.cars = [];
game.buildings = [];
game.roadLines = [];
game.particles = [];
game.score = 0;
game.distance = 0;
game.gameRunning = true;
game.camera.y = 0;
document.getElementById('gameOver').style.display = 'none';
initGame();
}
function gameLoop() {
if (game.gameRunning) {
updatePlayer();
updateCars();
updateBuildings();
updateRoadLines();
updateParticles();
}
draw();
updateUI();
requestAnimationFrame(gameLoop);
}
// Handle window resize
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
});
// Start the game
initGame();
gameLoop();
</script>
</body>
</html>
```
This is a fast-paced arcade racing game set in a city with the following features:
🏎️ **Gameplay:**
- Control your red sports car using arrow keys or WASD
- Navigate through traffic while building speed
- Avoid collisions with other colored cars
- Drive through a procedurally generated city with buildings
🎮 **Features:**
- Responsive controls for steering and acceleration/braking
- Speed-based particle effects when going fast
- Dynamic traffic that moves at different speeds
- City skyline with randomly lit windows
- Real-time score, speed, and distance tracking
- Game over screen with restart functionality
🌃 **Visual Elements:**
- Multi-lane highway with proper road markings
- Colorful city buildings on both sides
- Smooth gradient sky background
- Collision effects with explosion particles
- Speed indicator and comprehensive UI
The game emphasizes fast-paced action while requiring skill to weave through traffic and maintain high speeds for better scores!```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>City Rush – Mini Arcade Racer</title>
<style>
html,body{
margin:0;
height:100%;
background:#222;
overflow:hidden;
font-family:Arial,Helvetica,sans-serif;
color:#fff;
}
#ui{
position:fixed;
top:10px;
left:50%;
transform:translateX(-50%);
text-align:center;
pointer-events:none;
font-size:22px;
text-shadow:0 0 5px #000;
}
#ui span{
margin:0 15px;
}
#msg{
position:fixed;
top:50%;
left:50%;
transform:translate(-50%,-50%);
text-align:center;
font-size:32px;
background:rgba(0,0,0,.7);
padding:25px 40px;
border-radius:10px;
display:none;
}
#msg button{
margin-top:20px;
padding:10px 25px;
font-size:20px;
cursor:pointer;
background:#ff5050;
border:none;
border-radius:5px;
color:#fff;
}
canvas{
display:block;
}
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<div id="ui">
<span id="scoreLabel">SCORE: 0</span>
<span id="speedLabel">SPEED: 0 km/h</span>
</div>
<div id="msg">
<div id="msgText"></div>
<button id="restartBtn">RESTART ( SPACE )</button>
</div>
<script>
(() => {
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const uiScore = document.getElementById('scoreLabel');
const uiSpeed = document.getElementById('speedLabel');
const msgBox = document.getElementById('msg');
const msgText = document.getElementById('msgText');
const restartBtn = document.getElementById('restartBtn');
let W, H;
function resize(){
W = canvas.width = window.innerWidth;
H = canvas.height = window.innerHeight;
}
window.addEventListener('resize', resize);
resize();
// Game constants
const roadWidth = 400;
const laneCount = 3;
const laneWidth = roadWidth / laneCount;
const carWidth = 50;
const carHeight = 90;
const buildingWidth = 80;
const maxSpeed = 12;
// Game variables
let gameState; // 'running' | 'gameover'
let keys = {left:false,right:false};
let obstacles = [];
let buildings = [];
let stripeOffset=0;
let score = 0;
let speed = 4; // starts slow, ramps up
let timeSinceStart=0;
const player = {
x: 0,
y: 0,
lane: 1
};
function startGame(){
obstacles.length = 0;
buildings.length = 0;
stripeOffset = score = timeSinceStart = 0;
speed = 4;
player.lane = 1;
player.x = W/2 - laneWidth/2 - laneWidth + (laneWidth-player.width)/2;
player.y = H - carHeight - 30;
gameState = 'running';
msgBox.style.display='none';
requestAnimationFrame(loop);
}
// Input
window.addEventListener('keydown', e=>{
if(e.code==='ArrowLeft' || e.code==='KeyA') keys.left=true;
if(e.code==='ArrowRight'|| e.code==='KeyD') keys.right=true;
if(gameState==='gameover' && e.code==='Space') startGame();
});
window.addEventListener('keyup', e=>{
if(e.code==='ArrowLeft' || e.code==='KeyA') keys.left=false;
if(e.code==='ArrowRight'|| e.code==='KeyD') keys.right=false;
});
restartBtn.addEventListener('click',startGame);
// Helpers
function rand(min,max){return Math.random()*(max-min)+min;}
function collides(a,b){
return !(a.x+a.w < b.x || a.x > b.x+b.w || a.y+a.h < b.y || a.y > b.y+b.h);
}
// Main loop
let lastTime=0;
function loop(t){
if(gameState!=='running') return;
const dt = (t - lastTime)/16.666; // approx frames
lastTime = t;
update(dt);
draw();
requestAnimationFrame(loop);
}
function update(dt){
// Speed ramp-up
speed += 0.005*dt;
if(speed > maxSpeed) speed = maxSpeed;
stripeOffset += speed*dt*4;
if(stripeOffset>40) stripeOffset-=40;
// Player lane change
if(keys.left && player.lane>0){
player.lane--;
keys.left=false;
}
if(keys.right && player.lane<laneCount-1){
player.lane++;
keys.right=false;
}
player.x = (W/2 - roadWidth/2) + player.lane*laneWidth + (laneWidth-carWidth)/2;
// Spawn obstacles
if(Math.random() < 0.04*dt){
const lane = Math.floor(Math.random()*laneCount);
obstacles.push({
lane,
x: (W/2 - roadWidth/2)+lane*laneWidth+(laneWidth-carWidth)/2,
y: -carHeight,
w: carWidth,
h: carHeight,
color: 'hsl('+rand(0,360)+',70%,50%)'
});
}
// Spawn buildings for side-scroll
if(Math.random() < 0.1*dt){
const side = Math.random()<0.5 ? 'left' : 'right';
buildings.push({
side,
x: side==='left' ? W/2-roadWidth/2-buildingWidth : W/2+roadWidth/2,
y: -rand(80,160),
w: buildingWidth,
h: rand(120,220),
color:'#'+Math.floor(rand(0x444444,0xaaaaaa)).toString(16)
});
}
// Update obstacles
for(let i=obstacles.length-1;i>=0;--i){
const o = obstacles[i];
o.y += speed*dt*6;
if(o.y>H+100) obstacles.splice(i,1);
else if(collides({x:player.x,y:player.y,w:carWidth,h:carHeight},{x:o.x,y:o.y,w:o.w,h:o.h})){
gameOver();
}
}
// Update buildings
for(let i=buildings.length-1;i>=0;--i){
const b = buildings[i];
b.y += speed*dt*4;
if(b.y>H+50) buildings.splice(i,1);
}
// Score
timeSinceStart += dt;
score += speed*dt*0.5;
uiScore.textContent = 'SCORE: '+Math.floor(score);
uiSpeed.textContent = 'SPEED: '+Math.floor(speed*30)+' km/h';
}
function draw(){
ctx.fillStyle='#111';
ctx.fillRect(0,0,W,H);
// road
ctx.fillStyle='#444';
ctx.fillRect(W/2-roadWidth/2,0,roadWidth,H);
// lane stripes
ctx.strokeStyle='#eee';
ctx.lineWidth=4;
ctx.setLineDash([20,20]);
ctx.lineDashOffset = -stripeOffset;
for(let i=1;i<laneCount;i++){
const x = W/2-roadWidth/2 + i*laneWidth;
ctx.beginPath();
ctx.moveTo(x,0);
ctx.lineTo(x,H);
ctx.stroke();
}
ctx.setLineDash([]);
// buildings
buildings.forEach(b=>{
ctx.fillStyle=b.color;
ctx.fillRect(b.x,b.y,b.w,b.h);
// windows
ctx.fillStyle='rgba(255,255,255,.2)';
for(let wy=4;wy<b.h-10;wy+=16){
for(let wx=4;wx<b.w-10;wx+=16){
if(Math.random()<0.5){
ctx.fillRect(b.x+wx,b.y+wy,8,10);
}
}
}
});
// player's car
ctx.fillStyle='#00b3ff';
ctx.fillRect(player.x,player.y,carWidth,carHeight);
ctx.fillStyle='#fff';
ctx.fillRect(player.x+10,player.y+10,carWidth-20,carHeight-60); // windshield
ctx.fillStyle='#333';
ctx.fillRect(player.x+8,player.y+carHeight-28,carWidth-16,20); // rear
// obstacles
obstacles.forEach(o=>{
ctx.fillStyle=o.color;
ctx.fillRect(o.x,o.y,o.w,o.h);
ctx.fillStyle='#000';
ctx.fillRect(o.x+8,o.y+8,o.w-16,o.h-60);
});
}
function gameOver(){
gameState='gameover';
msgText.innerHTML = 'CRASH!<br>Final score: '+Math.floor(score);
msgBox.style.display='block';
}
// initial call
startGame();
})();
</script>
</body>
</html>
```Here's a fun, fast-paced arcade racer set in a city environment using JavaScript and HTML5 Canvas. I've included everything in a single HTML block with inline CSS and JavaScript, so it can be rendered directly in a browser. The game features a car dodging obstacles (other vehicles) on a scrolling city road with simple controls (arrow keys for movement).
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>City Arcade Racer</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #333;
font-family: Arial, sans-serif;
}
canvas {
border: 2px solid white;
}
#gameOver {
position: absolute;
color: white;
font-size: 30px;
text-align: center;
background: rgba(0, 0, 0, 0.8);
padding: 20px;
display: none;
}
#score {
position: absolute;
top: 10px;
left: 10px;
color: white;
font-size: 20px;
background: rgba(0, 0, 0, 0.5);
padding: 10px;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="400" height="600"></canvas>
<div id="gameOver">Game Over!<br>Press R to Restart</div>
<div id="score">Score: 0</div>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const gameOverDiv = document.getElementById('gameOver');
const scoreDiv = document.getElementById('score');
// Game settings
const carWidth = 50;
const carHeight = 80;
const roadWidth = canvas.width;
const roadHeight = canvas.height;
const playerSpeed = 5;
const obstacleSpeed = 7;
const backgroundSpeed = 5;
let score = 0;
let gameOver = false;
let highScore = localStorage.getItem('highScore') ? parseInt(localStorage.getItem('highScore')) : 0;
// Player car
const player = {
x: roadWidth / 2 - carWidth / 2,
y: roadHeight - carHeight - 20,
width: carWidth,
height: carHeight,
speed: playerSpeed,
color: 'red'
};
// Obstacles (enemy cars)
const obstacles = [];
const obstacleFrequency = 60; // Frames between spawning new obstacles
let frameCount = 0;
// Background (city road scrolling effect)
let bgY = 0;
const bgHeight = roadHeight * 2;
// Controls
let keys = { left: false, right: false };
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') keys.left = true;
if (e.key === 'ArrowRight') keys.right = true;
if (e.key.toLowerCase() === 'r' && gameOver) resetGame();
});
document.addEventListener('keyup', (e) => {
if (e.key === 'ArrowLeft') keys.left = false;
if (e.key === 'ArrowRight') keys.right = false;
});
function resetGame() {
player.x = roadWidth / 2 - carWidth / 2;
obstacles.length = 0;
score = 0;
gameOver = false;
gameOverDiv.style.display = 'none';
scoreDiv.textContent = `Score: ${score}`;
frameCount = 0;
}
function spawnObstacle() {
const obstacle = {
x: Math.random() * (roadWidth - carWidth),
y: -carHeight,
width: carWidth,
height: carHeight,
color: 'blue',
speed: obstacleSpeed + Math.random() * 2
};
obstacles.push(obstacle);
}
function update() {
if (gameOver) return;
// Move player
if (keys.left && player.x > 0) player.x -= player.speed;
if (keys.right && player.x < roadWidth - player.width) player.x += player.speed;
// Spawn obstacles
frameCount++;
if (frameCount >= obstacleFrequency) {
spawnObstacle();
frameCount = 0;
}
// Move obstacles
obstacles.forEach(obstacle => {
obstacle.y += obstacle.speed;
// Collision detection
if (
player.x < obstacle.x + obstacle.width &&
player.x + player.width > obstacle.x &&
player.y < obstacle.y + obstacle.height &&
player.y + player.height > obstacle.y
) {
gameOver = true;
if (score > highScore) {
highScore = score;
localStorage.setItem('highScore', highScore);
}
gameOverDiv.innerHTML = `Game Over!<br>Score: ${score}<br>High Score: ${highScore}<br>Press R to Restart`;
gameOverDiv.style.left = `${canvas.offsetLeft + roadWidth / 2 - gameOverDiv.offsetWidth / 2}px`;
gameOverDiv.style.top = `${canvas.offsetTop + roadHeight / 2 - gameOverDiv.offsetHeight / 2}px`;
gameOverDiv.style.display = 'block';
}
});
// Remove off-screen obstacles
obstacles.filter(obstacle => obstacle.y > roadHeight);
// Update background
bgY += backgroundSpeed;
if (bgY >= roadHeight) bgY = 0;
// Increase score
score += 1;
scoreDiv.textContent = `Score: ${score}`;
}
function draw() {
ctx.clearRect(0, 0, roadWidth, roadHeight);
// Draw scrolling city background
ctx.fillStyle = 'gray';
ctx.fillRect(0, 0, roadWidth, roadHeight);
// Road
ctx.fillStyle = 'darkgray';
ctx.fillRect(roadWidth / 4, bgY - roadHeight, roadWidth / 2, roadHeight * 2);
// Road lines
ctx.fillStyle = 'yellow';
for (let i = 0; i < roadHeight * 2; i += 50) {
ctx.fillRect(roadWidth / 2 - 5, (bgY + i) - roadHeight, 10, 30);
}
// City buildings (simple rectangles on sides)
ctx.fillStyle = '#444';
for (let i = 0; i < roadHeight * 2; i += 100) {
ctx.fillRect(0, (bgY + i) - roadHeight, roadWidth / 4 - 10, 60);
ctx.fillRect(roadWidth - (roadWidth / 4 - 10), (bgY + i) - roadHeight, roadWidth / 4 - 10, 60);
}
// Draw player car
ctx.fillStyle = player.color;
ctx.fillRect(player.x, player.y, player.width, player.height);
// Car details (headlights)
ctx.fillStyle = 'yellow';
ctx.fillRect(player.x + 5, player.y + player.height - 10, 10, 5);
ctx.fillRect(player.x + player.width - 15, player.y + player.height - 10, 10, 5);
// Draw obstacles (enemy cars)
obstacles.forEach(obstacle => {
ctx.fillStyle = obstacle.color;
ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
// Car details
ctx.fillStyle = 'white';
ctx.fillRect(obstacle.x + 5, obstacle.y + 5, 10, 5);
ctx.fillRect(obstacle.x + obstacle.width - 15, obstacle.y + 5, 10, 5);
});
}
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
// Start the game
gameLoop();
</script>
</body>
</html>
```
This code creates a complete arcade racer game with the following features:
- **City Setting**: A scrolling road with simplistic city buildings on the sides, road markings, and a gray urban aesthetic.
- **Fast-Paced Gameplay**: The player's car moves quickly with arrow key controls (left and right), while enemy cars spawn at random positions and move down the screen at varying speeds.
- **Mechanics**: Avoid crashing into other cars; score increases over time, and the game ends on collision with a restart option (press 'R').
- **Scoring**: Tracks current score and high score (saved in local storage).
Simply open this HTML file in a web browser to play. No external libraries are used—everything is built with vanilla JavaScript and Canvas. Enjoy racing through the city streets!```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>City Racer Arcade</title>
<style>
html, body {
background: #181c24;
margin: 0;
overflow: hidden;
user-select: none;
height: 100%;
}
canvas {
display: block;
margin: 0 auto;
background: #222941;
box-shadow: 0 8px 24px #000c;
}
#overlay {
position: fixed;
top: 0; left: 0; width: 100vw; height: 100vh;
background: rgba(0,0,0,0.65);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-family: 'Segoe UI', Arial, sans-serif;
font-size: 3vw;
letter-spacing: 2px;
z-index: 3;
flex-direction: column;
transition: 0.5s;
pointer-events: none;
opacity: 0;
}
#overlay.active {
opacity: 1;
pointer-events: all;
}
#overlay button {
margin-top: 2vw;
background: #ff2954;
color: #fff;
border: none;
font-size: 1.5vw;
padding: 1vw 2vw;
border-radius: 0.5vw;
cursor: pointer;
transition: background 0.2s;
}
#overlay button:hover {
background: #ff4480;
}
</style>
</head>
<body>
<canvas id="game"></canvas>
<div id="overlay">
<div id="message"></div>
<button id="startBtn">Start</button>
</div>
<script>
const W = 800, H = 600;
const ROAD_W = 360;
const ROAD_MARK_W = 12;
const LANE_COUNT = 3;
const BUILDING_W = 80;
const BUILDING_DIST = 80;
const BUILDING_ROWS = 6;
const SPEED_MAX = 14;
const SPEED_ACC = 0.16;
const SPEED_DEC = 0.12;
const TURN_SPEED = 0.23;
const CAR_W = 44, CAR_H = 68;
const OBSTACLE_W = 44, OBSTACLE_H = 68;
const SPAWN_INTERVAL = 880;
const CITY_COLORS = [
"#5D6774", "#848CA0", "#B2BAD0", "#57627B", "#444A5C"
];
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
canvas.width = W;
canvas.height = H;
const overlay = document.getElementById('overlay');
const message = document.getElementById('message');
const startBtn = document.getElementById('startBtn');
// Game state
let keys = {};
let car, obstacles, score, speed, gameState, spawnTimer, difficulty, buildings;
function resetGame() {
car = { x: W/2 - CAR_W/2, y: H-140, lane: 1, vy: 0, crashed: false };
obstacles = [];
buildings = [];
for (let row=0; row<BUILDING_ROWS; row++) {
let y = H - (row+1)*BUILDING_DIST;
genBuildingsRow(y, row%2===0 ? 0 : BUILDING_DIST/2);
}
score = 0;
speed = 7;
spawnTimer = 0;
difficulty = 1;
}
function genBuildingsRow(y, phase) {
// Left side
let bx = W/2 - ROAD_W/2 - BUILDING_W;
while (bx > 0) {
let clr = CITY_COLORS[Math.floor(Math.random()*CITY_COLORS.length)];
let h = 80+Math.random()*120;
buildings.push({x: bx, y, w: BUILDING_W-8, h, clr});
bx -= BUILDING_W;
}
// Right side
bx = W/2 + ROAD_W/2 + 8;
while (bx < W - BUILDING_W) {
let clr = CITY_COLORS[Math.floor(Math.random()*CITY_COLORS.length)];
let h = 80+Math.random()*120;
buildings.push({x: bx, y, w: BUILDING_W-8, h, clr});
bx += BUILDING_W;
}
}
function randomLane() {
return Math.floor(Math.random()*LANE_COUNT);
}
function laneX(lane) {
return W/2 - ROAD_W/2 + (lane+0.5)*(ROAD_W / LANE_COUNT) - CAR_W/2;
}
function startGame() {
resetGame();
gameState = 'play';
overlay.classList.remove('active');
}
function crash() {
gameState = 'crashed';
overlay.classList.add('active');
message.innerHTML = `CRASH!<br>SCORE: ${score}`;
startBtn.innerText = "Restart";
}
function update(dt) {
if (gameState !== 'play') return;
// Car controls (left/right)
if (!car.crashed) {
if (keys["ArrowLeft"] || keys["a"]) {
if (car.lane > 0) car.lane -= TURN_SPEED*dt/16;
if (car.lane < 0) car.lane = 0;
} else if (keys["ArrowRight"] || keys["d"]) {
if (car.lane < LANE_COUNT-1) car.lane += TURN_SPEED*dt/16;
if (car.lane > LANE_COUNT-1) car.lane = LANE_COUNT-1;
}
car.x += (laneX(Math.round(car.lane)) - car.x) * 0.16;
}
// Car speed (up/down)
if (keys["ArrowUp"] || keys["w"]) {
speed += SPEED_ACC*dt/16;
} else if (keys["ArrowDown"] || keys["s"]) {
speed -= SPEED_DEC*dt/16;
} else {
speed -= SPEED_DEC*0.32*dt/16;
}
speed = Math.max(6, Math.min(SPEED_MAX, speed));
score += Math.floor(speed*dt/80);
// Difficulty scales
difficulty = 1 + Math.floor(score/320);
// Obstacles
for (let o of obstacles) {
o.y += speed*dt/14 * (1.08 + 0.06*difficulty);
}
// Remove passed
obstacles = obstacles.filter(o => o.y < H+OBSTACLE_H);
// Spawn
spawnTimer -= dt;
if (spawnTimer <= 0) {
let usedLanes = obstacles.filter(o => o.y < 200).map(o => o.lane);
let availLanes = [];
for (let l=0; l<LANE_COUNT; l++)
if (!usedLanes.includes(l)) availLanes.push(l);
if (availLanes.length > 0) {
let lane = availLanes[Math.floor(Math.random()*availLanes.length)];
let colors = ["#25c7fc","#fc5a27","#ffdf3b"];
obstacles.push({
x: laneX(lane),
y: -OBSTACLE_H,
lane,
clr: colors[Math.floor(Math.random()*colors.length)]
});
}
spawnTimer = SPAWN_INTERVAL/(difficulty*1.2 + 0.5 + Math.random()*0.3);
}
// Collision
for (let o of obstacles) {
let dx = car.x - o.x;
let dy = car.y - o.y;
if (Math.abs(dx) < CAR_W-10 && Math.abs(dy) < CAR_H-16) {
car.crashed = true;
setTimeout(crash, 320);
break;
}
}
// Move buildings
for (let b of buildings) b.y += speed*dt/20;
// remove passed rows, generate new
if (buildings[0].y > H) {
buildings = buildings.filter(b => b.y <= H);
let y = buildings[buildings.length-1].y - BUILDING_DIST;
genBuildingsRow(y, Math.random()*BUILDING_DIST);
}
}
function drawRoad() {
// Road
ctx.save();
ctx.beginPath();
ctx.rect(W/2-ROAD_W/2, 0, ROAD_W, H);
ctx.fillStyle = "#232439";
ctx.shadowColor = '#16161d';
ctx.shadowBlur = 48;
ctx.fill();
ctx.restore();
// Lanes
ctx.strokeStyle = "#eee";
ctx.setLineDash([22, 18]);
ctx.lineWidth = ROAD_MARK_W;
for (let l=1; l<LANE_COUNT; l++) {
let x = W/2-ROAD_W/2 + l*ROAD_W/LANE_COUNT;
ctx.beginPath();
ctx.moveTo(x,0);
ctx.lineTo(x,H);
ctx.stroke();
}
ctx.setLineDash([]);
}
// Simple building top w/ windows
function drawBuilding(b) {
ctx.save();
ctx.beginPath();
ctx.rect(b.x, b.y-b.h, b.w, b.h);
ctx.fillStyle = b.clr;
ctx.globalAlpha = 0.92;
ctx.fill();
// Windows (randomized)
ctx.globalAlpha = 0.65;
ctx.fillStyle = Math.random() > 0.5 ? "#ffd" : "#dfe0f7";
for (let wy=0; wy<b.h-12; wy+=16) {
for (let wx=4; wx<b.w-8; wx+=16)
if (Math.random() > 0.4)
ctx.fillRect(b.x+wx, b.y-b.h+wy, 6, 10);
}
ctx.restore();
}
function drawObstacles() {
for (let o of obstacles) {
// Car shape
ctx.save();
ctx.translate(o.x+OBSTACLE_W/2, o.y+OBSTACLE_H/2);
ctx.rotate(Math.sin(o.y/100)*0.01);
// body
ctx.beginPath();
ctx.roundRect(-OBSTACLE_W/2, -OBSTACLE_H/2, OBSTACLE_W, OBSTACLE_H, 11);
ctx.fillStyle = o.clr;
ctx.shadowColor = "#111";
ctx.shadowBlur = 10;
ctx.fill();
// windows
ctx.fillStyle = "#aef4fa";
ctx.globalAlpha = 0.6;
ctx.fillRect(-14, -OBSTACLE_H/2+13, 28, 20);
ctx.globalAlpha = 1;
// wheels
ctx.fillStyle = "#222";
ctx.fillRect(-OBSTACLE_W/2+5, -OBSTACLE_H/2+8, 8, 12);
ctx.fillRect(OBSTACLE_W/2-13, -OBSTACLE_H/2+8, 8, 12);
ctx.fillRect(-OBSTACLE_W/2+5, OBSTACLE_H/2-20, 8, 12);
ctx.fillRect(OBSTACLE_W/2-13, OBSTACLE_H/2-20, 8, 12);
ctx.restore();
}
}
function drawCar() {
ctx.save();
ctx.translate(car.x+CAR_W/2, car.y+CAR_H/2);
let tilt = 0;
if (car.lane < Math.round(car.lane)-0.05)
tilt = -0.13;
else if (car.lane > Math.round(car.lane)+0.05)
tilt = 0.13;
if (car.crashed) tilt = Math.sin(Date.now()/40)*0.38;
ctx.rotate(tilt);
// shadow
ctx.globalAlpha = 0.32;
ctx.fillStyle = "#011";
ctx.ellipse(0, 32, 17, 11, 0, 0, 2*Math.PI);
ctx.fill();
ctx.globalAlpha = 1;
// Main body
ctx.beginPath();
ctx.roundRect(-CAR_W/2, -CAR_H/2, CAR_W, CAR_H, 12);
ctx.fillStyle = "#ff2954";
ctx.shadowColor = "#3f1637";
ctx.shadowBlur = 14;
ctx.fill();
// Windows
ctx.beginPath();
ctx.moveTo(-13,-CAR_H/2+13);
ctx.lineTo(13,-CAR_H/2+13);
ctx.lineTo(16, 7);
ctx.lineTo(-16,7);
ctx.closePath();
ctx.fillStyle = "#aef4fa";
ctx.globalAlpha = 0.65;
ctx.fill();
ctx.globalAlpha = 1;
// Wheels
ctx.save();
ctx.rotate(0.01*Math.sin(Date.now()/130));
ctx.fillStyle = "#23262f";
// Front wheels
ctx.fillRect(-CAR_W/2+6, -CAR_H/2+8, 8, 12);
ctx.fillRect(CAR_W/2-14, -CAR_H/2+8, 8, 12);
// Rear wheels
ctx.fillRect(-CAR_W/2+6, CAR_H/2-20, 8, 12);
ctx.fillRect(CAR_W/2-14, CAR_H/2-20, 8, 12);
ctx.restore();
// Lights
// Headlights
ctx.beginPath();
ctx.ellipse(-8,-CAR_H/2+6,3,5,0,0,Math.PI*2);
ctx.ellipse(8,-CAR_H/2+6,3,5,0,0,Math.PI*2);
ctx.fillStyle = "#fff7c0";
ctx.globalAlpha = 0.62 + 0.22 * Math.sin(Date.now()/80);
ctx.fill();
ctx.restore();
}
// City background gradient
function drawSky() {
let grad = ctx.createLinearGradient(0,0,0,H);
grad.addColorStop(0, "#3f53a0");
grad.addColorStop(0.33, "#617cc1");
grad.addColorStop(1, "#191d2a");
ctx.fillStyle = grad;
ctx.fillRect(0,0,W,H);
}
// HUD
function drawHud() {
ctx.save();
ctx.font = "bold 24px Segoe UI, Arial";
ctx.fillStyle = "#fff";
ctx.shadowColor = '#191c28';
ctx.shadowBlur = 8;
ctx.fillText("SCORE: "+score, 24, 38);
ctx.font = "bold 14px Segoe UI, Arial";
ctx.fillStyle = "#cdf";
ctx.shadowBlur = 4;
ctx.fillText("Speed: "+speed.toFixed(1)+" / "+SPEED_MAX, 24, 56);
ctx.restore();
// Hints
if (score<40) {
ctx.save();
ctx.font = "18px Segoe UI";
ctx.fillStyle = "rgba(255,255,255,0.7)";
ctx.fillText("← → or A/D: Move / ↑ or W: Speed up", W/2-138, H-38);
ctx.restore();
}
}
function render() {
drawSky();
// Draw buildings
for (let b of buildings) drawBuilding(b);
drawRoad();
drawObstacles();
drawCar();
drawHud();
}
let lastT = performance.now();
function loop(){
let now = performance.now();
let dt = Math.min(38, now-lastT);
lastT = now;
if (gameState == 'play') update(dt);
render();
requestAnimationFrame(loop);
}
// Input handlers
document.addEventListener('keydown', e => {
keys[e.key.toLowerCase()] = true;
if ((e.key === " " || e.key === "Enter") && overlay.classList.contains('active')) {
startBtn.click();
}
});
document.addEventListener('keyup', e => { keys[e.key.toLowerCase()] = false; });
startBtn.onclick = () => {
startGame();
}
function init() {
// Initial overlay
overlay.classList.add('active');
message.innerHTML = "CITY RACER<br><span style='font-size:1vw;letter-spacing:1px;'>Fast-paced arcade action<br>Dodge cars and reach a high score!</span>";
startBtn.innerText = "Start";
gameState = 'menu';
loop();
}
init();
</script>
</body>
</html>
```Below is a simple implementation of a fun, fast-paced arcade racer in JavaScript, HTML, and CSS. The game is set in a cityscape and uses keyboard controls for movement. The car accelerates automatically, and the player must avoid obstacles (represented by red squares).
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>City Racer</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #87CEEB; /* Sky blue background */
}
canvas {
display: block;
}
.score {
position: absolute;
top: 10px;
left: 10px;
font-size: 24px;
font-family: Arial, sans-serif;
color: white;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
</style>
</head>
<body>
<div class="score">Score: <span id="score">0</span></div>
<canvas id="gameCanvas"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const scoreDisplay = document.getElementById('score');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const player = {
x: canvas.width / 2 - 20,
y: canvas.height - 100,
width: 40,
height: 20,
speed: 5,
color: '#FF6347' // Tomato color
};
let obstacles = [];
let score = 0;
let gameSpeed = 3;
let keys = {
ArrowLeft: false,
ArrowRight: false
};
function createObstacle() {
const size = Math.floor(Math.random() * 30) + 20;
const gap = Math.floor(Math.random() * 100) + 50;
const x = canvas.width;
const y = Math.random() * (canvas.height - gap - size);
obstacles.push({ x, y, width: size, height: size, passed: false });
}
function update() {
player.x += (keys.ArrowRight ? player.speed : 0) - (keys.ArrowLeft ? player.speed : 0);
if (player.x < 0) player.x = 0;
if (player.x + player.width > canvas.width) player.x = canvas.width - player.width;
obstacles.forEach(obstacle => {
obstacle.x -= gameSpeed;
if (!obstacle.passed && obstacle.x + obstacle.width < player.x) {
obstacle.passed = true;
score++;
scoreDisplay.textContent = score;
}
if (obstacle.x + obstacle.width > player.x && obstacle.x < player.x + player.width &&
obstacle.y + obstacle.height > player.y && obstacle.y < player.y + player.height) {
cancelAnimationFrame(animationFrame);
alert('Game Over! Score: ' + score);
document.location.reload();
}
});
obstacles = obstacles.filter(obstacle => obstacle.x + obstacle.width > 0);
if (Math.random() < 0.02) {
createObstacle();
}
}
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw cityscape background
ctx.fillStyle = '#CCCCCC'; // Light gray buildings
ctx.fillRect(0, canvas.height - 50, canvas.width, 50);
// Draw road
ctx.fillStyle = '#444444'; // Dark gray road
ctx.fillRect(0, canvas.height - 100, canvas.width, 100);
// Draw lane lines
ctx.strokeStyle = '#FFFFFF';
ctx.lineWidth = 4;
ctx.setLineDash([20, 20]);
ctx.beginPath();
for (let i = -5; i <= canvas.width; i += 50) {
ctx.moveTo(i, canvas.height - 80);
ctx.lineTo(i + 20, canvas.height - 80);
}
ctx.stroke();
ctx.setLineDash([]);
// Draw player
ctx.fillStyle = player.color;
ctx.fillRect(player.x, player.y, player.width, player.height);
// Draw obstacles
ctx.fillStyle = '#FF0000'; // Red obstacles
obstacles.forEach(obstacle => {
ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
});
}
function gameLoop() {
update();
draw();
const animationFrame = requestAnimationFrame(gameLoop);
}
document.addEventListener('keydown', e => {
if (e.key in keys) {
keys[e.key] = true;
}
});
document.addEventListener('keyup', e => {
if (e.key in keys) {
keys[e.key] = false;
}
});
createObstacle();
gameLoop();
</script>
</body>
</html>
```
**Features of the Game:**
1. **Auto-Acceleration:** The car moves forward automatically, and the player only needs to steer left or right using the arrow keys.
2. **Obstacle Avoidance:** Red squares appear randomly on the road, and the player must avoid them to stay alive.
3. **Scoring System:** The score increases as the player overtakes obstacles.
4. **Game Over:** The game ends if the player collides with an obstacle, displaying the final score.
5. **Responsive Controls:** Smooth left and right movement using the arrow keys.
**To run the game:**
1. Copy the code into an HTML file (e.g., `city-racer.html`).
2. Open the file in a web browser.
3. Use the left and right arrow keys to control the car.
Enjoy the fast-paced city racing action!```html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
body { margin: 0; overflow: hidden; background: #000; }
canvas { display: block; background: #0a0a0a; }
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let keys = { ArrowLeft: false, ArrowRight: false, ArrowUp: false, ArrowDown: false };
let lastTime = 0;
let gameSpeed = 4;
let spawnTimer = 0;
let score = 0;
let highScore = 0;
let gameOver = false;
// Layers
const layers = {
stars: Array.from({length: 100}, () => ({
x: Math.random()*canvas.width,
y: Math.random()*canvas.height,
size: Math.random()*1.5,
opacity: Math.random()
})),
buildings: [
{ x: 0, y: 40, width: 80, height: 200, speed: 1.2 },
{ x: 150, y: 60, width: 50, height: 180, speed: 1.2 },
{ x: 300, y: 30, width: 90, height: 220, speed: 1.2 },
// Middle ground
{ x: 0, y: 300, width: 60, height: 400, speed: 1.5 },
{ x: 120, y: 250, width: 70, height: 450, speed: 1.5 },
{ x: 250, y: 280, width: 30, height: 420, speed: 1.5 },
// Road reflectors
{ x: 0, y: canvas.height-20, width: 10, height: 5, speed: 2, color: '#ff0' },
{ x: 30, y: canvas.height-20, width: 10, height: 5, speed: 2, color: '#ff0' }
],
enemies: Array.from({length: 5}, () => ({
x: canvas.width + Math.random()*200,
y: 600 - Math.random()*300,
width: 40,
height: 80,
speed: 2.5 + Math.random()
}))
};
const player = {
x: canvas.width/2 - 20,
y: canvas.height - 120,
width: 40,
height: 80,
color: '#ff3333'
};
function resetGame() {
score = 0;
gameOver = false;
player.x = canvas.width/2 - 20;
layers.enemies.forEach(e => {
e.x = canvas.width + Math.random()*200;
e.y = 600 - Math.random()*300;
});
requestAnimationFrame(gameLoop);
}
function drawBackground() {
// Night sky
ctx.fillStyle = '#0a0a0a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Stars
ctx.fillStyle = '#fff';
layers.stars.forEach(star => {
ctx.globalAlpha = star.opacity;
ctx.fillRect(star.x, star.y, star.size, star.size);
});
ctx.globalAlpha = 1;
// Buildings
layers.buildings.forEach(b => {
ctx.fillStyle = b.color || '#222222';
ctx.fillRect(b.x, b.y, b.width, b.height);
});
}
function drawPlayer() {
ctx.fillStyle = player.color;
ctx.beginPath();
ctx.roundRect(player.x, player.y, player.width, player.height, 5).fill();
}
function updatePlayer() {
if(keys.ArrowLeft) player.x -= 6;
if(keys.ArrowRight) player.x += 6;
// Boundary check
player.x = Math.max(0, Math.min(player.x, canvas.width - player.width));
}
function drawEnemies() {
ctx.fillStyle = '#33ff33';
layers.enemies.forEach(enemy => {
ctx.beginPath();
ctx.roundRect(enemy.x, enemy.y, enemy.width, enemy.height, 8).fill();
});
}
function updateEnemies() {
layers.enemies.forEach(enemy => {
enemy.x -= enemy.speed;
// Respawn if offscreen
if(enemy.x + enemy.width < 0) {
enemy.x = canvas.width + Math.random()*200;
enemy.y = 600 - Math.random()*300;
enemy.speed = 2.5 + Math.random();
score += 1;
if(score > highScore) highScore = score;
}
});
}
function drawHUD() {
ctx.fillStyle = '#fff';
ctx.font = '20px Arial';
ctx.fillText(`Score: ${score}`, 20, 30);
ctx.fillText(`High: ${highScore}`, 20, 50);
if(gameOver) {
ctx.font = '50px Arial';
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, canvas.height/2-60, canvas.width, 120);
ctx.fillStyle = '#ff0000';
ctx.fillText('GAME OVER', canvas.width/2-100, canvas.height/2-10);
ctx.fillStyle = '#fff';
ctx.fillText('Press Space to Restart', canvas.width/2-130, canvas.height/2+30);
}
}
function checkCollisions() {
const playerLeft = player.x;
const playerRight = player.x + player.width;
const playerTop = player.y;
const playerBottom = player.y + player.height;
layers.enemies.forEach(enemy => {
const enemyLeft = enemy.x;
const enemyRight = enemy.x + enemy.width;
const enemyTop = enemy.y;
const enemyBottom = enemy.y + enemy.height;
if(playerRight > enemyLeft &&
playerLeft < enemyRight &&
playerBottom > enemyTop &&
playerTop < enemyBottom) {
gameOver = true;
}
});
}
function updateBackground() {
// Move buildings
layers.buildings.forEach(b => {
b.x -= b.speed;
// Reset position if offscreen
if(b.x <= -b.width) {
b.x = canvas.width + b.width;
b.y = 50 + Math.random()*(canvas.height/2 - 20);
b.width = 30 + Math.random()*60;
b.height = 50 + Math.random()*300;
}
});
// Update stars (modulate opacity)
layers.stars.forEach(st => {
st.opacity = 0.5 + 0.5*Math.sin(Date.now()*0.005 + st.x);
});
}
function gameLoop(timestamp) {
if(gameOver) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
updateBackground();
drawBackground();
updatePlayer();
drawPlayer();
updateEnemies();
drawEnemies();
checkCollisions();
drawHUD();
requestAnimationFrame(gameLoop);
}
// Key controls
window.addEventListener('keydown', e => {
if(e.code in keys) keys[e.code] = true;
if(e.code === 'Space' && gameOver) resetGame();
});
window.addEventListener('keyup', e => {
if(e.code in keys) keys[e.code] = false;
});
// Path rounding helper - easier for rounded car coords
CanvasRenderingContext2D.prototype.roundRect = function (x, y, width, height, radius) {
if (typeof radius === 'number') radius = {tl: radius, tr: radius, br: radius, bl: radius};
this.beginPath();
this.moveTo(x + radius.tl, y);
this.lineTo(x + width - radius.tr, y);
this.quadraticCurveTo(x + width, y, x + width, y + radius.tr);
this.lineTo(x + width, y + height - radius.br);
this.quadraticCurveTo(x + width, y + height, x + width - radius.br, y + height);
this.lineTo(x + radius.bl, y + height);
this.quadraticCurveTo(x, y + height, x, y + height - radius.bl);
this.lineTo(x, y + radius.tl);
this.quadraticCurveTo(x, y, x + radius.tl, y);
this.closePath();
return this;
};
// Start game
resetGame();
</script>
</body>
</html>
``````html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>City Arcade Racer</title>
<style>
* { margin:0; padding:0; box-sizing:border-box; }
body { background:#333; display:flex; align-items:center; justify-content:center; height:100vh; }
canvas { background:#444; display:block; }
</style>
</head>
<body>
<canvas id="game"></canvas>
<script>
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 600;
// Road & lanes
const lanes = 3;
const roadWidth = 200;
const roadX = (canvas.width - roadWidth) / 2;
const laneWidth = roadWidth / lanes;
// Player car
const carW = laneWidth * 0.6;
const carH = 80;
let laneIndex = 1; // 0,1,2
let carX = roadX + laneIndex * laneWidth + (laneWidth - carW) / 2;
const carY = canvas.height - carH - 20;
// Controls
let leftPressed = false, rightPressed = false, upPressed = false, downPressed = false, spacePressed = false;
// Game state
let speed = 5;
const maxSpeed = 15, minSpeed = 3;
let frameCount = 0, spawnInterval = 90;
let obstacles = [];
let buildings = [];
let laneMarkerOffset = 0;
let score = 0;
let gameOver = false;
// Initialize buildings for city sides
function initBuildings() {
buildings = [];
for (let i = 0; i < 15; i++) {
['left','right'].forEach(side => {
const bWidth = 50 + Math.random() * 50;
const bHeight = 100 + Math.random() * 150;
const x = side === 'left'
? roadX - 20 - bWidth
: roadX + roadWidth + 20;
const y = Math.random() * -canvas.height;
buildings.push({ x, y, width: bWidth, height: bHeight, color: '#999' });
});
}
}
// Spawn an obstacle car in a random lane
function spawnObstacle() {
const lane = Math.floor(Math.random() * lanes);
const x = roadX + lane * laneWidth + (laneWidth - carW) / 2;
obstacles.push({ x, y: -carH, width: carW, height: carH, color: '#e33' });
spawnInterval = 60 + Math.random() * 60;
}
// Reset game
function resetGame() {
laneIndex = 1;
carX = roadX + laneIndex * laneWidth + (laneWidth - carW) / 2;
speed = 5;
frameCount = 0; score = 0;
obstacles = [];
laneMarkerOffset = 0;
gameOver = false;
initBuildings();
requestAnimationFrame(gameLoop);
}
// Key handlers
document.addEventListener('keydown', e => {
if (e.key === 'ArrowLeft' || e.key === 'a') leftPressed = true;
if (e.key === 'ArrowRight' || e.key === 'd') rightPressed = true;
if (e.key === 'ArrowUp' || e.key === 'w') upPressed = true;
if (e.key === 'ArrowDown' || e.key === 's') downPressed = true;
if (e.key === ' ' || e.key === 'Spacebar') spacePressed = true;
});
document.addEventListener('keyup', e => {
if (e.key === 'ArrowLeft' || e.key === 'a') leftPressed = false;
if (e.key === 'ArrowRight' || e.key === 'd') rightPressed = false;
if (e.key === 'ArrowUp' || e.key === 'w') upPressed = false;
if (e.key === 'ArrowDown' || e.key === 's') downPressed = false;
if (e.key === ' ' || e.key === 'Spacebar') spacePressed = false;
});
// Main loop
function gameLoop() {
update();
draw();
if (!gameOver) requestAnimationFrame(gameLoop);
}
function update() {
frameCount++;
// Move car between lanes
if (leftPressed && laneIndex > 0) { laneIndex--; leftPressed = false; }
if (rightPressed && laneIndex < lanes - 1) { laneIndex++; rightPressed = false; }
carX = roadX + laneIndex * laneWidth + (laneWidth - carW) / 2;
// Speed control
if (upPressed) speed = Math.min(maxSpeed, speed + 0.1);
if (downPressed) speed = Math.max(minSpeed, speed - 0.1);
// Lane markers offset
laneMarkerOffset = (laneMarkerOffset + speed) % 40;
// Buildings parallax
buildings.forEach(b => {
b.y += speed * 0.5;
if (b.y > canvas.height) {
b.y = -b.height - Math.random() * canvas.height;
b.height = 100 + Math.random() * 150;
}
});
// Obstacles
if (frameCount % Math.floor(spawnInterval) === 0) spawnObstacle();
obstacles.forEach(o => o.y += speed);
obstacles = obstacles.filter(o => o.y < canvas.height + o.height);
// Collision detection
obstacles.forEach(o => {
if (o.x < carX + carW &&
o.x + o.width > carX &&
o.y < carY + carH &&
o.y + o.height > carY) {
gameOver = true;
}
});
// Score
score += speed * 0.1;
// Restart
if (gameOver && spacePressed) {
resetGame();
}
}
function draw() {
// Clear
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Grass sides
ctx.fillStyle = '#072';
ctx.fillRect(0, 0, roadX, canvas.height);
ctx.fillRect(roadX + roadWidth, 0, roadX, canvas.height);
// Buildings
buildings.forEach(b => {
ctx.fillStyle = b.color;
ctx.fillRect(b.x, b.y, b.width, b.height);
});
// Road
ctx.fillStyle = '#555';
ctx.fillRect(roadX, 0, roadWidth, canvas.height);
// Road edges
ctx.strokeStyle = '#fff';
ctx.lineWidth = 4;
ctx.beginPath();
ctx.moveTo(roadX, 0); ctx.lineTo(roadX, canvas.height);
ctx.moveTo(roadX + roadWidth, 0); ctx.lineTo(roadX + roadWidth, canvas.height);
ctx.stroke();
// Lane markers
ctx.strokeStyle = '#fff';
ctx.lineWidth = 2;
ctx.setLineDash([20, 20]);
for (let i = 1; i < lanes; i++) {
const x = roadX + i * laneWidth;
ctx.beginPath();
ctx.moveTo(x, -laneMarkerOffset);
ctx.lineTo(x, canvas.height);
ctx.stroke();
}
ctx.setLineDash([]);
// Obstacles
obstacles.forEach(o => {
ctx.fillStyle = o.color;
ctx.fillRect(o.x, o.y, o.width, o.height);
});
// Player car
ctx.fillStyle = '#3af';
ctx.fillRect(carX, carY, carW, carH);
// Score
ctx.fillStyle = '#fff';
ctx.font = '20px sans-serif';
ctx.fillText('Score: ' + Math.floor(score), 10, 30);
// Game Over
if (gameOver) {
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#f33';
ctx.font = '40px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('GAME OVER', canvas.width/2, canvas.height/2 - 20);
ctx.fillStyle = '#fff';
ctx.font = '20px sans-serif';
ctx.fillText('Press SPACE to restart', canvas.width/2, canvas.height/2 + 20);
}
}
// Start
initBuildings();
resetGame();
</script>
</body>
</html>
```**City Racer Game**
=====================
### Overview
Here is a simple implementation of a fast-paced arcade racer game set in a city using JavaScript and HTML5 Canvas.
### Game Description
* The game features a player-controlled car that can be steered left and right using the arrow keys or 'A' and 'D' keys.
* The objective is to avoid obstacles (other cars) while navigating through the city streets.
* The game gets progressively harder as the player's score increases.
### Code
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>City Racer</title>
<style>
/* Add some basic styling to our game */
canvas {
border: 1px solid black;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<script>
// Get the canvas element
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Set the canvas dimensions
canvas.width = 800;
canvas.height = 600;
// Define some game constants
const ROAD_WIDTH = 400;
const CAR_WIDTH = 50;
const CAR_HEIGHT = 100;
const OBSTACLE_WIDTH = 50;
const OBSTACLE_HEIGHT = 100;
const OBSTACLE_SPAWN_RATE = 1000; // milliseconds
const GAME_SPEED = 5; // pixels per frame
// Define some game variables
let playerCarX = canvas.width / 2;
let playerCarY = canvas.height - CAR_HEIGHT - 20;
let obstacles = [];
let score = 0;
let lastObstacleSpawn = 0;
// Main game loop
function update() {
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw the road
ctx.fillStyle = 'gray';
ctx.fillRect((canvas.width - ROAD_WIDTH) / 2, 0, ROAD_WIDTH, canvas.height);
// Draw the player's car
ctx.fillStyle = 'blue';
ctx.fillRect(playerCarX, playerCarY, CAR_WIDTH, CAR_HEIGHT);
// Move and draw obstacles
for (let i = 0; i < obstacles.length; i++) {
obstacles[i].y += GAME_SPEED;
ctx.fillStyle = 'red';
ctx.fillRect(obstacles[i].x, obstacles[i].y, OBSTACLE_WIDTH, OBSTACLE_HEIGHT);
// Check for collision with the player's car
if (checkCollision(playerCarX, playerCarY, CAR_WIDTH, CAR_HEIGHT, obstacles[i].x, obstacles[i].y, OBSTACLE_WIDTH, OBSTACLE_HEIGHT)) {
alert('Game Over! Your score: ' + score);
// Reset the game state
obstacles = [];
score = 0;
lastObstacleSpawn = 0;
}
// Remove obstacles that are off the screen
if (obstacles[i].y > canvas.height) {
obstacles.splice(i, 1);
score++;
}
}
// Spawn new obstacles
const now = Date.now();
if (now - lastObstacleSpawn > OBSTACLE_SPAWN_RATE) {
lastObstacleSpawn = now;
obstacles.push({
x: Math.random() * (canvas.width - OBSTACLE_WIDTH),
y: -OBSTACLE_HEIGHT
});
}
// Update the player's car position based on user input
if (isKeyPressed('ArrowLeft') || isKeyPressed('a')) {
playerCarX -= 5;
}
if (isKeyPressed('ArrowRight') || isKeyPressed('d')) {
playerCarX += 5;
}
// Ensure the player's car stays within the road boundaries
playerCarX = Math.max((canvas.width - ROAD_WIDTH) / 2, Math.min(playerCarX, (canvas.width + ROAD_WIDTH) / 2 - CAR_WIDTH));
// Display the score
ctx.fillStyle = 'black';
ctx.font = '24px Arial';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.fillText('Score: ' + score, 10, 10);
// Request the next frame
requestAnimationFrame(update);
}
// Helper function to check for collision between two rectangles
function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2) {
return x1 < x2 + w2 &&
x1 + w1 > x2 &&
y1 < y2 + h2 &&
y1 + h1 > y2;
}
// Helper function to check if a key is currently pressed
const keyState = {};
document.addEventListener('keydown', (e) => {
keyState[e.key] = true;
});
document.addEventListener('keyup', (e) => {
keyState[e.key] = false;
});
function isKeyPressed(key) {
return keyState[key] === true;
}
// Start the game
update();
</script>
</body>
</html>
```
### How to Run
1. Save the above code in an HTML file (e.g., `city_racer.html`).
2. Open the HTML file in a web browser.
3. Use the arrow keys or 'A' and 'D' keys to control the player's car.
4. Avoid obstacles and try to survive for as long as possible.
### Features
* Fast-paced gameplay with increasing difficulty
* Simple yet intuitive controls
* Collision detection with obstacles
* Scoring system
### Future Improvements
* Add different types of obstacles (e.g., pedestrians, roadblocks)
* Introduce power-ups or bonuses (e.g., speed boost, shield)
* Implement a high score system
* Enhance the game's visuals and audio (e.g., add graphics, sound effects, music)```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Arcade City Racer</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #1a1a1a; /* Dark background */
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
font-family: sans-serif;
}
canvas {
border: 1px solid #000;
background-color: #505050; /* Default road color */
display: block; /* Remove extra space below canvas */
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="400" height="600"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Game State
let gameState = 'start'; // 'start', 'playing', 'gameOver'
// Game Constants
const GAME_WIDTH = canvas.width;
const GAME_HEIGHT = canvas.height;
const LANE_WIDTH = 80; // Width of one lane
const NUM_LANES = 3; // Number of visible driving lanes
const ROAD_LINE_WIDTH = 5;
const ROAD_LINE_COLOR = '#ffffff'; // White dashed lines
const ROAD_COLOR = '#505050'; // Grey road color
const SKY_COLOR = '#87CEEB'; // Sky blue
const BUILDING_COLOR_1 = '#4d4d4d'; // Dark grey building
const BUILDING_COLOR_2 = '#333333'; // Darker grey building
// Calculate Road Dimensions
const ROAD_WIDTH = NUM_LANES * LANE_WIDTH;
const ROAD_X = GAME_WIDTH / 2 - ROAD_WIDTH / 2;
// Player Car
const CAR_WIDTH = LANE_WIDTH * 0.6; // Make car slightly narrower than lane
const CAR_HEIGHT = CAR_WIDTH * 1.75; // Proportionate height
let carX = ROAD_X + LANE_WIDTH + (LANE_WIDTH / 2) - (CAR_WIDTH / 2); // Start in the middle lane
let carY = GAME_HEIGHT - CAR_HEIGHT - 30; // Position near bottom
const CAR_SPEED_X = 7; // How fast car moves left/right
// Game Variables
let speed = 6; // Initial downward speed of objects
let score = 0;
let highScore = 0;
let frameCount = 0;
// Obstacles (Other cars/objects)
const OBSTACLE_WIDTH = LANE_WIDTH * 0.6;
const OBSTACLE_HEIGHT = OBSTACLE_WIDTH * 1.75; // Same size as player car
const OBSTACLE_BASE_SPAWN_RATE = 90; // Frames between spawns at low speed
let obstacles = [];
const OBSTACLE_COLORS = ['#00ffff', '#ffff00', '#ff00ff', '#00ff00', '#ff8800', '#0000ff']; // Various car colors
// City Elements (Buildings/Side Stuff)
const BUILDING_MIN_HEIGHT = 150; // Taller buildings
const BUILDING_MAX_HEIGHT = 500;
const BUILDING_MIN_WIDTH = 60; // Wider buildings
const BUILDING_MAX_WIDTH = 120;
const BUILDING_SPAWN_RATE = 50; // Frames between building spawns
let buildingsLeft = [];
let buildingsRight = [];
// Input Handling
let keys = {
ArrowLeft: false,
ArrowRight: false,
Enter: false
};
document.addEventListener('keydown', (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = true;
}
// Prevent default behavior for arrow keys and space (prevents scrolling)
if (['ArrowLeft', 'ArrowRight', ' '].includes(e.key)) {
e.preventDefault();
}
});
document.addEventListener('keyup', (e) => {
if (keys.hasOwnProperty(e.key)) {
keys[e.key] = false;
}
});
// Game Functions
function initGame() {
// Start car in the middle lane
carX = ROAD_X + LANE_WIDTH + (LANE_WIDTH / 2) - (CAR_WIDTH / 2);
carY = GAME_HEIGHT - CAR_HEIGHT - 30; // Keep car near bottom
speed = 6; // Initial speed
score = 0;
frameCount = 0;
obstacles = [];
buildingsLeft = [];
buildingsRight = [];
// Spawn a few initial buildings off-screen
for(let i = 0; i < 7; i++) { // Spawn enough to fill sides initially
spawnBuilding('left', -i * (BUILDING_MAX_HEIGHT/4 + Math.random()*50));
spawnBuilding('right', -i * (BUILDING_MAX_HEIGHT/4 + Math.random()*50));
}
gameState = 'playing';
}
function update() {
if (gameState !== 'playing') return;
frameCount++;
score += Math.floor(speed / 6); // Increase score based on speed
// Increase speed gradually
if (frameCount % 120 === 0) { // Increase speed every 2 seconds at 60fps
speed += 0.3;
speed = Math.min(speed, 25); // Cap speed for playability
}
// Player car movement
if (keys.ArrowLeft) { carX -= CAR_SPEED_X; }
if (keys.ArrowRight) { carX += CAR_SPEED_X; }
// Clamp carX to road boundaries (allows movement within the 3 lanes)
const minCarX = ROAD_X;
const maxCarX = ROAD_X + ROAD_WIDTH - CAR_WIDTH;
carX = Math.max(minCarX, Math.min(maxCarX, carX));
// Update obstacles
obstacles.forEach(obstacle => {
obstacle.y += speed * 0.9; // Obstacles move slightly slower than road speed
});
// Remove obstacles that are off-screen (top or bottom - shouldn't go top with current speed)
obstacles = obstacles.filter(obstacle => obstacle.y < GAME_HEIGHT);
// Spawn new obstacles
const currentObstacleSpawnRate = Math.max(30, OBSTACLE_BASE_SPAWN_RATE - Math.floor(speed * 1.5)); // Spawn rate increases with speed, clamped at minimum 30 frames
if (frameCount % currentObstacleSpawnRate === 0) {
spawnObstacle();
}
// Update buildings
[...buildingsLeft, ...buildingsRight].forEach(building => {
building.y += speed * 0.3; // Buildings move significantly slower than road/obstacles for parallax effect
});
buildingsLeft = buildingsLeft.filter(building => building.y - building.height < GAME_HEIGHT); // Keep if any part is on screen
buildingsRight = buildingsRight.filter(building => building.y - building.height < GAME_HEIGHT);
// Spawn new buildings
// Spawn based on frame count AND ensure there's space for a new building off-screen
const lastLeft = buildingsLeft.length > 0 ? buildingsLeft[buildingsLeft.length - 1] : null;
const lastRight = buildingsRight.length > 0 ? buildingsRight[buildingsRight.length - 1] : null;
if (frameCount % BUILDING_SPAWN_RATE === 0) {
if (!lastLeft || lastLeft.y + lastLeft.height + speed * 0.3 > -BUILDING_MIN_HEIGHT/2) { // Check if last building is close to screen top
spawnBuilding('left');
}
if (!lastRight || lastRight.y + lastRight.height + speed * 0.3 > -BUILDING_MIN_HEIGHT/2) {
spawnBuilding('right');
}
}
// Collision detection
checkCollisions();
}
function draw() {
// Background City Sky
ctx.fillStyle = SKY_COLOR;
ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
// Draw Buildings (simplified non-perspective rectangles)
drawBuildings();
// Draw Road
ctx.fillStyle = ROAD_COLOR;
ctx.fillRect(ROAD_X, 0, ROAD_WIDTH, GAME_HEIGHT);
// Draw Lane Lines (dashed)
ctx.fillStyle = ROAD_LINE_COLOR;
const numLines = NUM_LANES - 1;
const lineLength = 30;
const lineGap = 30; // Adjust gap from 20 to 30
const totalLineSegment = lineLength + lineGap;
// Y offset for animation - faster animation with speed
const lineOffsetY = (frameCount * speed * 0.2) % totalLineSegment;
for (let i = 0; i < numLines; i++) {
const lineX = ROAD_X + LANE_WIDTH * (i + 1) - ROAD_LINE_WIDTH / 2;
// Draw dashed line segments
for (let j = lineOffsetY - totalLineSegment; j < GAME_HEIGHT; j += totalLineSegment) {
ctx.fillRect(lineX, j, ROAD_LINE_WIDTH, lineLength);
}
}
// Draw Player Car
ctx.fillStyle = '#ff0000'; // Red car
ctx.fillRect(carX, carY, CAR_WIDTH, CAR_HEIGHT);
// Draw Obstacles (Other Cars)
obstacles.forEach(obstacle => {
ctx.fillStyle = obstacle.color; // Obstacle color
ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
});
// Draw UI (Score, Speed)
ctx.fillStyle = '#ffffff';
ctx.font = '20px Arial';
ctx.fillText(`Score: ${score}`, 10, 30);
ctx.fillText(`Speed: ${speed.toFixed(1)}`, 10, 60); // Fictional speed unit
// Draw Start/Game Over Screen
if (gameState === 'start') {
drawStartScreen();
} else if (gameState === 'gameOver') {
drawGameOverScreen();
}
}
function spawnObstacle() {
// Choose a random lane to spawn in
const lane = Math.floor(Math.random() * NUM_LANES);
const obstacleX = ROAD_X + lane * LANE_WIDTH + (LANE_WIDTH / 2) - (OBSTACLE_WIDTH / 2);
// Start off-screen at the top, position relative to the very top
const obstacleY = -OBSTACLE_HEIGHT - Math.random() * GAME_HEIGHT/4; // Random variance before appearing
const color = OBSTACLE_COLORS[Math.floor(Math.random() * OBSTACLE_COLORS.length)];
// Add to obstacles array
obstacles.push({ x: obstacleX, y: obstacleY, color: color, width: OBSTACLE_WIDTH, height: OBSTACLE_HEIGHT });
}
// Spawn building at a specific Y coordinate (or random if null)
function spawnBuilding(side, startY = null) {
const width = BUILDING_MIN_WIDTH + Math.random() * (BUILDING_MAX_WIDTH - BUILDING_MIN_WIDTH);
const height = BUILDING_MIN_HEIGHT + Math.random() * (BUILDING_MAX_HEIGHT - BUILDING_MIN_HEIGHT);
// Start off-screen at the top, positioned just above the top edge or further up
const y = startY !== null ? startY : -height - Math.random() * GAME_HEIGHT/4; // Random spacing between buildings
let x;
if (side === 'left') {
x = ROAD_X - width; // Place buildings to the immediate left of the road
buildingsLeft.push({ x: x, y: y, width: width, height: height, color: color });
} else { // side === 'right'
x = ROAD_X + ROAD_WIDTH; // Place buildings to the immediate right of the road
buildingsRight.push({ x: x, y: y, width: width, height: height, color: color });
}
}
function drawBuildings() {
// Draw buildings slightly behind the road perspective-wise
[...buildingsLeft, ...buildingsRight].forEach(building => {
ctx.fillStyle = building.color;
ctx.fillRect(building.x, building.y, building.width, building.height);
});
}
function checkCollisions() {
const carHitBox = { // Simple AABB
x: carX,
y: carY,
width: CAR_WIDTH,
height: CAR_HEIGHT
};
for (let i = 0; i < obstacles.length; i++) {
const obstacle = obstacles[i];
// Simple AABB collision detection
if (carHitBox.x < obstacle.x + obstacle.width &&
carHitBox.x + carHitBox.width > obstacle.x &&
carHitBox.y < obstacle.y + obstacle.height &&
carHitBox.y + carHitBox.height > obstacle.y) {
// Collision detected
gameOver();
return; // Exit function after game over
}
}
}
function gameOver() {
if (score > highScore) {
highScore = score;
}
gameState = 'gameOver';
}
function drawStartScreen() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'; // Semi-transparent black overlay
ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
ctx.fillStyle = '#ffffff';
ctx.font = '40px Arial';
ctx.textAlign = 'center';
ctx.fillText('Arcade City Racer', GAME_WIDTH / 2, GAME_HEIGHT / 2 - 50);
ctx.font = '25px Arial';
ctx.fillText('Press ENTER to Start', GAME_WIDTH / 2, GAME_HEIGHT / 2 + 20);
ctx.font = '20px Arial';
ctx.fillText('Use LEFT/RIGHT Arrows to Steer', GAME_WIDTH / 2, GAME_HEIGHT / 2 + 70);
ctx.textAlign = 'left'; // Reset alignment
}
function drawGameOverScreen() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'; // Semi-transparent black overlay
ctx.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
ctx.fillStyle = '#ffffff';
ctx.font = '40px Arial';
ctx.textAlign = 'center';
ctx.fillText('Game Over!', GAME_WIDTH / 2, GAME_HEIGHT / 2 - 60);
ctx.font = '30px Arial';
ctx.fillText(`Final Score: ${score}`, GAME_WIDTH / 2, GAME_HEIGHT / 2 + 0);
ctx.fillText(`High Score: ${highScore}`, GAME_WIDTH / 2, GAME_HEIGHT / 2 + 40);
ctx.font = '25px Arial';
ctx.fillText('Press ENTER to Restart', GAME_WIDTH / 2, GAME_HEIGHT / 2 + 100);
ctx.textAlign = 'left'; // Reset alignment
}
// Game Loop
function gameLoop() {
// Handle input for state transitions only once per frame
if ((gameState === 'start' || gameState === 'gameOver') && keys.Enter) {
initGame(); // Start or restart the game
keys.Enter = false; // Consume the key press immediately
}
update();
draw();
requestAnimationFrame(gameLoop);
}
// Start the game loop when window loads
window.onload = function() {
gameLoop();
};
</script>
</body>
</html>
``````html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>City Arcade Racer</title>
<style>
body { margin: 0; background: #000; display: flex; justify-content: center; align-items: center; height: 100vh; }
canvas { background: #333; }
</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
canvas.width = 400;
canvas.height = 600;
let playerLane = 1; // 0 left, 1 middle, 2 right
const lanes = 3;
const laneWidth = canvas.width / lanes;
const laneCenters = [laneWidth / 2, laneWidth * 1.5, laneWidth * 2.5];
const playerY = canvas.height - 60;
let speed = 4;
let obstacles = [];
let buildings = [];
let roadLines = [];
let score = 0;
let gameOver = false;
let lastSpawn = 0;
let speedIncrease = 0;
// Generate initial buildings
for (let i = 0; i < 10; i++) {
buildings.push({
x: Math.random() < 0.5 ? -Math.random() * 50 - 10 : canvas.width + Math.random() * 50 + 10,
y: i * 100 - 1000,
width: 40 + Math.random() * 40,
height: 100 + Math.random() * 200,
color: `rgb(${100 + Math.random()*100}, ${100 + Math.random()*100}, ${100 + Math.random()*100})`
});
}
// Generate initial road lines
for (let i = 0; i < 20; i++) {
roadLines.push({ y: i * 40 - 800 });
}
// Input handling
document.addEventListener('keydown', (e) => {
if (gameOver) {
if (e.key === 'Enter') {
resetGame();
}
return;
}
if (e.key === 'ArrowLeft' && playerLane > 0) playerLane--;
if (e.key === 'ArrowRight' && playerLane < 2) playerLane++;
});
function resetGame() {
playerLane = 1;
speed = 4;
obstacles = [];
score = 0;
gameOver = false;
speedIncrease = 0;
lastSpawn = 0;
update();
}
function update() {
if (gameOver) return;
speed = 4 + speedIncrease * 0.1;
speedIncrease += 0.01; // Gradually increase speed
// Update player position
const playerX = laneCenters[playerLane];
// Spawn obstacles
if (Math.random() < 0.02 + speedIncrease * 0.001 && Date.now() - lastSpawn > 300) {
const obsLane = Math.floor(Math.random() * lanes);
obstacles.push({ lane: obsLane, y: canvas.height + 50 });
lastSpawn = Date.now();
}
// Move obstacles
obstacles.forEach((obs, index) => {
obs.y -= speed;
if (obs.y < -50) {
obstacles.splice(index, 1);
}
});
// Move buildings
buildings.forEach((bldg) => {
bldg.y += speed;
if (bldg.y > canvas.height) {
bldg.y = -bldg.height - Math.random() * 200;
bldg.x = Math.random() < 0.5 ? -Math.random() * 50 - 10 : canvas.width + Math.random() * 50 + 10;
bldg.width = 40 + Math.random() * 40;
bldg.height = 100 + Math.random() * 200;
bldg.color = `rgb(${100 + Math.random()*100}, ${100 + Math.random()*100}, ${100 + Math.random()*100})`;
}
});
// Move road lines
roadLines.forEach((line) => {
line.y += speed;
if (line.y > canvas.height) {
line.y = -40;
}
});
// Check collisions
obstacles.forEach((obs) => {
if (Math.abs(obs.y - playerY) < 40 && obs.lane === playerLane) {
gameOver = true;
}
});
// Increase score
score += Math.floor(speed / 10);
// Draw everything
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw road
ctx.fillStyle = '#555';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw road lines
ctx.fillStyle = '#fff';
roadLines.forEach((line) => {
ctx.fillRect(laneWidth - 5, line.y, 10, 20);
ctx.fillRect(laneWidth * 2 - 5, line.y, 10, 20);
});
// Draw buildings
buildings.forEach((bldg) => {
ctx.fillStyle = bldg.color;
ctx.fillRect(bldg.x, bldg.y, bldg.width, bldg.height);
// Draw windows
ctx.fillStyle = '#ff0';
for (let wy = 10; wy < bldg.height - 10; wy += 20) {
for (let wx = 5; wx < bldg.width - 5; wx += 15) {
if (Math.random() < 0.7) {
ctx.fillRect(bldg.x + wx, bldg.y + wy, 5, 5);
}
}
}
});
// Draw obstacles (enemy cars)
obstacles.forEach((obs) => {
const obsX = laneCenters[obs.lane];
ctx.fillStyle = '#f00';
ctx.fillRect(obsX - 20, obs.y - 40, 40, 50);
ctx.fillStyle = '#fff';
ctx.fillRect(obsX - 15, obs.y - 35, 30, 10);
ctx.fillRect(obsX - 15, obs.y - 10, 30, 10);
});
// Draw player car
ctx.fillStyle = '#0f0';
ctx.fillRect(playerX - 20, playerY - 40, 40, 50);
ctx.fillStyle = '#fff';
ctx.fillRect(playerX - 15, playerY - 35, 30, 10);
ctx.fillRect(playerX - 15, playerY - 10, 30, 10);
// Draw score
ctx.fillStyle = '#fff';
ctx.font = '20px Arial';
ctx.fillText(`Score: ${score}`, 10, 30);
if (gameOver) {
ctx.fillStyle = 'rgba(0,0,0,0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#fff';
ctx.font = '40px Arial';
ctx.fillText('Game Over!', 50, canvas.height / 2 - 20);
ctx.font = '20px Arial';
ctx.fillText(`Score: ${score}`, 140, canvas.height / 2 + 20);
ctx.fillText('Press Enter to Restart', 60, canvas.height / 2 + 50);
} else {
requestAnimationFrame(update);
}
}
update();
</script>
</body>
</html>
```Below is a complete HTML file that implements a simple fun arcade racer game in JavaScript. The game is set in a stylized city environment with scrolling buildings and roads, and it's designed to be fast-paced. You control a car using the arrow keys: left and right to steer, and up to accelerate. Avoid obstacles to survive as long as possible!
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>City Arcade Racer</title>
<style>
body {
margin: 0;
overflow: hidden;
background-color: #000;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
canvas {
border: 1px solid #fff;
background-color: #222;
}
</style>
</head>
<body>
<canvas id="gameCanvas" width="800" height="600"></canvas>
<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
// Game variables
const car = {
x: canvas.width / 2,
y: canvas.height - 100,
width: 50,
height: 30,
speed: 5,
velocity: 0, // For acceleration
angle: 0 // For steering
};
const obstacles = []; // Array to hold obstacles
let score = 0;
const obstacleFrequency = 100; // Frames between obstacles
let frameCount = 0;
// Background elements (city buildings and road)
const buildings = [
{ x: 0, width: 100, height: 200, color: '#444' },
{ x: 150, width: 80, height: 150, color: '#555' },
{ x: 300, width: 120, height: 250, color: '#666' },
{ x: 450, width: 90, height: 180, color: '#777' },
{ x: 600, width: 110, height: 220, color: '#888' },
{ x: 750, width: 100, height: 190, color: '#999' }
];
let roadLines = []; // For scrolling road lines
// Initialize road lines
for (let i = 0; i < 20; i++) {
roadLines.push({ x: i * 100, y: canvas.height });
}
// Keyboard controls
const keys = {
ArrowLeft: false,
ArrowRight: false,
ArrowUp: false
};
window.addEventListener('keydown', (e) => {
if (keys.hasOwnProperty(e.code)) keys[e.code] = true;
});
window.addEventListener('keyup', (e) => {
if (keys.hasOwnProperty(e.code)) keys[e.code] = false;
});
// Game loop
function gameLoop() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Update score
score++;
frameCount++;
// Generate obstacles periodically
if (frameCount % obstacleFrequency === 0) {
obstacles.push({
x: Math.random() * (canvas.width - 50),
y: -50,
width: 50,
height: 50,
speed: 5 + score / 1000 // Increase speed over time
});
}
// Update car
if (keys.ArrowUp) car.velocity += 0.1; // Accelerate
if (keys.ArrowLeft) car.angle -= 0.05; // Turn left
if (keys.ArrowRight) car.angle += 0.05; // Turn right
car.velocity *= 0.98; // Natural deceleration
car.x += (car.speed + car.velocity) * Math.sin(car.angle); // Forward movement with angle
car.y -= car.speed + car.velocity; // Move forward
// Boundary checks
if (car.x < 0) car.x = 0;
if (car.x + car.width > canvas.width) car.x = canvas.width - car.width;
if (car.y < 0) car.y = canvas.height - car.height; // Wrap around vertically for fun
// Update obstacles
obstacles.forEach((obs, index) => {
obs.y += obs.speed;
if (obs.y > canvas.height) obstacles.splice(index, 1); // Remove off-screen obstacles
// Collision detection
if (
car.x < obs.x + obs.width &&
car.x + car.width > obs.x &&
car.y < obs.y + obs.height &&
car.y + car.height > obs.y
) {
alert('Game Over! Score: ' + score);
document.location.reload(); // Restart game
}
});
// Update and draw background (scrolling buildings and road)
buildings.forEach(building => {
ctx.fillStyle = building.color;
ctx.fillRect(building.x, building.y || canvas.height - building.height, building.width, building.height);
building.x -= 2; // Scroll buildings
if (building.x + building.width < 0) building.x = canvas.width; // Loop
});
roadLines.forEach(line => {
ctx.strokeStyle = '#fff';
ctx.beginPath();
ctx.moveTo(line.x, line.y);
ctx.lineTo(line.x + 50, line.y);
ctx.stroke();
line.y -= (car.speed + car.velocity); // Scroll with speed
if (line.y < 0) line.y = canvas.height; // Loop
});
// Draw car
ctx.fillStyle = '#f00'; // Red car
ctx.save();
ctx.translate(car.x + car.width / 2, car.y + car.height / 2);
ctx.rotate(car.angle);
ctx.fillRect(-car.width / 2, -car.height / 2, car.width, car.height);
ctx.restore();
// Draw obstacles
obstacles.forEach(obs => {
ctx.fillStyle = '#0f0'; // Green obstacles
ctx.fillRect(obs.x, obs.y, obs.width, obs.height);
});
// Draw score
ctx.fillStyle = '#fff';
ctx.font = '20px Arial';
ctx.fillText('Score: ' + score, 10, 30);
requestAnimationFrame(gameLoop);
}
gameLoop(); // Start the game
</script>
</body>
</html>
```