BikeGame
Pair your trainer, pick a route and press Play. In the ride, M or Esc opens the menu for cameras, trainer settings, workouts and Ride together.
1 Pair devices
Pair each sensor in its own slot. Paired devices are remembered and reconnected next time. Web Bluetooth has no scan list: Pair opens the browser's chooser.
Rider profile
Weight and bike weight set the physics; FTP and max heart rate set the zones. Saved on this device.
Heart-rate zones
2 Choose a route
Real roads and trails, real gradients. Pick one, then press Play. The route loads when you start, so a paired trainer stays connected.
3 Ride settings
These can also be changed from the ride menu without stopping the session.
Pick a route to ride.
About the prototype, the cameras and smart trainers
Smart trainer
Bluetooth FTMS: power and cadence in, gradient or target power out
- Any FTMS trainer (Wahoo KICKR, Tacx Neo/Flux, Elite, Saris, Zwift Hub…) has its resistance set from the road under you, once a second at most. A plain power meter works too, but read-only.
- Difficulty scales the gradient the trainer simulates, like Zwift's trainer difficulty: at 50% a 10% ramp feels like 5%. Speed always uses the real gradient.
- ERG makes the trainer hold the ERG target watts whatever the gradient, and the game turns that power into speed, so hills no longer change how hard you pedal. Switch with the Sim / ERG buttons (they need a trainer that supports target power); in ERG the arrow keys move the target by 5 W and Difficulty has no effect.
- Drafting: sit close behind another rider and aero drag falls by up to 30%. The trainer is sent a lower wind-resistance setting, so the same speed gets easier. The DRAFT bar shows how much of the saving you have.
- Assist modes (E-bike slider, Pro category) make the game move you on more power than you make: an e-bike adds 0 to 200% of it (up to 3x), and a Pro category (Cat 4 to Pro, 3.0 to 5.8 W/kg) scales it so your FTP moves you like that category's W/kg. It only changes the speed in the game: the trainer's resistance is not eased and the big watts figure is still your real power. A badge shows the assisted watts, and segment efforts ridden assisted go on a separate board.
- Devices & rider pairs a trainer, a power meter, a cadence sensor and a heart-rate strap separately (a power meter, if paired, is used for power while the trainer only sets resistance), remembers them, and holds your weight, bike weight, FTP and max heart rate. Weight and bike weight are the mass in the physics; the heart rate on the HUD is coloured by zone.
- Needs Web Bluetooth: Chrome or Edge on desktop or Android, from
localhostor https. For a phone, runadb reverse tcp:8737 tcp:8737and openlocalhost:8737. iPhone Safari has no Web Bluetooth (Bluefy works for testing); the real app would use native Bluetooth. - Simulated trainer runs the same code against a fake device that speaks real FTMS bytes, so you can try it with no hardware.
Chase cam (ride view)
Pseudo-3D: OutRun, Road Rash
- Closest to the Zwift feel: the road comes at you, climbs rise up ahead, riders to chase.
- Cheap: one fill per scanline plus scaled sprites. Runs on anything.
- Hairpins become sharp bends. Pseudo-3D can't show a road doubling back on itself.
Side
2D profile: Excitebike, Paperboy
- The gradient you feel in your legs is the picture (vertical ×3.5).
- Sprites never scale, so the pixel art stays crisp.
- Loses turns and sense of place; every route reads as a line graph.
Overhead
Mode 7: F-Zero, Super Mario Kart
- The real map is the level: you watch yourself wind up the switchbacks.
- Flat by nature: gradient lives only in the HUD and the trainer's resistance.
Isometric (preview)
Marble Madness, Populous, SimCity 2000
- The mountain carries the elevation: real terrain, hill-shaded, 25 m contours, height ×2.
- Switchbacks stack up the slope, so you can see the climb you're on.
- Fixed angle: the road sometimes heads toward the camera or hides behind a ridge.
- Riders are tiny at this scale (drawn ~10× oversize to stay visible).
How the route was built
tools/build_route.py
- Road from OSRM (OpenStreetMap routing), resampled every 20 m.
- Elevation from the Copernicus 90 m DEM; switchback artefacts clamped to 15%.
- Hairpins from the road's shape; the first 21 get numbered signs.
- OSM landcover either side of the road and on a 20 m tile grid.
- A terrain heightmap of the whole area from Terrarium elevation tiles (for the isometric camera).
Map data © OpenStreetMap contributors (ODbL). Elevation: Copernicus GLO-90 via Open-Meteo; terrain: Mapzen Terrain Tiles (AWS Open Data). Routing: OSRM.