Long-Exposure Art

A photo of my night sky art on my table. There's also a toy dog beside it for scale.

I’ve been fascinated with long-exposure shots of the night sky recently (like this one), so I thought it would be fun to emulate that effect on <canvas>. I assumed that it would take me a while to figure it out, but the algorithm turned out to be surprisingly simple!

The first thing I noticed was that each “star trail” is basically a small segment of a circle, so I figured that I’d be using the arc() method a lot. After staring at different long-exposure photos, I also noticed that the star trails grow in length as it moves farther away from the center of the spiral. This told me that the angle that I’ll use for the arc will probably be constant throughout the artwork. The only thing that needed to change was the radius of that arc.

But how do I draw multiple arcs across the canvas? My first solution was to generate random x-y coordinates all over the page and then get the distance between (0, 0) and (x, y) using Math.hypot(). I then created an arc for each point using the distance as its radius and then spun it around to a random angle using rotate(). It looked something like this in CoffeeScript:

# For each point:
# 1. Find the distance between (0,0) and (x,y)
# 2. Create an arc with constant theta
# 3. Rotate it to a random angle (Math.random() * 2π)
drawStarTrails = (x, y) ->
    context.arc 0, 0, Math.hypot(x, y), 0, theta
    context.rotate Math.random() * 2 * Math.PI

# Draw random x-y coordinates
drawStarTrails(Math.random() * width, Math.random() * height) for n in [1..1000]

But then I realized that I didn’t need to find the distance between (0, 0) and a random point at all. I could just plug in a random radius and I’d get the same effect:

# For each iteration:
# 1. Find a random radius.
# 2. Create an arc with constant theta
# 2. Rotate it to a random angle (Math.random() * 2π)
drawStarTrails = () ->
    context.arc 0, 0, Math.random() * width, 0, theta
    context.rotate Math.random() * 2 * Math.PI

# Create the star trails
drawStarTrails() for n in [1..1000]

And that was it! Now it took me a couple of hours fiddling with different colors, gradients, angles, stroke widths, and the number of arcs to get it to look right (the final version has 9000 arcs!), but this project reminded me how simple rules could generate something that looks complex. It’s honestly one of my favorite things about generative art.

You can find the complete source code on Sourcehut. You can also get prints in digital and physical formats (if it’s in stock) here 🖼.

This is the final version of the artwork. You can find the source code here if you’d like to read the whole program or make modifications yourself.

Leave a Reply

Your email address will not be published. Required fields are marked *