Building a Running Pace Calculator With AMP

Sometimes you need to know how fast you need to run to achieve a personal best time. Previously the way I did this was to search “running pace calculator” and follow and use one of the top results. However, I was doing this almost always on mobile and none of those results are very mobile friendly. There might be good native apps for this, but I’m a fan of the web and don’t want to download an extra app if I can avoid it.


Normally you wouldn’t think of AMP as a dynamic web framework, but it recently gained the ability to build dynamic web pages with amp-bind and also now supports some PWA features like Service Worker. I’m a fan of AMP and I thought this would be an awesome project to learn more about those features.


Before going into implementation details, here’s the finished product, live on


The setup for this project was super simple. You can view all the code via the source on For hosting I chose Firebase, using the same automatic deploy-on-green method I describe here.

Here’s a quick run through how I used amp-bind in this project. amp-bind works off a global state using AMP.setState(). That means each input can set its own value in the store, like this:

<input type="number"
  on="input-throttled:AMP.setState({ timeHours: event.value })">

The distance shortcut buttons are implemented similarly, setting distance:

<div class="button-set">
  <button on="tap:AMP.setState({distance: 3.1})">5K</button>
  <button on="tap:AMP.setState({distance: 6.2})">10K</button>
  <button on="tap:AMP.setState({distance: 13.1})">Half Marathon</button>
  <button on="tap:AMP.setState({distance: 26.2})">Marathon</button>

The pace calculation happens when you push the “Calculate” buttons. It gets a little verbose, but it works.

<button on="tap:AMP.setState({
    paceHours: toHours(paceInSeconds(timeHours, timeMinutes, timeSeconds, distance)),
    paceMinutes: toMinutes(paceInSeconds(timeHours, timeMinutes, timeSeconds, distance)),
    paceSeconds: toSeconds(paceInSeconds(timeHours, timeMinutes, timeSeconds, distance)),
  Calculate Pace

The functions used above, like timeInSeconds are achieved using amp-bind-macro for re-using calculations. For example:

<amp-bind-macro id="timeInSeconds"
  arguments="paceHours, paceMinutes, paceSeconds, distance"
  expression="(paceHours*3600 + paceMinutes*60 + paceSeconds*1) * distance" />

I also enabled offline loading with amp-install-serviceworker as described here.

Thoughts on AMP as a dynamic web framework

I was surprised how versatile AMP can be when utilizing amp-bind. Anyone with experience using a JavaScript web framework can quickly grok how amp-bind works. I like that it supports a React/Redux-esque global state store and doesn’t require a huge learning curve.

Finishing thoughts

The site is live at! I don’t have any future plans for it, but if you have any ideas or feedback I’d love to hear it. Thanks for reading.

Contents (top)