Game Build Breakdown
Valley Racer FPV Drone Course: Terrain, Gates and Speed in Three.js
Valley Racer is the opposite of a corridor racer: the world moves under a flying camera, so terrain, horizon, gates and controls all have to cooperate to make speed understandable.
Production context
This guide studies Valley Racer, a published Supagames game. Repository source: biggames/valley-racer/game.js.
The snippets below are intentionally smaller than the production file. They isolate transferable systems so you can reuse the idea in your own browser game without copying every detail of the original project.
1. FPV Control Is Rotation First
Drone flight feels different from walking because pitch, yaw and roll define where thrust goes. The player is not just moving on a plane; they are rotating a body and then applying forward force through that body.
A readable implementation keeps angular velocity separate from linear velocity. Input changes rotation rates, damping keeps the drone from spinning forever, and thrust adds force along the current forward vector.
function updateDrone(input, dt) {
drone.angular.x += input.pitch * 1.8 * dt;
drone.angular.y += input.yaw * 1.5 * dt;
drone.angular.z += input.roll * 2.2 * dt;
drone.rotation.x += drone.angular.x;
drone.rotation.y += drone.angular.y;
drone.rotation.z += drone.angular.z;
drone.angular.multiplyScalar(Math.exp(-5 * dt));
}
2. Gates Must Be Readable Before They Are Close
Racing gates are navigation UI disguised as world objects. They should be visible from far away, communicate direction and avoid blending into terrain. A subtle arrow, ring color or beacon line can do more for playability than extra decorative meshes.
The gate system should track current target, next target and missed target. That lets the HUD explain why the timer did or did not update.
function updateGateProgress(dronePosition) {
const gate = gates[currentGateIndex];
const distance = dronePosition.distanceTo(gate.position);
gate.marker.visible = distance < 260;
if (distance < gate.radius) {
currentGateIndex += 1;
addRaceTimeBonus(gate.bonus);
}
}
3. Terrain Cues Create Speed
An empty horizon makes a fast drone feel slow. Valley racing needs near-ground reference points: grass clumps, rocks, tree silhouettes, shadows and water edges. These elements give the eye parallax, but too many of them can destroy frame time.
Use density by distance and importance. Objects near the route can be richer; far objects can be larger silhouettes or instanced patches.
function chooseSceneryDensity(distanceFromRoute, quality) {
const routeBoost = distanceFromRoute < 35 ? 1.35 : 0.75;
const qualityScale = quality === "mobile" ? 0.45 : 1;
return routeBoost * qualityScale;
}
4. Always Budget the World Around the Frame
A drone racer punishes stutter because the player is always correcting motion. A small FPS meter is not just a debug toy; it helps decide whether tree count, shadow range, pixel ratio or water effects are too expensive for the current device.
Quality settings should be visible in data. If forest density changes, save that setting so the same race can be tested again later.
function applyQuality(renderer, quality) {
const ratio = quality === "mobile" ? 1.1 : 1.5;
renderer.setPixelRatio(Math.min(window.devicePixelRatio, ratio));
world.treeDensity = quality === "mobile" ? 0.45 : 0.82;
world.shadowDistance = quality === "mobile" ? 45 : 90;
}
5. Build checklist
- Treat drone controls as rotation plus thrust, not simple top-down movement.
- Make the next gate visible early with color, shape and HUD support.
- Use scenery density to create parallax, then cap it aggressively for performance.
- Save quality and generator settings so world tuning can be reproduced.