Fiddling with JavaScript Astronomy: ThreeWorlds (Shallow Thoughts)

Akkana's Musings on Open Source Computing and Technology, Science, and Nature.

Sun, 06 Jun 2021

Fiddling with JavaScript Astronomy: ThreeWorlds

[analemma webapp] I have another PEEC Planetarium talk coming up in a few weeks, a talk on the summer solstice co-presenting with Chick Keller on Fri, Jun 18 at 7pm MDT.

I'm letting Chick do most of the talking about archaeoastronomy since he knows a lot more about it than I do, while I'll be talking about the celestial dynamics -- what is a solstice, what is the sun doing in our sky and why would you care, and some weirdnesses relating to sunrise and sunset times and the length of the day. And of course I'll be talking about the analemma, because just try to stop me talking about analemmas whenever the topic of the sun's motion comes up.

But besides the analemma, I need a lot of graphics of the earth showing the terminator, the dividing line between day and night.

This month we're still giving "planetarium" talks via Zoom. (Next month, talks will alternate between Zoom and the physical planetarium, though personally, I'm not sure how comfortable I feel about giving talks in a closed room, even fully vaccinated.)

Anyway, for the section on day length and sunrise/sunset times, I wanted a good visualization of the Earth's terminator at specific times. I want to mention the phenomenon discussed in Bruce McClure's EarthSky article during the last winter solstice, Solstice tale of two cities. I found that article fascinating but hard to visualize, because the two images, sunrise and sunset, seem so different. I wanted something interactive that I could show people, and that I could animate to show how the shadow moves.

Python is my go-to language for astronomy calculations, because it has great libraries like XEphem and its successor, Skyfield. Alas, what Python doesn't have is libraries to draw a globe in 3-D and let you drag it around in real time using OpenGL while projecting shadows properly.

What language does have that? JavaScript. There's a library called Three.js that's very good at things like projecting a globe and letting you drag it around using WebGL. I've used it once before, for MarsMap, and it's pretty amazing. JavaScript isn't my favorite language by a long shot, but sometimes you have to be flexible.

Three.js Directional Light

Three.js lets you specify a Directional Light, and if you enable the DirectionalLightShadow then you just have to calculate the direction of the sun and put your light there.

That works pretty nicely, except that the shadow is extremely fuzzy, as though from a light that's both large and close, not the way the Earth's actual terminator looks. I haven't found any way to make the shadow sharper. light.position.set() lets you specify a vector, but making the vector longer so the light is farther away doesn't affect the fuzziness the way it would with a real light.

You can view it here: ThreeWorlds Directional Light model.

Overlaying Two Layers on a Globe

One thing I wanted out of my model was the ability to show the artificial lights on the dark side of the globe, like the Fourmilab Earth Viewer does. It was easy to write a Python script to draw a rectangular map that combined day and night images, making the terminator as fuzzy as I wanted -- -- and then wrap that around a Three.js globe. But to make the terminator move as time passes would have required generating an image for every time step in Python and re-wrapping the globe each time, which seemed awfully inefficient.

If I could get Three.js to have both a day and a night image, and specify where the terminator divides one from the other, that seemed like a much better idea.

Three.js Multiple Textures and Clipping Planes

It's easy to define a clipping plane to indicate the boundary between day and night. But getting the day and night images onto the same sphere was harder. I couldn't find any documentation explaining how to load more than one texture.

Part of the problem is that all the examples set up a loader for the texture image, and then initialize the globe in a callback when the texture finishes loading. That's great if you only have one texture, but it gets a lot more complicated if there are more than one.

Eventually I found an example showing several layers on an Earth globe: Globe.js gist by marcopompili. I later found a blog tutorial doing almost the same thing: Creating a WebGL Earth with three.js and this StackOverflow thread also had some useful comments.

I was able to adapt Globe.js by putting my night image in the cloud layer. The result is ThreeWorlds: daynightglobe model. I'll probably use that one for the talk even though I wasn't able to blur the shadow edge.

All of these are on my ThreeWorlds GitHub repository and can be viewed live on ThreeWorlds on

Oh, and did it help me visualize that changing sunrise and sunset shadow angle? Well, no, not really. I'm hoping that by staring at it and fiddling with animations between now and June 18, I'll come up with a better way to explain this.

Tilting Earth's Axis and Rotating

Explaining the changing sunrise/sunset angles might require a wider view where Earth's axis is tilted 23 degrees. Since the models I was working with mostly show Earth from above a given latitude and longitude, it didn't really make sense to tilt it, and, I confess, the fact that doing so probably requires figuring out quaternions made me want to put it off.

Anyway, if I do some day decide I need a tilted globe, there are a couple of StackOverflow discussions with useful examples (one, two).


Of course I'll be talking about the analemma too. So as long as I was fiddling with JavaScript, I found a mini JS sun position library and built an Analemma JavaScript web app around it. It takes lots of different URL parameters, like latitude, background image, and time of day, and can let you step through the analemma, which will be handy when giving talks.

Analemma viewer, and the analemma source code on GitHub.

Tags: , , , ,
[ 18:33 Jun 06, 2021    More science/astro | permalink to this entry | ]

Comments via Disqus:

blog comments powered by Disqus