Download at https://trajectory.fitness.
Powerful local-first workout analyzer that keeps your data private.
A local-first desktop app for exploring .tcx/.fit activities with dashboard, maps, streaks and custom analytics.
No cloud sync, no account, no server. Just you and your training data.
There is no single reason for why I built Trajectory, but rather a combination of factors:
- I wanted a local-first solution to analyze my workout data without relying on cloud platforms or accounts.
- I wanted to get the most out of my workout data. Having collected so many data points over the years, I wanted a way to explore and analyze it in more depth, beyond what the usual fitness platforms offer (and more tailored to my needs). See Advanced Analytics for more details and some useful examples.
- I wanted to learn about Tauri and Rust by building a real-world app.
- Last but not least, I was inspired by the "you can just do things" philosophy expressed by Peter Steinberger and many others.
You can install Trajectory by following the instructions on https://trajectory.fitness or build from source using the instructions below.
Current builds support Apple Silicon on macOS and 64-bit systems on Windows and Linux.
Notes:
-
Release builds are not yet production-signed, so your operating system may show a security warning on first launch.
-
If macOS reports that Trajectory is damaged or corrupted, remove the quarantine flag after installation:
sudo xattr -dr com.apple.quarantine /Applications/Trajectory.app
-
On macOS, if you still cannot open the app, follow the instructions on Apple's support site.
- Launch Trajectory.
- Select your activity folder containing
.tcxand/or.fitfiles. - Start in Dashboard for a quick overview of training load and history.
- Use Activities to filter, sort, and inspect individual workouts. Select a chart range to see segment metrics; Show Full Workout restores the activity totals.
- Open Heatmap to spot route patterns across all GPS sessions.
- Use Advanced Analytics to define custom metrics, streaks, and charts. See Advanced Analytics for more details and examples.
- Open Settings to customize appearance, configure heart-rate zones, and run rescans.
Activity details show Heart Rate Drift and Pace–HR decoupling for the full workout or a selected chart range. Both compare time-weighted averages in two equal halves of active time, excluding recorded pauses:
- Heart Rate Drift: the percentage increase in average HR. For example, 140 → 150 bpm gives +7.1%. During steady exercise, rising HR can indicate increasing cardiovascular strain.
- Pace–HR decoupling: the percentage decline in average speed / average HR. Positive values mean less speed per heartbeat; negative values mean improved efficiency. It also detects slowing down at constant HR. At constant speed, the example above gives +6.7%: the percentages are not interchangeable.
How to use them: select a steady aerobic section longer than 20 minutes, excluding warm-up and cool-down, and compare similar sessions over time. Lower decoupling can suggest better endurance at that workload. Roughly 5% is a common coaching benchmark, not an automatic aerobic-threshold test.
Interpretation: heat, terrain, and changing pace can affect the result. Trajectory uses speed without adjusting for hills or using cycling power. For a structured field-test protocol, see Scott Johnston's guide to aerobic assessment, which explains both constant-speed HR drift and outdoor pace–HR analysis.
Availability: at least one minute of active time and valid HR for 80% of each half; decoupling requires paired positive speed and HR for 80% of each half. Insufficient coverage shows Unavailable. Selecting a range also updates workout metrics and HR zones.
Thanks to Will (@bitloop-dev) and @farrrunnner for adding these metrics and fixing various other bugs.
Here is the workflow I personally use (and would recommend) for athletes in the Apple ecosystem:
-
Set up the iOS app RunGap. This is a powerful workout data manager that can sync up activities from all the usual fitness platforms (Strava, Garmin Connect, Apple Health, etc.). Set it up to sync your workouts to a local folder on your Mac (e.g. via iCloud Drive or Dropbox).
-
Install Trajectory on your Mac. Point it to the same folder where RunGap is syncing your workouts. Done.
Trajectory allows you to get the most out of your data by providing a simple yet powerful tool to define custom metrics.
To get started, navigate to the Advanced Analytics section in the sidebar and switch to the Configure mode. There are three fundamental building blocks to create your custom analytics:
- Metrics, which can be subdivided into
- base metrics and
- formula metrics
- Streaks
- Charts
Each type is explained below.
Metrics
Metrics are the core building block of the analytics system. They represent individual data points that can be calculated from your workout data. There are two types of metrics:
- Base Metrics: These are directly calculated from the raw workout data.
- Formula Metrics: These are defined as formulas that can reference other metrics (both base and formula). This allows you to create complex derived metrics based on simpler ones.
A base metric allows to measure:
- activities count
- active days count
- distance sum
- duration sum
- moving time sum
- elevation gain sum
- sample time
After deciding on a base metric, one can also empose conditions such as title, category, distance, duration and many more. Multiple conditions can be combined using AND/OR logic, allowing for highly customized metrics. Then the metric is calculated for all activities that satisfy the conditions. It can be directly displayed (one aggregated value) or it can be used as a building block for more complex metrics, streaks and charts.
The base metric sample time works a bit differently from the others. It allows for more granular time-based calculations within activities. One can measure the time spent doing an activity while satisfying certain conditions. Using AND/OR logic, one can combine conditions of the following types:
- heart rate zone
- heart rate
- power
- cadence
- speed
- pace
On top of that, one can empose a minimum continuous match time, which allows to only count time intervals where the conditions are continuously satisfied for at least the specified time. At the bottom of the configuration page, there is a visual representation of which sample times where filtered out (before and after applying the minimum continuous match time condition), which can be very useful to make sure the metric is configured as intended.
A formula metric allows to define a metric as a formula that can reference other metrics (both base and formula). This allows to create complex derived metrics based on simpler ones.
Streaks
A streak tracks consecutive periods where your metrics hit a goal. You can configure:
- Period: day or week
- Threshold operator:
>,>=,<,<=,= - Threshold value: numeric target to compare against
- Required metrics: one or more metrics that must all pass the threshold (AND logic)
Trajectory then computes:
- Current streak (how many consecutive periods up to now satisfy the rule)
- Longest streak in your available history
- Status (
active,pending,broken) - Current period value (with progress toward the configured threshold)
Charts
A chart view turns saved metrics into time-bucketed visualizations. You can configure:
- Chart type:
- Bar (exactly 1 metric)
- Line (exactly 1 metric)
- Stacked Bar (2 to 5 metrics)
- Granularity: day, week, or month
- Time range: all time, rolling windows, or custom dates
Each chart supports interactive zoom on the x-axis and includes a dedicated preview. Note that the preview shows all data points, while the chart in view mode respects the selected time range.
All the following examples can be imported using the file
file assets/trajectory-advanced-analytics-examples.json.
-
Let's start with my favorite metric aerobic time fraction. It measures the fraction of time spent in aerobic heart rate zones during workouts. It crucially relies on the base metric sample time with heart rate zone conditions and with a minimum continuous match time of 5 minutes (to filter out short intervals that don't really count as aerobic efforts, e.g. during weight training).
-
Another useful metric is the weekly push/pull streak. I like to do push/pull splits and indicate them in the workout title (e.g. "Push day", "Pull day"). This streak tracks consecutive weeks where I do at least one push day and one pull day, which helps me maintain a balanced routine.
-
Another interesting metric is called risky ride. It measures the time spent riding faster than 50 km/h, which can be useful to convince your loved ones that your hobby is not that dangerous after all.
- Your activity files stay local
- Data is processed on-device
- No account or telemetry backend required
- Map basemaps are fetched from OpenStreetMap at runtime
- macOS on Apple Silicon (
aarch64) - Windows 64-bit (
x64) - Linux 64-bit (
amd64)
Any feedback and contributions are very welcome! Bug reports, ideas, documentation improvements, and code changes all help. Feel free to open an issue or submit a pull request.
Thank you to Will (@bitloop-dev) and @farrrunnner for their contributions and for helping make Trajectory better.
This section is for contributors and developers.
- Node.js 22.12+
- Rust stable
npm install
npm run tauri devnpm run checknpm run tauri buildOutput artifacts are generated under:
src-tauri/target/release/bundle/macos/*.appsrc-tauri/target/release/bundle/dmg/*.dmgsrc-tauri/target/release/bundle/nsis/*.exesrc-tauri/target/release/bundle/msi/*.msisrc-tauri/target/release/bundle/deb/*.debsrc-tauri/target/release/bundle/appimage/*.AppImage
- Developer guide:
docs/DEVELOPER_GUIDE.md
- Tauri v2 + Rust
- React + TypeScript + Vite + Tailwind CSS
- SQLite (local app storage)
- Route planner.
- Production code signing and macOS notarization.
MIT. See LICENSE.