My Concept
For the midterm, I created Highway Havoc, a fast-paced driving game where the player must weave through incoming traffic and avoid collisions while maintaining control at high speeds. My goal was to build an arcade-style driving experience that feels alive, unpredictable, and immersive. Cars rush toward you, radar traps flash if you’re speeding, and the scrolling environment creates a sense of depth and motion.
To make the gameplay feel as close to real highway driving as possible, I implemented several interactive and behavioral features:
- Dynamic traffic flow: Cars and buses spawn randomly, sometimes creating congested traffic and other times leaving open gaps.
- Lane-based speeds: Vehicles on the left lanes drive faster than those on the right, mimicking real traffic patterns.
- Adaptive driving: Vehicles automatically slow down and match the speed of the car ahead to prevent crashes.
- Autonomous behavior: Vehicles occasionally use turn signals for three seconds and switch lanes when the adjacent lane is clear of other NPC cars. They can still crash into the player to make the game more challenging.
- Reactive NPCs: There’s a 10% chance that a vehicle will get “spooked” and change lanes when the player flashes their headlights.
Website: https://mohdalkhatib.github.io/highwayhavoc/
Embedded Sketch
How it works
The game is built using object-oriented programming to manage all its core components: Player, EnemyCar, SchoolBus, and several static environment classes like LaneLine, Tree, and Radar. To make the player car look like it’s going upwards, all the objects on the canvas move downwards depending on the difference between the player’s speed and the object’s speed. Timed progression is handled using millis(), allowing the game to dynamically scale its difficulty It begins with just two enemy cars and gradually increases to seven over time. After 30 seconds, a special SchoolBus class appears. This bus inherits most of its behavior from EnemyCar but acts as a larger, slower obstacle, adding variety and challenge to the gameplay.
Player movement is handled with smooth lerp() transitions instead of instant lane jumps, creating a realistic sliding motion. The car tilts slightly while switching lanes, giving the animation a sense of weight and momentum.
// Smooth lane movement this.x = lerp(this.x, this.targetX, 0.15); // 0.15 is speed of slide // Calculate tilt: proportional to distance from targetX let tilt = map(this.x - this.targetX, -63, 63, 0.2, -0.2); // max tilt ±0.2 rad // Rotate car while sliding translate(this.x, this.y); rotate(tilt);
The garage menu is implemented as a separate state in the program. All player cars are stored in a 2D array (matrix) where the first dimension represents car type and the second represents color variants. Selecting a car updates the active type index, and selecting a color updates the corresponding color index. The game then loads the pre-rendered image from playerCarImgs[type][color] for the main gameplay.
let playerCarImgs = []; let carColors = [ ["#f10a10", "#b77bc6", "#ffde59"], // Car 1 colors ["#737373", "#302d2d", "#df91c1"], // Car 2 colors ["#7ed957", "#6f7271", "#df91c1"] // Car 3 colors ]; let selectedCarIndex = -1; let selectedColorIndex = 0; let selectedColorIndices = [0, 0, 0]; // store each car's color choice
// Load player cars (3 types × 3 colors) for (let i = 0; i < 3; i++) { playerCarImgs[i] = []; for (let j = 0; j < 3; j++) { playerCarImgs[i][j] = loadImage(`player_car_${i}_${j}.png`); } }
From a design standpoint, I’m especially happy with how the visual composition is handled. The positions of all road elements (lane markers, trees, etc.) are responsive relative to the center of the road, ensuring that everything remains aligned even if the screen size changes. The gradual introduction of more enemies also creates a natural difficulty curve without overwhelming the player early on.
One of the features I’m most proud of is the radar system. It actively monitors the player’s speed and position. If the player passes a radar while exceeding the speed limit, the game transitions to a “game over” state. At that moment, p5 captures a snapshot of the player’s car beside the radar, displays it on the game over screen, and plays a camera shutter sound effect. Similarly, collisions trigger a crash sound, giving the sense of danger on the highway.
detect(player) { return abs(this.y - player.y) < 10 && playerSpeed > speedLimit; } if (radar.detect(player)) { gameState = "GAMEOVER"; engineSound.stop(); captureGameOver("You were caught speeding!"); shutterSound.play(); } // Crashed or caught by radar: take snapshot let snapWidth = 900; let snapHeight = 300; let sx = constrain(player.x - snapWidth / 2, 0, width - snapWidth); let sy = constrain(player.y - snapHeight / 2, 0, height - snapHeight); gameOverImage = get(sx, sy, snapWidth, snapHeight); gameOverMessage = message;
Problem I Resolved
One of the biggest challenges I faced was figuring out how to implement realistic car sounds that respond naturally to gameplay. My initial idea was to use separate audio clips for different speeds or for moments when the player accelerates or decelerates. However, this quickly became too complex and didn’t sound seamless in motion.
Instead, I switched to a single looping engine sound and mapped both its playback rate and volume to the player’s current speed. This made the sound naturally increase in pitch and intensity as the car accelerated, without needing multiple clips. To add more realism, I also added a short braking sound effect that triggers automatically when a large drop in speed is detected, simulating tire friction or skidding during sudden stops.
function handleInput() { if (keyIsDown(UP_ARROW)) playerSpeed = min(playerSpeed + 0.1, maxSpeed); if (keyIsDown(DOWN_ARROW)) { let oldSpeed = playerSpeed; playerSpeed = max(playerSpeed - 0.5, 0); } // Adjust pitch and volume of engine let volume = map(playerSpeed, 0, maxSpeed, 0.1, 1.0); engineSound.setVolume(volume); let pitch = map(playerSpeed, 0, maxSpeed, 0.8, 4.0); engineSound.rate(pitch); } // If the speed decreased by 5 or more within 1 second if (speedDrop >= 5) { if (!brakeSound.isPlaying()) { brakeSound.play(); } }
Areas for Improvement
One issue I noticed is that enemy cars sometimes merge into the same lane at the same time, leading to overlapping or clipping. Improving their lane-changing logic to better detect other vehicles could make the traffic feel more realistic. I’d also like to expand the variety of vehicles by adding a larger collection of car models and giving each one unique attributes such as top speed, acceleration, and braking ability.