Workout log updates… again: Tracking training load

This is the fourth in a series of posts documenting the making of my workout log. Although not required, you might be interested in reading the first three before proceeding:

  1. My blog workout log
  2. Workout log and site updates
  3. Workout log now totally, 100% feature complete (LOL!)

It started out, as these things often do, with a small spark of a thought.

‘I wonder what my training load right now is like compared to back when I was training to run marathons?’

My options for trying to get a proper answer were:

  1. Find a way to backfill my Intervals.icu account with my full workout history. I began using this magnificent service on recommendation from ‘sirpoc’ early last year. True to my liking, it is an indie service, and it lets you analyse your training in every which way you could imagine. In addition to logging everything I do in my workout log I also upload everything to Intervals to track my training load.
  2. Create the functionality to estimate training load and track how it changes over time within my own workout log.

One of these is a more exciting problem than the other. Also, much as I like Intervals, the thought of being able to track and measure ‘everything’ with my own home brewed solution still appeals. Guess I better get to work! But first, a small primer.

How do you even measure training load?

There are a whole host of different ways to do this, all with their own unique pros and cons.

As far as running goes, the most popular approach is the simplest: Track the distance you run for a given time period. Any self-respecting running log does this. Even mine! In the first and simplest version, I only showed totals per month. Then I discovered that I also like to see my weekly, yearly and overall distance totals. So I added those as well.

Distance run is a very crude measure. It doesn't say anything about whether your runs are easy or hard. Or somewhere in-between. Without the added context of time spent running, you cannot even figure out how fast (or slow, in my case) you went. Time spent running is also used as a way to measure training load. On its own it is probably marginally more informative than distance. Together with distance it starts to paint a richer picture. Especially when looking at trends.

Then there are more sophisticated methods. These typically originate from cycling, which tends to be far more advanced than running when it comes to using data to analyse and plan your training. Common for these approaches is that they aim to calculate your training load in a way that also reflects the intensity of your training. In other words, equal time at a higher intensity results in a higher training load. If you want to know more, I recommend the TrainingPeaks Education Articles. Begin with the post on TSS and work your way through.

My original idea was to compare my training load calculated in this manner against what I did a couple of years back. I already knew I was spending less time running fewer kilometres per week. But how did the increased frequency of quality sessions I am doing these days impact the weekly load?

Understanding my data

My first order of business was to determine exactly how to calculate training load per activity.

Ever since I first began tracking my workouts in 2017, I have been diligent in gathering consistent data. I've worn a GPS watch for practically every run. Ninety nine times out of a hundred I've worn a chest heart rate strap to record my heart rate. And since back in 2020 I've worn a footpod which estimates running power for the majority of my runs.

This gave me three options for estimating training load. Pace, power and heart rate. The fact that I was missing power data for activities up before early 2020 took that off the table. Which left pace and heart rate as viable options.

Both options rely on an estimate of threshold to calculate the intensity of an activity. This poses a challenge when it comes to looking at a dataset that covers a significant time frame. An accurate load calculation for any given activity would require knowing exactly what kind of shape I was in at that time.

When tracking our training in ‘real time’ you (ideally) incorporate all out fitness tests on a regular basis. And whatever your number ends up being, you use that as the basis for calculating your training load until you next perform a test.

Doing that after the fact, when looking at ten years of activities, is more challenging. While I've raced every now and then over the past decade, there are large periods for which I have no reliable fitness estimate. Enter heart rate.

Sure, heart rate comes with its own challenges. There is day to day variation based on factors like sleep, food intake, caffeine, time of day and much more. These factors makes it a less than ideal measure for modulating intensity during a workout. These variations do revolve around a mean, however, and cancel out over time. Which means that heart rate is actually well suited to estimating big picture things. Like, say, how training load evolves over time. The fact that it is also the most reliable number in my dataset, given my propensity for wearing a chest strap, makes it an easy choice.

There's just one caveat: Heart rate is not static over such a long period. Your max heart rate decreases with age. This is particularly relevant for a man who's entered middle age at the tail end of this period.

I know this from experience. Over the years I've seen my heart rate trending down for any given perceived intensity. Based on observing these changes, I have devised a way of adjusting my max and threshold heart rates: Every two years, I decrease both with a single point. My max heart rate, estimated to 200 beats per minutes back in 2017, now sits at 195 in 2026. Next year it will decrease another point. Similarly, my threshold heart rate has gone from 180 to 175 during this period, also decreasing with a point every other year.

