BikeControl (SwiftControl)
Use Zwift Click, Zwift Play, Shimano Di2, or any Bluetooth controller with MyWhoosh, Rouvy, Zwift, and more. Free to try on iOS, Android, Windows, and macOS.
翻訳待ち
Open-source indoor cycling with friends — and bots, so the road is never empty.
翻訳待ち
Loading the full README.
Read the full README ↗Hook up a smart trainer, open the browser and ride a virtual route together with others. Every server is just a Docker container: run it at home for your LAN, on a VPS for your club, or join a public one. This repository contains the proof of concept: a Rust game server and a browser client (installable as an app) with a 3D and a 2D view.
Status: proof of concept. Expect breaking changes.
Requirements: Docker with Compose v2 and make. Rust and Node are optional.
make up
Then open http://localhost:8080 in Chrome or Edge, enter a name, connect your
trainer (or pick Simulated trainer) and join. The first make up copies
.env.example to .env; adjust it and run make up again to apply changes.
| Control | Action |
|---|---|
← / → or A / D | change lane |
↑ / ↓ or W / S | simulated trainer power ±10 W |
E / Q or + / − | shift up / down (virtual shifting) |
J | join a race during its countdown / leave it |
L | cycle the HUD layout: auto, cockpit, broadcast, training, race |
M / Esc | open the menu (overview, view, race, training, settings) / close panels |
V | switch between 3D and 2D view |
C / T | cycle camera (chase, close, drone, TV side) / time of day |
P | take a photo (find them under Menu → Finish ride) |
↑ / ↓ in ERG mode | target ±5 W, or intensity ±5 % during a workout |
On a phone or tablet the same controls are on screen: lane arrows at the sides and, with the simulated trainer, a power stepper.
No trainer? Phone paddles. Pick 📱 Phone paddles on the start screen and tap the left and right paddles in turn, like pedal strokes. A steady rhythm in the green band holds about 75 % of your FTP; tapping faster sprints up to 150 %, but drains your energy bar, and it refills when you ease off. Tapping the same side twice is a stumble. Swipe sideways above the paddles to change lanes. Paddle riders race with everyone and get a 📱 by their name. Paddle rides stay in OpenRadlr: no Strava upload and no ride files (photos and the summary card can be saved). Simulated-trainer rides can be downloaded but not uploaded to Strava.
.zwo import),
low-poly 3D view with chase camera, a top-down 2D map view, an elevation profile, a
minimap, nearby riders, race panel and chat; live player counts in the start
menu and during the ride; ride photos and a summary image; ride summary with
.tcx/.gpx/.fit download and Strava upload (as a virtual ride with its climbing), placed on the map in a virtual world off Mallorca;
power-only trainers and power meters (no resistance control) are supported.Web Bluetooth works in Chromium browsers (Chrome, Edge) on Windows, macOS, Android and ChromeOS — not in Firefox or Safari/iOS. Without Bluetooth you can still ride with the simulated trainer.
Run make to list everything. The most important ones:
| Command | What it does |
|---|---|
make up | Build and start in the background, wait until healthy |
make down | Stop and remove the containers |
make logs | Follow the logs |
make update | git pull, rebuild, restart, wait until healthy |
make test | All tests (Rust and JavaScript) |
make lint | rustfmt, clippy (-D warnings), ESLint |
make check | Everything CI runs |
make hooks | Install the pre-commit and pre-push git hooks |
make dev | Run the server with local cargo, no Docker |
make lock | Create server/Cargo.lock — run once and commit it |
If cargo or npm is not installed, the test and lint targets run them in Docker.
Run make hooks once per clone to install the pre-commit
and pre-push hooks (see CONTRIBUTING.md).
Full guide, including your own reverse proxy and Strava: docs/deploy.md.
git clone <your-repo-url> openradlr && cd openradlr
make setup # creates .env (later: adds new settings to it)
$EDITOR .env # set OPENRADLR_SERVER_NAME etc.
make up
Later, to deploy new commits: make update.
This is the beginning of the README. Read the rest at the original. Read the full README ↗