Terrain Rider: A Physics-Based Mountain Biking Game Built for the Browser
Mountain biking games live or die by feel. A convincing trail is not enough: the suspension has to load into a berm, tire grip has to break predictably, the rider has to move with the bike, and a badly timed landing has to end with more than a canned animation.
Terrain Rider began with a deliberately difficult question: how much of that experience could we deliver through a URL?
Digital Nature designed and developed Terrain Rider as a free online mountain biking game with no installer, native client, or platform-specific build. Players can ride a downhill bike or electric dirt bike through streamed mountain terrain, tune their setup, race timed runs, meet other riders, and spawn at real locations around the world—all inside a modern web browser.
That simple entry point hides a serious technical stack. The application combines a custom dynamics model with WebAssembly physics, real digital elevation data, OpenStreetMap trail networks, multi-level terrain streaming, procedural character animation, runtime 3D model assembly, real-time multiplayer, and a device-aware graphics pipeline.
Building the Game With React Three Fiber, Three.js, and Rapier
Terrain Rider uses a modern TypeScript architecture built around technologies we use throughout our Three.js and React Three Fiber development work:
- React 19 and TypeScript organize the game, interface, configurator, and application state.
- Three.js provides the WebGL renderer, materials, geometry, cameras, lighting, and scene infrastructure.
- React Three Fiber gives the 3D world a declarative component architecture without moving simulation state into React's render cycle.
- Rapier supplies a Rust-based WebAssembly rigid-body and collision engine through
@react-three/rapier. - Zustand keeps menus, equipment, environment options, and game state lightweight and predictable.
- Vite handles the development and production build, including custom optimization for the Rapier WASM payload.
- Node.js services bake and stream terrain data and coordinate multiplayer sessions over WebSockets.
React owns product state; Rapier owns the physical world; Three.js owns frame-by-frame presentation. Keeping those responsibilities separate was essential. The bike can update at simulation speed without triggering component renders, while the HUD reads a small runtime snapshot for speed, stamina, suspension, and run timing.
Engineering Custom Mountain Bike Physics
Terrain Rider does not use a generic four-wheel vehicle controller with two wheels removed. We built a mountain-bike-specific dynamics system on top of Rapier.
A Stable Physical Core
The bike is represented by one tuned rigid body with custom mass properties. Each wheel uses a raycast contact model, allowing the simulation to calculate suspension and tire forces without asking the physics engine to solve a fragile chain of small wheel and frame joints.
The front suspension ray follows the fork's actual steering axis, and available travel comes from the selected fork. Spring force, bump damping, and rebound damping are calculated independently for each end of the bike. The visual fork lowers and rear swingarm then follow the physical compression values, so the rendered bike and simulation stay aligned.
Tire Grip, Braking, and Control
Each contact patch runs through a simplified Pacejka-style tire model. Longitudinal and lateral slip, wheel angular velocity, normal load, grip, braking torque, and surface speed all contribute to the final force. This produces behaviour a basic friction coefficient cannot:
- Tires load progressively before sliding.
- Braking can slow, lock, or release a wheel within a bounded torque budget.
- Rear-wheel skids emerge from the relationship between ground speed and wheel speed.
- Different terrain and trail surfaces can change available grip.
- Landing forces feed naturally into suspension and traction.
Balance is assisted, not scripted. A PID controller works toward a speed-scaled lean target, while cross-slope probes add berm assistance when the rider steers into a bank. Steering range narrows as speed increases. Pedal or motor torque is limited by traction and top-speed curves, and the front brake is lean-gated to reduce impossible low-side recoveries.
The bunny hop system follows the same philosophy. Holding the control preloads the rider and builds charge; release applies an impulse along the bike's current up axis. Lean and steering input contribute pitch and spin, making a hop off a sloped trail behave differently from one on flat ground.
Crashes That Belong to the Simulation
Crash detection watches for physical failure states rather than selecting a fall animation. When the rider bails, the current procedural pose is converted into a Rapier biped ragdoll built from capsule bodies and constrained joints. The bike continues under its own momentum, the camera follows the rider, and the player can remount without reloading the world.
This is the same production principle explored in our guide to React Three Fiber and Rapier physics: use the physics engine for collision and rigid-body solving, then add the domain-specific model the experience actually needs.
Turning Real Terrain Data Into Rideable 3D Worlds
The terrain system is the largest architectural difference between Terrain Rider and a conventional browser game. Instead of shipping one fixed level, the game can use a geographic origin to build a rideable world from real terrain and mapping data. Whistler is the optimized default, while an origin URL parameter can place the player at another latitude and longitude.
Server-Authored DEM and OpenStreetMap Data
Gameplay terrain is prepared by a dedicated Node.js terrain service. The server combines digital elevation model data with OpenStreetMap features such as trails, roads, chairlifts, land cover, lakes, rivers, and coastlines.
Trail profiles and terrain carving happen once on the server, not independently in every browser. The bake process smooths local ground, limits cuts and fills, resolves junctions, clears fine vegetation around trail and lift corridors, and stores the final result in a deterministic planetary coordinate frame.
That division of labour keeps expensive and consistency-sensitive work off the client. A gameplay browser never needs to download raw DEM files, interpret OSM tags, solve trail profiles, or reproduce the server's terrain-carving algorithm.
A Compact Multi-Channel Tile Format
The baked world is delivered as demand-paged 96-metre terrain tiles using a compact progressive binary format. Each page contains a final height plane plus coverage data for:
- Surface material
- Trail and road clearing
- Grass distribution
- Forest distribution
The same authoritative height data feeds both the visual mesh and Rapier heightfield colliders. This prevents the common open-world problem where the rendered trail and collision surface drift apart.
Clipmap Streaming and Floating Origins
The browser renders terrain as concentric clipmap-style level-of-detail rings. Fine tiles surround the player; progressively larger tiles extend the view toward the horizon. Mesh edges morph toward the next level to close cracks between resolutions, while an independent near-field ring maintains detailed physics colliders around the bike.
Requests are prioritized by proximity and need—boot, visual, collider, stitching, forest, lift, or point sampling—and decoded pages are held in a bounded least-recently-used cache. Visual geometry is built within a per-frame time budget so an arriving group of tiles does not create a large main-thread stall.
Because long-distance coordinates eventually lose floating-point precision, the local scene can re-anchor around the rider. Terrain, roads, water, lifts, multiplayer positions, and cached tile keys all translate into the new local frame while the server's fixed planetary coordinates remain stable.
Rendering a Living Mountain Environment
Terrain Rider's world has to communicate speed, slope, and surface condition at a glance. We built the environment as a set of coordinated rendering systems rather than one monolithic level model.
Terrain Materials and Vegetation
A custom terrain material blends ground, rock, mud, and snow using the server-authored surface data together with height, slope, and environmental inputs. Trail clearing drives the placement system, keeping grass and trees out of riding lines without requiring thousands of hand-authored exclusion objects.
Grass blades and forest trees are rendered with instancing and distance-based detail. Vegetation streams around the camera on separate work budgets, while deterministic placement keeps a location visually stable as tiles unload and return.
Atmosphere, Weather, and Water
The scene includes an analytic sky, aerial perspective, cloud shadowing, wind-driven particles, dust, birds, and configurable environmental presets. These systems share global lighting and weather uniforms so terrain, vegetation, and water remain part of the same atmosphere.
Water is generated from server-resolved geographic features. Lakes, rivers, and ocean coastlines use a custom shader with depth-based transmission, sky reflection, flow-driven foam, shoreline treatment, caustics, and sun glitter. The effect scales by graphics tier rather than disappearing into a separate visual style on lower-powered hardware.
Device-Aware Quality Without Different Physics
At startup, Terrain Rider selects a low, medium, or high graphics tier using device and GPU signals. Each tier can adjust terrain view distance, mesh density, forest distance, shadows, grass, particles, water detail, and post-processing cost.
Physics is deliberately not tiered. Collider resolution, simulation behaviour, and the terrain data that feeds Rapier stay consistent across devices, preserving the same handling model even when visual detail changes.
Assembling Custom Bikes at Runtime
Terrain Rider includes a complete 3D bike configurator inside the game. Frames, forks, bars, wheelsets, tires, drivetrains, pedals, seats, and protective gear are stored as separate optimized GLB assets.
Each authored part exposes named sockets such as fork, wheel, stem, grip, crank, pedal, and seat positions. The runtime assembly system uses those sockets to build the selected bike, mirror paired components, scale front and rear wheels independently, apply frame colour, and calculate physical geometry from the result.
Players can start from downhill, enduro, or electric presets—including Santa Cruz V10, Scott Ransom, and Sur-Ron configurations—then change individual parts and select 26, 27.5, or 29-inch wheels. The chosen configuration persists locally and is included in the multiplayer profile so other riders see the correct bike and gear.
This data-driven structure makes the configurator more than a visual menu. Fork travel changes the simulation, frame characteristics determine drive behaviour, and an electric setup enables a separately modelled motor and audio system.
Procedural Rider Animation and Inverse Kinematics
A pre-baked riding animation would not survive variable frames, suspension travel, wheel sizes, steering angles, or moving pedals. Terrain Rider instead poses its rigged character procedurally every frame.
The rider's hips are resolved against the live bike geometry. Two-bone inverse kinematics keeps hands on the grips and feet on the rotating pedals. Spine crouch responds to stance, lean, suspension compression, braking, airtime, and hop preload, while the head remains level enough to preserve a believable line of sight.
Because the pose is generated from current simulation data, the rider moves with the exact bike assembled in the configurator. It also gives the crash system a valid live skeleton pose from which to initialize the ragdoll.
Building Global Multiplayer With Spatial Interest Management
Multiplayer connects players through a dedicated WebSocket service. Everyone can enter a shared global lobby, create a private room with a short key, open text chat, and share an invite URL that carries both the room and geographic spawn point.
The global terrain created a networking constraint: two riders might be in the same lobby but separated by thousands of kilometres. Broadcasting every pose to every player would waste bandwidth and client resources.
Terrain Rider solves this with a fixed planetary coordinate system and server-side spatial interest management. Clients send a compact 20-value pose at 15 Hz, including the bike transform, suspension, wheel rotation, crank position, rider stance, and state flags. The server places each rider in a two-kilometre grid and relays high-frequency state only to peers within the surrounding 3×3 neighbourhood. Chat and presence remain room-wide.
Remote riders interpolate buffered state locally. Appearance changes are sent separately at a low rate, and inactive clients retain lightweight keepalives instead of repeating identical transforms. The result is a multiplayer model that can share one global world without treating the entire planet as one network interest area.
Designing Responsive Web Audio
Sound is driven by simulation values rather than a single speed loop. Terrain Rider mixes separate sources for wind, tire roll, freewheel hub, skidding, rider breathing, and impacts. Gain and playback rate respond to air speed, contacted wheel speed, wheel slip, braking, stamina, and collision force.
The electric bike adds a synthesized drivetrain voice through the Web Audio API. Its oscillator and filter network models motor speed, load, gear mesh, controller whine, and chain noise from the rear wheel and throttle state. This produces a responsive motor sound without storing a large set of recorded RPM samples.
Performance Engineering for a Zero-Install 3D Game
A browser game has to earn every byte before the first ride. We made several build and runtime decisions specifically for fast delivery and repeat visits:
- Separate stable vendor chunks allow React, Three.js, and Rapier to stay cached when game code changes.
- A custom Vite plugin extracts Rapier's embedded base64 WebAssembly into a real
.wasmasset, enabling streaming instantiation and avoiding a multi-megabyte string on the eager JavaScript path. - Content-derived model revisions invalidate GLB URLs only when the model collection changes.
- Immutable asset caching keeps hashed bundles and revisioned models in the browser and CDN cache.
- Terrain-server preconnection starts DNS, TLS, and connection setup before the game requests its first world manifest.
- Bounded terrain, geometry, and vegetation caches prevent an open world from becoming unbounded browser memory.
- Per-frame build budgets and nearest-first queues spread terrain preparation over time while prioritizing what the rider will reach first.
- Draco-ready model delivery and optimized GLBs reduce the cost of the modular bike, rider, gear, vegetation, and park assets.
Performance work continues across the entire frame: simulation data avoids React rerenders, repeated scenery uses instancing, physics colliders exist only near the rider, remote multiplayer bodies are interest-filtered, and graphics tiers constrain visual work without changing gameplay.
Building an SEO Foundation for an Interactive WebGL Experience
Terrain Rider is an application first, but it still needs a clear search result and share preview. The production HTML includes a canonical URL, indexable robots directive, descriptive title and meta description, Open Graph and Twitter card metadata, and the dedicated Terrain Rider artwork used as the primary image in this case study.
Structured data describes the project as both a VideoGame and SoftwareApplication, supported by WebSite and Organization entities. The schema identifies the browser platform, sports-simulation genre, free access model, publisher, description, and image. These signals give search engines useful context even though the product experience itself begins inside a WebGL canvas.
The Result
Terrain Rider brings systems usually associated with installed sports games into a link anyone can open:
- A custom mountain bike simulation with suspension, tire slip, braking, balance, tricks, and crashes
- Real-world terrain and trail networks streamed from server-authored DEM and OpenStreetMap data
- A scalable clipmap renderer with matched visual and physics surfaces
- Runtime bike and rider configuration with physically meaningful equipment choices
- Procedural rider IK, live ragdolls, and simulation-driven audio
- Global multiplayer with public and private sessions, chat, and location-aware invite links
- Adaptive graphics, progressive world loading, and browser-focused caching and build optimization
Terrain Rider is a demonstration of what browser-native 3D can become when physics, geography, rendering, product architecture, and delivery are designed as one system. There is no download and no artificial boundary around a single level—just a bike, a mountain, and a URL.
If you are planning a browser-based simulation, interactive 3D product, or physics-driven WebGL experience, explore our interactive 3D development services or start a project with Digital Nature.







