Earth at Night, Mountains of Light

What if Earth’s terrain was created by nighttime lights?

Japan at night

Recently I worked on a 3D JavaScript mapping experiment where the luminance of light emissions captured in satellite imagery raises and sculpts new mountain ranges across the globe.  Brighter areas become high peaks, while darker regions flatten into valleys and plains.

Note: This post could have been written in puns alone, but I’m going to hold myself back from that and try to be a good person.  But I mean if you want me to shed some *light* on the situation and step out of the *shadows*, this wasn’t a *night*mare to make…

Another note: I believe Sarah coined the term “mountains of light“.  I like it, thanks!

Here’s the general workflow of how the code works:

  1. Create a custom elevation layer with the ArcGIS API for JavaScript.
  2. Get that elevation layer wired up to Nasa’s nighttime lights satellite imagery server.
    1. Ok fine, if you really want to geek out with me, it comes from the Suomi-NPP VIIRS sensor.
    2. The polished and cleaned up pixels are called “Black Marble” and provide a 500m image resolution.
    3. This isn’t live data but rather a static composite of images taken at different times.  Joshua Stevens and Rob Simmons helped me get a better understanding of the hard work that went in to creating and cleaning up this raw imagery into a beautiful worldwide dataset.
  3. Back to the code: for every nighttime light pixel, convert the visual color ramp that goes from dark blue/black to yellow/white into a simple 0-1 number range.  For this I used the chroma.js “luminance” calculation.
  4. Take the 0-1 number value and multiply it by some huge number!  We don’t want 1 meter tall mounds, we want wild and exaggerated mountains reaching high into the sky.
  5. Drape anything you want on top of this fake terrain.  In this case it made sense to also put the nighttime lights imagery on top of the terrain that it generated.

This is a view of northern India, the Himalaya, Nepal, Bhutan, and the Tibetan plateau with our fake mountains at night.

Earth at Night: Himalaya night

If we drape daytime satellite imagery over this synthetic terrain it is hard not to notice something is a bit off with the Himalaya.  Viewing the Earth this way wasn’t the original intent of the app, but it is fun to look at if you don’t mind the headache.

Earth at Night: Himalaya day

Not gonna lie, I’m also pretty damn hyped that this JavaScript dataviz experiment made it on to Kenneth Field’s “Favourite maps from 2018” and was also featured on Nathan Yau’s FlowingData and John Metcalfe‘s Washington Post article.  Thank you all very much!

And here’s a bunch of other links to keep you going down the rabbit hole.

Visit the app here:

Flow Mapping Bezier Curves onto the HTML Canvas

*This post was written in February 2017. It was the first collaborative geovisual project between Jacob and myself. We have since presented this mapping method at the North American Cartographic Information Society annual conference in Montreal, Quebec in October 2017.

This post presents our Canvas-Flowmap-Layer, an extension of the ArcGIS API for JavaScript (Esri JSAPI) for the purpose of mapping the flow of objects from an origin point to a destination point by using a Bezier curve. Collaboration with friend and colleague, Jacob Wasilkowski, was precisely the push I needed to finally (re)begin releasing some (non-work-related) mapping and dataviz projects into the wild. It is imperative to note that much of the responsibility for this repo rests on Jacob’s shoulders, even though it is under my name.

Flow maps, academically speaking

In Bernhard Jenny et al.’s recent article Design principles for origin-destination flow maps(2016) flow maps are defined as maps that “visualize movement using a static image and demonstrate not only which places have been affected by movement but also the direction and volume of movement” (p 1). Jenny et al.’s article points out the lack of empirical user studies for flow map design, stating that “Design principles for flow maps are largely based on expert intuition and aesthetic considerations” (p. 1). I am the type of cartographer who finds extreme liberation in these two aforementioned points, which I will restate below:

The lack of empirically based cartographic design principles results in the freedom of relying on intuition and aesthetics.

So for all the mappers out there reading this, I hope you find extreme freedom in knowing that this Canvas-Flowmap-Layer is only one of many ways to map the journey of phenomenon from one spot on Earth to another. It is one method that we are quite comfortable in presenting, and have successfully implemented versions of the Canvas-Flowmap-Layer in a few real world scenarios.

