A while ago me and some friends of mine released MusicMap, a Spotify app that has you navigating a 3D-globe representation of Earth, listening to popular music by artists from the countries you click. This post outlines how the 3D-globe was built.
The MusicMap globe is based on The WebGL Globe project, one of the Chrome Experiments. The WebGL Globe was primarily built to visualize geo-data, but it’s a good baseline globe implementation with rotation and zooming among other things. The WebGL Globe is built with Three.js, an abstraction layer atop WebGL.
After stumbling across Ilmari Heikkinen’s excellent post on the Making of the World Wonders 3D Globe, we thought the look he was achieving with vector continents was really cool, and decided to go with that. He in turn got the vector data from Mozilla’s Globe Tweeter demo, which uses a heavily compressed version of continent data from Natural Earth. This compression is why, up close, the shape of the continents look quite rough. Even so, the size of the compressed js-file containing the continent data is a whopping 2 megabytes.
As for the starry backdrop, it is made up by 300 tiny spheres positioned randomly along the surface of a scene-containing sphere, dimly lit from afar by a spotlight to create a gradient effect.
The country label
We also tried to implement country labels similar to Heikkinen’s CSS markers, but quickly ran into performance issues due to the fact that we needed to render a lot of labels, for a lot of countries. Moving that many DOM-elements around the screen at once was just not going to work.
After trying a couple of WebGL solutions, we decided to go with a simple CSS label that follows the mouse around as you hover the countries. In order to do this, we needed to be able to convert a mouse location to a country name, and fast. Calculating the latitude and longitude was not a problem, but converting that information into the name of a country typically means shooting of a request to some external service, such as GeoNames, which is just way to slow.
If we could just map out what pixel on the globe belonged to what country, we could solve the problem without needing to bring in latitudes and longitudes at all. What we wanted was some kind of lookup texture that could be positioned off screen, where each country was represented by an identifier that could be translated into the name of the country. Ideally, this lookup texture would be a map of the world, where each pixel would contain the identifier. Luckily, we found such a texture in another Chrome Experiment. The globe in Small Arms and Ammunition - Imports & Exports uses a greyscale map of the world, where each country can be identified by a particular shade of grey. This texture we rendered off screen, invisible to users. After that, the conversion of a screen coordinate to a country name looked something like this:
- Convert screen coordinate to globe coordinate. We do this using ray tracing: we shoot a ray from the position of the camera towards the coordinates of the mouse and grab the exact point in space where the ray intersects the globe.
- Convert globe coordinate to texture coordinate. We use the formula for UV mapping of a sphere in order to calculate the UV coordinates. These coordinates represent a pixel in the lookup texture.
- Convert texture coordinate to country code. We take the texture coordinate and read the color of the pixel from the lookup texture. This gives us the particular shade of grey representing the country beneath the mouse cursor.
- Convert country code to country name. We do a simple lookup in the country table and finally have the name of the country.
What all of this means is that we get super fast country name lookups, fast enough so that we can have our label follow the mouse cursor around.
This project took quite a while to finish. Probably mostly because none of us had experience doing 3D-programming, which, it turns out, is kinda hard. There’s math. Lots and lots of math. But even though the code is probably horrible and full of things seasoned 3D-programmers wouldn’t dream of doing, we thought we’d put it up on GitHub. There might be useful parts here and there. We fixed up the World Wonders 3D Globe code to work with recent versions of Three.js for instance.
This has been a fun project to work on, even though at times it felt like we would never finish. Thanks to my co-developers Sandra, Rickard and Johanna for making sure we did, and thanks to everyone who’s used the app and given us great feedback!