Is this a perfect solution? Absolutely not. But it is good enough, and tracks very well with the observations I've made from tracking my heart rate during activity six days per week over the past ten years. Whether this formula will hold in the years to come, I have no idea. But my ‘fingerspitzgefühl’ for heart rate is such that I believe I will be able to pick up deviations. Especially as I plan to do all out efforts at least a couple of times per year.

My resting heart rate, another data point used in calculating TRIMP, has remained more or less stable throughout the years. So that one is constant through the entire period.

Which activities to include

The approach for calculating a training load per activity sorted, I had to figure out what to include and what to omit. The term ‘training load’ is actually a bit of a misnomer here. What I'm really trying to track is my cardiovascular training load. Or, to be even more precise, the only thing I'm really interested in is tracking my running training load.

On the surface, that points to omitting every other activity type from the calculation. But, there is some overlap. Think of all the times (ahem…) I've spent hours and hours on the elliptical. Cross training in an attempt to stem the fitness loss due to an injury that's kept me from running. Or all the roller ski sessions (cough…) I've put in to add some extra cardiovascular load without taxing my legs with the impact forces of running.

You get the point.

Determining the overlap is of course a futile exercise. I decided to take the simplest possible approach here. Apply the training load calculation as is to all activities that is cardiovascular exercise in nature. I mostly run. The bulk of my load is going to come from my runs either way.

My approach to achieve this is super simple. If heart rate is recorded, I calculate activity training load into the .md companion file that contains all the the data I want to use from the original activity .fit file. When generating the site (and workout log) the script checks the activity type against a list of specified types to determine whether training load should be displayed and used for downstream calculations.

Surfacing the training load data

Estimating training load for a bunch of activities is cool. So now every qualified activity (cardiovascular in nature, as defined by me, and heart rate data included) has a training load number. I can compare how the load differs from one session to another. Pretty great.

But what I'm really after is tracking my load over time. Enter the PMC, or Performance Management Chart. Once you're calculating training load per session, creating the PMC is fairly straightforward. And, more importantly, well documented.

My goal, as with previous features and illustrations in the workout log and this site at large, is to stick to simple, tried and tested web technologies. That means relying chiefly on HTML and CSS. For more complex illustrations, like the PMC, I turn to SVG. That means generating the illustrations on building the website. The result is sacrificing some niceties, such as user selectable display periods in favour of fixed periods. Since I am making this for precisely one user, myself, I know that this isn't a problem. I've spent a couple of years with Intervals.icu and I have stuck to the 365 day window for the PMC all the while. So I decided to display the chart for 365 days back at time of build in my implementation of the PMC.

That's not to say I'm not interested in seeing the chart for periods further back. Doing so was the thing that triggered this whole adventure, remember? I went with the obvious solution: Generate charts for every preceding 365 day period. All the way back to the first activity in my log. To navigate between the charts I reused the simple HTML and CSS tabbing solution I had previously used on the activity details sections.

Here, I ended up using a bit of JavaScript as a ‘progressive enhancement’. A ‘scrubber’ feature on the chart to show the exact values for a particular date when hovering or clicking on a date. The details box also shows the title of that day's activities, linking to the details. This easily allows me to navigate between the big picture, when looking at the chart, and the nitty gritty details on a workout level. Even so, if someone visits the PMC without JavaScript enabled, it still fulfils the basic purpose of displaying the development of my aerobic training load.

Wrapping up

Even at nearly two thousand words, I feel like I'm just scratching the surface and omitting a bunch of important details that went into making this feature. But I've been working on the post, on and off, for weeks already. And I need to just publish it. Because, believe it or not, since I began writing, I've also implemented a bunch of other ‘quality of life’ improvements to the workout log that I need to cover. In a completely separate post, of course.

Let's see if I get around to that by Christmas.

Now, let's close the loop on this post. What prompted this entire project was the question ‘I wonder what my training load right now is like compared to back when I was training to run marathons?’ The answer is now in hand. Or, rather, on the site. And you can see for yourself by checking out my Performance Management Chart and comparing my training load the past year against what I did in the period from 2018 to 2023.

The answer was definitive. I no longer have to wonder why I ran faster back then.

The more you know!