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.
Motivation
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.
Result
Before going into implementation details, here’s the finished product, live on pacing.run:
Implementation
The setup for this project was super simple. You can view all the code via the source on pacing.run. 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"
name="time-hours"
min="0"
max="1000"
placeholder="0"
[value]="timeHours"
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>
</div>
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
</button>
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 pacing.run! 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.