openCycle GitHub
The openCycle ride screen: two ships chase a raider past a blue supergiant, with the workout sidebar on the left and rider cards at the bottom.

Recorded on the built-in simulator

I built my own indoor cycling app.

openCycle holds your KICKR at the watts the workout asks for and records every ride as a FIT file. While you ride, the workout flies a small fleet of ships to a new star system.

View on GitHub Run it

MIT licensed · macOS and Node 24 · tested on my Wahoo KICKR CORE

What it does

It replaced the subscription app I was paying for. It runs the trainer, the workout and the ride file, and it can run more than one rider on the same computer.

A burn leg in progress: the ship fires at a raider inside a lock ring near a black hole, the sidebar shows BURN 1/4 as the current step, and the rider card shows the target watts, cadence and heart rate.
A burn at 115% FTP. Hold the watts and your ship keeps firing. The lock meter up top fills as you go.
ERG control
Your trainer gets the target over Bluetooth FTMS ERG and holds it. You pedal and the resistance follows the workout.
Several riders, one machine
Two or more people can ride one session on the same computer. Everyone gets their own trainer, heart rate strap, targets and FIT file.
A FIT file for every ride
Every session is saved as a standard FIT file. Download it from History, or let it upload to Garmin Connect.
Workouts and plans
The repo comes with workouts and multi-week plan templates as JSON. You can write your own and check the whole library with one command.
Garmin import and upload
Drop a Garmin account export into the import folder and your old rides come back in. Turn on auto-upload in a profile, save a Garmin login once, and new rides go up on their own.

Every workout is a flight to a new system.

I wanted it to look like Interstellar and fly like Elite Dangerous. So every system has something huge in it: a black hole bending the starlight around it, a blue supergiant, or two stars orbiting each other. The game only reads the trainer and the workout. It never changes your targets.

  1. Launchlaunch, 450 seconds
  2. Launchlaunch, 450 seconds
  3. Burn 1/4burn 1 of 4, 240 seconds, one survey
  4. Coastcoast, 180 seconds
  5. Burn 2/4burn 2 of 4, 240 seconds, one survey
  6. Coastcoast, 180 seconds
  7. Burn 3/4burn 3 of 4, 240 seconds, one survey
  8. Coastcoast, 180 seconds
  9. Burn 4/4burn 4 of 4, 240 seconds, one survey
  10. Coastcoast, 180 seconds
  11. Approachapproach, 600 seconds

A lit dot is a survey chance. Cruise, climb and burn legs all count.

VO2max 4x4 as eleven legs. It's 53 minutes with two launches, four burns, four coasts and an approach. Finish the workout and you arrive.

The mission clock

You get there by finishing the workout. How hard you push changes what you find when you arrive. It never decides whether you arrive.

Surveys

Every cruise, climb and burn leg is a survey chance. Stay within 10% of the target for 85% of the leg (the first five seconds don't count, so ERG can settle) and the survey locks. A probe flies to the planet, and when you arrive it lights up a ring, a moon or a station. Going over target doesn't count.

Pursuits

Every burn is a chase. A raider warps in ahead of you, and while you're on target your ship shoots at it and the lock fills. Finish the burn clean and it goes down. Miss it and it gets away. Nobody takes damage, and the lock is the same number the survey uses.

Your star map

Each rider has a voyage page with every system they've reached and the surveys they locked. The next stop is already picked, so a planned workout already has a destination.

Riding together

With two or more riders, everyone flies to the same destination and chases the same raider. Hold your zones together and a tether links the ships. If someone's cadence drops, someone else can cover by riding 10% over their own target for fifteen seconds. A shield goes up around the struggling ship in the helper's color.

Two riders on a burn firing at a raider inside a lock ring, next to a blue supergiant. Both rider cards read ON TARGET.
Two riders on the same burn, chasing one raider.
The Voyage page: systems laid out along a winding path, each with its date, workout and survey marks.
The Voyage page. Every system a rider has reached, with the surveys they locked.
Arrival at the destination: the ship in orbit beside a ringed planet, silhouetted against a black hole and its accretion disk.
Arrival. Each survey you locked lights up one part of the system, and the ones you missed stay as faint outlines.

Hardware

The server runs on the computer next to the bike and talks to the trainer over Bluetooth. There's no account and nothing to sign up for.

Trainer
I've only tested it on my own Wahoo KICKR CORE, over Bluetooth FTMS ERG. Other FTMS trainers might work. I just haven't tried one.
Heart rate
Any Bluetooth strap that uses the standard Heart Rate Service.
Computer
macOS and Node 24. The Bluetooth library is a native addon, so the server needs Node. Bun won't run it.
Your data
Rides and profiles live in ~/.opencycle by default. Set OPENCYCLE_DATA_DIR to put them somewhere else.

Run it

You don't need a trainer to try it. The simulator fakes two trainers and two heart rate straps, and pnpm demo opens it in your browser.

Shell
git clone https://github.com/rlfleming93/openCycle.git
cd openCycle
pnpm install
pnpm demo

Create a profile, pick a workout and start a session. The demo keeps its data in a temp folder, so it won't touch real rides. You need Node 24 and pnpm.

For real rides on a Mac, run pnpm app. It builds an openCycle app into your Applications folder. Open it, allow Bluetooth once, and it starts everything and opens the browser. Quit it from the menu bar. On any other computer, pnpm start serves the whole thing on port 4000.

40 seconds from the simulator. The jump into a new system, a burn that ends with the raider down, two riders on the same chase, then arrival.

There's no login.

Anyone who can reach the server can start sessions, change profiles and read ride data. Run it on a home network you trust. Don't put it on the internet.

The server listens on your whole network so a TV or tablet can show the ride. OPENCYCLE_HOST=127.0.0.1 keeps it on this computer.