RandomRun creates new routes with turn-by-turn voice-guided instructions for running, jogging or walking. You tell RandomRun how far you'd like to go, choose a loop that suits you, and you're on your way. It was a simple and attractive idea, but there were some potential pitfalls that needed consideration.
First, we weren’t aware of any off-the-shelf turn-by-turn direction libraries for a mobile platform. That meant building our own, and there are a lot of challenges to building a turn-by-turn system. We also needed to figure out how to plan a random route. Runners mostly want to run along roads and paths but we don't have data for roads and paths readily available. And if we did (say we got an Open Street Map server up and running) we would have to know all the rules for where people are allowed to run (e.g. not highways). But basic route generation services do exist, so, confident in our abilities, we set out to solve some of these problems and develop our app for iOS, Apple Watch, and Pebble.
Most routing systems are designed to get you from point A to point B in the shortest distance or time possible. We want a routing system to get you from point A to point A that takes you along a specific distance. A simple solution suggests itself.
If you want to run a distance of D miles:
This oversimplified approach already presents two problems. The idea of the app is appealing partly because of the promise of variety in your running routine -- this out-and-back run will always be half-boring. And just because you pick a point two miles away doesn't mean that walking directions there and back will cover four miles -- it will very likely be longer.
Instead of going out and back, let's make a loop. That's pretty simple geometry.
To make a loop of distance D:
Now we have N routes that if we follow in sequence ought to take us on a more interesting run.
All the routes we have generated so far share an interesting problem: they're all too long. That's because we're not out running in open fields; we're following roads. Even if you have a perfectly regular grid of roads for miles and miles, the distance along city blocks is going to be longer than the straight-line distance if you aren’t picking paths that perfectly align to the grid.
After playing around with some interesting analytical geometry of ellipses in an ℓ1 norm we decided we could save some headaches and just scale everything down by the square root of 2.
Once we generate use the scaling factor to shrink the desired loop down a bit and then generate a set of end-to-end routes to make the loop, we have a running route. Often it turns out that sometimes the sum total distance is significantly different from what the user asked for. Sometimes, if one of the points we randomly selected happens to be in the middle of the ocean (remember that as the program we can't see what's on the map) or is otherwise unroutable, we end up with a gap in our loop. In these cases, we pick a new direction and try again.
Now we have a set of routes. But because the routing service we are using doesn’t know they are intended to go together, many odd things happen at the joints.
The route indicates that the route has ended, then indicates that a new route is beginning. In other words, a third of the way through your run, you might hear “You have arrived at your destination. Proceed to Meeting Street”. In the simplest case, we can fix this by removing the last instruction from one leg of the run and the first instruction form the next leg of the run, and you no longer hear these spurious maneuvers.
This, however, this only works when you need to continue on the same road you were on. So we have to additionally examine the route geometry at the seam and determine whether we need to present the user with a new maneuver, such as “Turn right on Meeting Street”, or even just “Turn around”.
Another issue that often occurs at the seams of a run are something we call “spurs”. Because the points we use to generate the run can’t take into account what’s on the map, and because the routing system thinks you’re trying to arrive at a destination, sometimes you’ll find directions that take you along a road, you turn off to a side street just to find that you need to turn around a half a block later. Other times the detour may take you two or three blocks out, with several turns, just to have you come back to where you were. And yet other times an entire run may consist of just a few spurs, each several miles long.
So we have to be cautious when trimming these from a run. We want to provide the runner with a smooth experience, but we want to make sure to not throw out perfectly valid runs in areas that necessitate a little backtracking.
There are four considerations we need to address when building a turn-by-turn engine.
The app has text directions coming in from the direction service, but these are intended to be read not heard. When translating the written form of the directions into speech, issues with abbreviations occur. The abbreviations for North, South, East, and West cannot be translated from N, S, E, W.
Road abbreviations have their own host of transformational issues. If a direction is read as, “R on St. Louis Ave.,” you may end up hearing “Arr on Street Louis Ave” instead of “Right on Saint Louis Avenue”.
When running, you expect to hear the instructions for your next maneuver far enough in advance to do something about it. And the spoken directions take just as long to say aloud whether you’re working on breaking a 5-minute mile or you realized that RandomWalk is just as good a name for the app. So the app needs to dynamically track your speed to decide when to give you advice in a timely fashion. Furthermore, it needs to register whether you successfully reached, or passed your coordinate so it can provide the next maneuver.
In essence, the computer is your co-pilot and anyone who has ever taken a road trip knows the importance of a co-pilot who can anticipate the driver’s needs.
Next up, we worked on the “odometer” function. This is everything relating to a sequence of GPS positions that give you your run statistics such as current split, previous split, and historical splits for previous miles. Implementing the odometer function is not an easy task because GPS data is inherently noisy. The GPS signal tends to jump around which means we have to be able to smooth out those jumps in order to deliver better data to the runner.
In the days that followed the launch, RandomRun began to gain traction in the iTunes App Store and was featured on ProductHunt. We continue to gather feedback and improve the app and its features on a regular basis, including adapting the app for Apple Watch and Pebble.
Apple offered to early-ship Apple Watch orders to developers in anticipation of the official launch. Early access to the watch allowed us to understand how watch app interactions differed from phone apps.
Watch users want to tap and go -- they don’t want to spend more than a few seconds getting setup. As a result, the RandomRun app was stripped down to essential functions only. And scaling back was a good thing. Most apps have a tendency to cram in too much, attempting to transfer the functionality of an iOS app to the watch without any respect for product differentiation or user behavior.
Like any project we take on at MojoTech, we never ship for the sake of shipping. As watch development and demand increases, we continue to work on creating a rich and enjoyable experience that doesn’t interfere with your motivation to get up and go.
Let's launch your product. Contact us.→