Yesterday, Tycho and I released Forecast, an app which uses your local weather forecast to generate a unique Spotify and Apple Music playlist. Not only was this a fun developer challenge, it was also an opportunity to work with Scott (Tycho) directly on design. I think the concept works because it meets each of the three principles I strive for: Simple, Accessible, and Magic. It’s simple in that the user merely needs to tap the sun in order to invest themselves in the app. It’s accessible via the web from a url. No downloading needed. And it turns the fucking weather into music! That’s magic.

The sweet design doesn’t hurt either.

These days users want to put themselves within the app and location is one of those readily available variables which can make the experience more unique. I’ve used it to similar effect when terrifying Manson’s following and allowing Khruangbin fans to generate music for upcoming flights. Just don’t be creepy with location data.

So, how does one turn the weather into music? Read on to find out.

Getting the Forecast

Icons by Tycho

It all starts with a pair of coordinates which you can get by using the Geolocation API. In addition, I’m using the MaxMind GeoIP service as a fallback in case something goes wrong. MaxMind will turn the user’s IP address into a location which is a solid fallback for a weather app.

navigator.geolocation.getCurrentPosition(response => {

// response.coords.latitude

// response.coords.longitude

}, error => {

geoip2.city(response => {

// response.location.latitude

// response.location.longitude

}, error => {

// ok, something really went wrong

})

})

With coordinates now available it’s time to get the forecast. Dark Sky is a popular weather app (the one I use) which also has an excellent and affordable API for getting past, current, and future weather conditions. I’ve actually used it at least once before on the Purple Rain Report. The endpoint we’re most interested in is forecast which takes a pair of coordinates and returns the weather. But first, let’s talk about my setup.

I am a recent Netlify convert and I simply can’t get enough of how this platform has streamlined my workflow. I’m able to run their platform right from my computer using Netlify Dev which keeps all my environment variables synced. In addition, Netlify also allows you to easily deploy serverless functions which can also be tested locally with Dev. Functions are a perfect solution to meet Dark Sky’s security requirement of making calls via your server. Ya know, without the server.

To initialize a function, I might use the Netlify CLI command:

netlify function create --name forecast

With your function created, you can write it very simply. I’m using the node-fetch module which brings window.fetch to Node.js to hit the Dark Sky API with a pair of coordinates passed as parameters to the Netlify function. Here’s a gist to get you going.

Finally, if you’re using Netlify Dev, you can test your function locally by simply sending a request to the appropriate endpoint. In the case of a function called “forecast,” it would be accessed from /.netlify/functions/forecast . So, once I obtain those coordinates, I can send a fetch request from my client to receive the user’s current weather conditions.

fetch(`/.netlify/functions/forecast?latitude=${lat}&longitude=${lon}`)

.then(response => response.json())

.then(data => {

// data.currently

})

Correlating Weather to Audio Features

Dark Sky sends over a lot of interesting data about the current weather. The next step is to relate that data somehow to features of an audio track. In previous projects such as Sadboi Detector and AirKhruang, I have utilized the track audio features Spotify provides such as valence and energy. In the case of this app, we looked at several audio features and simply chose which weather measurement might symbolically relate. For example, a gusty day might produce tracks with a higher tempo. Or the temperature might affect the valence or mood of the songs. We took liberty in these connections and just had fun with out.

Most of these properties on Spotify run from a scale of 0.0 to 1.0 . Somehow I was going to need to create a similar scale for the weather measurements being utilized. I ended up researching the a average, max, and minimum of each of these and then used a normalization function to convert them into the scale I desired.

let normalize = (val, min, max) => {

return Math.max(0, Math.min(1, (val - min) / (max - min)))

}

For example, if the average wind speed in the United States is 9mph, I could say that is 0.5 on our scale. Making 0mph equal to 0.0 and 18mph equal to 1.0 . It’s not very scientific but it works. Once you do this for everything, you’re left with an array of data which represents all of the weather properties, the audio feature they relate to, and their normalized value.

let measures = [

{

property: 'wind',

feature: 'tempo',

value: normalize(wind, 0, 18)

}

]

I then loop through each of these measures, sorting the entire song list by feature values which are nearest to the normalized weather value. This sorted songs list is then used to increment the score of a song depending on its position in the stack. This process is continued for each measurement until all song scores are adjusted accordingly.

measures.forEach(measure => {

songs.sort((a, b) => {

let aValue = a.features[measure.feature]

let bValue = b.features[measure.feature] return Math.abs(aVal - measure.value) - Math.abs(bVal - measure.value)

}) songs.forEach((song, i) => {

let score = _.round(1 - (i / (songs.length - 1)), 3)



song.score += score

})

})

Songs are sorted by score and then the app takes the 25 highest scorers. Finally, the the selected tracks are shuffled, producing a playlist.

songs.sort((a, b) => {

return a.score - b.score

}).reverse() let playlist = _.shuffle(_.take(songs, 25))

It’s worth mentioning that I integrated Contentful as the system where we manage our pool of songs. The client was also provided with two UI extensions for easily searching Spotify and Apple Music songs from within the comfort of Contentful. Check out this blog for info on how I integrate Contentful into my app framework of choice Nuxt.js. In addition, I wrote an article on extending Contentful to include streaming service search fields.

Design and Transitions