Cadence / measurement
One Step Per Beat
A few weeks ago my legs felt more tired than they should. I move a lot, and that week was calisthenics, running and kitesurfing, but still. So I asked Alfred, my AI assistant, to look at a year of my Garmin data. He came back with two things: most of my runs were a bit too hard, do more zone two, and here is a playlist at 164 beats a minute from your own library, so your feet hold the right cadence without you thinking about it. I liked the idea, skimmed the list, and went running. I did not check a single number. It was not the playlist I had asked for.
It felt off on the road: I kept having to change cadence between tracks that were supposed to be the same tempo. So I measured the whole hour, nearly nine kilometres of it, every track against what my feet actually did.
Sixty-one minutes, drawn
Why I was counting steps at all
Zone two. Most running should be easy, everyone says so, and it is surprisingly hard: zone two is light, and paradoxically you feel like you should be running faster. Some call it fighting your ego. I came back faster than I meant to every time, and only found out afterwards, from the watch. Cadence, the steps I take per minute, was the number I had never used.
I rarely run with music. It is usually friends, or a podcast, so an hour on my feet is an hour of talking or listening. Running to a beat was a change, and it turned into a game: the beat is in your ears and the job is to land on it. Stepping to the beat of Coleen is fun. Same with Kanye. Cinnamon Chasers and Sparta were the favourites, which tells you something about my taste.
What the numbers had to survive
Alfred picked 164 because my cadence had averaged 153 that week, and the one kilometre I had run at 164 was the one that felt fine. Then he read my library. I have around 2,500 liked tracks on Spotify and I do not know many of them by title: years ago I liked whole albums, and there are tracks on them I clearly do not like. I did not give Alfred that detail. So the list had tracks I would never put there. It had way too much Morphine. Buena mesmerises me every time and I would still not call it running music. Thirty-five tracks, a list called 164bpm run, and I assumed the number, at least, was right. A tempo is a number, and a machine should get a number right.
It is not. Chasing that down turned into its own job: the sources disagree about which octave a song lives in, and one of them read a 172 bpm drum and bass track as 115. I was building a video editor that cuts to the beat at the time, so I went down the hole of how tempo is measured and why it went so wrong. That is another article. What came out of it is one rule every number on this page has to pass: pin the recording by its ISRC, read its tempo from more than one source, and treat anything fewer than two agree on as an opinion, not a reading.
With the tempos pinned, the list gave up its other secret. 164bpm run was not a 164 playlist at all, it was two, one near 161 and one near 165, and a third of this run was spent on the wrong one. I felt every move between 160 and 166, and the jazz with a drifting tempo confused the brain-leg connection entirely, which is what the skips on the trace are. For the record the run itself came back easy, heart rate 132 average, 139 maximum. One run cannot tell me whether the music is why.
Tempo, not pace
The obvious objection: I ran faster during the faster songs and cadence came along for the ride. Across these fourteen tracks cadence barely tracks pace () and tracks tempo almost perfectly ().
Each dot is a track, tempo across, my average cadence up, size is time on it. The dashed line is not fitted, it is y = x. The measured slope is : one extra beat in the music, one extra step on the ground.
The dot off the line is the track a single source claimed. My feet disagreed with it by two steps a minute, which is roughly how much I trust that number.
Cadence is not speed: speed is cadence times how far each step carries you, so the same pace at a higher cadence means shorter steps. The run happens to contain a matched pair, two songs, not a trial. Coleen came up at 6:58 per kilometre, heart rate 134, and I loved running to it. Lmchi w Rjou3 came up at 6:58 per kilometre, heart rate 134. On Coleen I took 165.8 steps a minute, each step covering 0.87 m. On Lmchi w Rjou3 I took 160.2, each step covering 0.90 m. Same pace, same heart rate, a shorter step at the higher cadence.
A week later, no music
A week later I ran with no music. Lex Fridman and DHH in my ears instead, five hours of two programmers arguing about what AI does to the craft, which is my day job, so the hour went quickly.
Recorded heart rate was almost the same, 131 average and 139 maximum against 132 and 139. Pace was faster, 6:36 per kilometre against 6:58. Cadence, with nothing to follow, averaged 169.5, higher than every track in the playlist. Second-to-second wobble around a one-minute local mean was about the same as with music, 1.4 steps a minute against 1.6. What the trace does not have is the steps. Instead it drifts, from 166 in the first ten minutes up to 172 at the peak. This was not a controlled comparison, and it gives me no reason to credit the music with a faster or a steadier run.
I expected a mess without the beat and got a straighter line at a higher number. That run, faster and without music, averaged 169.5, which makes me question the 164 target, but one run cannot say what the right number was for the first one. A better-built playlist would probably hold cadence wherever I put it, and I now have the two lists to try. Running to the beat is a game, a good one. But not having to step to a beat leaves room to adjust pace to heart rate, and zone two is about heart rate, not cadence. So podcasts or silence for most easy runs, and I keep running and learning at the same time. The episode is number 501, DHH on programming with agents, five hours, enough for quite a few runs, worth it whether or not you write code.
What this cannot tell you
Two windows in the run carry no logged track, 154 and 64 seconds wide, hatched on the trace. Spotify only records a play once it passes thirty seconds, so a skipped track leaves a hole rather than a name, at least nine of them.
The agreement here is . That is what entrainment looks like on one run by one runner, not a result about running.
On this run cadence and tempo moved together, step for step, and the matched pair suggests this was not simply faster running. That is as far as the data goes.
How this was built
The pipeline pulls the watch data at one sample per second, rebuilds Spotify’s play windows backwards from the timestamp it stores when a track ends, pins every track to one recording by ISRC, checks its tempo against three sources, and lays the result on the cadence trace, with Spotify’s own player wired to it. The playlist took one sitting, and a third of it was not what I meant by 164. The check took longer than the build. Alfred did a mediocre job picking the music and a good one measuring what my feet did with it. Both are the job.
Cadence, pace, heart rate: Garmin Venu 3, one sample per second. Play history: Spotify recently-played, where the timestamp marks the end of a track, so each window is reconstructed backwards from it. The 4 September run: same watch, same sampling, no play history to align.
Tempo: Deezer BPM by ISRC, AcousticBrainz by ISRC via MusicBrainz, and a spectral-flux onset autocorrelation multiplied by a Fourier tempogram at the same lag, run over the thirty-second preview of the ISRC-matched recording. Agreement window 3 bpm, all readings folded into one octave first.
Playback is Spotify’s own embedded player. Signed out it gives thirty seconds of each track; signed in to a Premium account in the same browser it gives the whole thing.
Want this on your own runs?
It takes a watch that logs cadence every second, a play history with timestamps, and an agent willing to check its own numbers instead of trusting the first one it finds. If you would like your agent to run this analysis on your runs, drop me a line at mateusz@sawka.pro.