Curves with Rules

Common solutions for dynamic flow mapping include using straight lines and geodesic lines, both of which have immediate visual limitations. Using a Bezier curve for flow map lines, where each curve on the map is created with the same formula benefits mappers twofold:

  1. While straight lines are not inherently ugly, an overlapping or convergence of them across a global dataset can be. A series of Bezier curves created with the same formula, even when displaying an over-abundance, has a mathematically congruent flow that greatly improves the map’s aesthetics.

2. The single formula that is used to draw each of the Bezier curves means that map readers can immediately know the direction of the line without having to add animation (explained below).


Ultimately, the flow line’s direction from the origin is the order of the bend (a northwest direction’s curve bends north-then-west)

  • northwest from the origin, the curvature will bend northward-then-west
  • southwest from the origin, the curvature will bend southward-then-west
  • northeast from the origin, the curvature will bend northward-then-east
  • southeast from the origin, the curvature will bend southward-then-east

As you can see from the image below, if the end point is due north (or south, east, or west) of the origin, the resulting curve is a straight line.


Each point is added to the map twice – the first instance of each point is an invisible “ghost” Esri graphic with associated attribute data, geometry, etc. We call these “ghost” graphics since the symbology has no outline or fill color. These ghost graphics are required for 1) having a to- and from- point from which to connect the Bezier curve, and 2) allowing the points to provide functionality and interactivity that developers and users are familiar with.

The second instance of the points is what you actually see on the map. Each one of these points has the same radius as their ghost counterparts that lie directly beneath them, but are created by finding the screen coordinates of the ghost graphic and then placing them onto the HTML canvas. By doing this, we are able to draw the curved lines that connect the points onto the canvas.


I was drafting Bezier curves onto prototypes before this became an actual interactive map layer. Once it came time to add the curves to the map canvas there was no shortage of resources for figuring it out. Here is one of the many useful out there. The lines are added to the HTML canvas by finding the screen coordinates of the points, and then using those coordinates in the .bezierCurveTo() canvas method.


The convexity or concavity of the curve does convey the direction of the flow line, but the directionality won’t be easily intuited due to its novelty. In the event that this mapping technique catches like wildfire, we can delete the second part of the previous sentence. In the meantime, we’ve added line animations, similar to the “ants marching” effect, with a nice easing effect inspired by this Kirupa post. The Canvas-Flowmap-Layer uses two separate lines when animation is added, although two lines are not required to achieve animation. The first line is the solid static Bezier curve, and the second line is the dotted or hashed animated Bezier curve that sits on top of the first line. Those animated traveling dots are actually a curved line with really a  big gap setting in the hash pattern.

Demos and code

You’ll find out a lot more about how the Canvas-Flowmap-Layer was constructed by diving into the code, and checking out the comparison and main demos.


Projection – This is not a limitation, just a bit of information. This particular instance of using a single formula Bezier curve for flow mapping onto the canvas was implemented with Web Mercator projection. This cartographic method is not limited Web Mercator, provided that you are mapping a projected map and grabbing the correct screen coordinates from your start and end points.

Connection vs. route – The Canvas-Flowmap-Layer is ideal for the flow of things, where the route can be abstracted to a simple conveyance of start point connected to end point. Our data did not contain route information. When mapping thematically for things like the flow of ideas, phone calls, or even the logistical flow of things that – while they do take an actual route – can be reduced to its mere start-end connectivity. (In fact, oftentimes adding actual routes can clutter the map with chart junk). There are things to note, though, when giving precedence to connectivity over actual route. Namely, for example, when you have things from Seattle being shipped to Tokyo, but the flowline travels across Europe, Kazakhstan, and China on its way to Tokyo, when the shipment might have actually traveled across the Pacific Ocean. Jacob has cleverly built in a cool feature where the flowline updates as you pan the map so that most/all of each flowline can be viewed (at the small scales). It is totally feasible to add this sort of directional information to your data (where true direction matters). It might be pretty prudent to restrict users to your dataset’s minimum and maximum extent as much as possible. We didn’t add that restriction to our sample maps.