a real night sky at 66 degrees north

September 9, 2026 meta designpixelart

Lovund sits at roughly 66 degrees north, close enough to the Arctic Circle that summer nights barely get dark and winter ones swallow most of the afternoon. I wanted the site's dark mode to nod at that, a real sky, not a generic scatter of white dots that could be anywhere. Once I started actually researching how the sky at this latitude looks, "generic scatter of dots" stopped being good enough for me to ship.

Dusk over the fjell, the light this whole thing is trying to earn

dots first, then a sky

The first version of the night background was exactly the thing I just said I didn't want, a pixel-star sprite laid down as a uniform-ish sprinkle across the margins, dark theme only:

// first version: just scatter dots, no real positions
const stars = Array.from({ length: 40 }, () => ({
	x: Math.random() * width,
	y: Math.random() * height
}));

Fine, atmospheric, forgettable. What changed my mind was realizing that from this latitude, in winter especially, you can actually pick out the Big Dipper and Cassiopeia most clear nights, wheeling around Polaris the whole time because at 66 north they never dip below the horizon at all, they're circumpolar. That's a real, specific, findable fact about where this site is written from, and a plain star sprinkle was throwing it away for no reason. So the random scatter stayed only for filler stars, and the named constellations got actual coordinates instead.

putting real stars in the right place

I pulled the actual published right-ascension and declination values for the bright stars in both constellations, the same coordinates any planetarium app uses, and projected them the way circumpolar star charts do it: each star's angle around the pole comes from its RA in degrees, its distance from the pole comes from ninety minus its declination.

Approximate RA/Dec used (hours/minutes, degrees/minutes):
  Polaris    2h31m +89 15   Dubhe   11h03m +61 45   Merak  11h01m +56 23
  Phecda    11h53m +53 41   Megrez  12h15m +57 02   Alioth 12h54m +55 57
  Mizar     13h23m +54 55   Alkaid  13h47m +49 19

The Dipper's seven stars and Cassiopeia's five both fall out of that projection sitting almost exactly opposite each other around Polaris, which matches reality, their mean right ascensions are roughly 170 degrees apart. That also means the classic amateur-astronomy trick, follow the line from Merak through Dubhe and it points you at Polaris, comes out correct for free. I didn't have to fudge a single star's position to make that pointer line work, it's just what the real coordinates do when you project them honestly.

const DIPPER: Star[] = [
	{ x: 1479.6, y: 391.3, r: 1.8, fill: MUTED, opacity: 0.6, twinkle: '-2.1s' }, // Dubhe (pointer star)
	{ x: 1475.3, y: 380.3, r: 1.7, fill: MUTED, opacity: 0.6 }, // Merak (pointer star)
	{ x: 1490.8, y: 370.6, r: 1.6, fill: SEA, opacity: 0.55 }, // Phecda
	{ x: 1498.6, y: 377.5, r: 1.5, fill: SEA, opacity: 0.5 }, // Megrez
	{ x: 1511.2, y: 375.9, r: 1.6, fill: MUTED, opacity: 0.55 }, // Alioth
	{ x: 1521.1, y: 375.8, r: 1.5, fill: SEA, opacity: 0.5 }, // Mizar
	{ x: 1533.4, y: 366.9, r: 1.7, fill: MUTED, opacity: 0.55 } // Alkaid
];

Everything outside that named cluster stays as loose filler stars, deliberately irregular in size and spacing rather than gridded, so the two real constellations actually stand out from the background scatter around them instead of blending in as more of the same.

the field wheels, slowly

Because Polaris sits close enough to the true celestial pole that circumpolar stars visibly rotate around it over the course of a night, I let the whole cluster rotate too, one full turn every 40 minutes, pinned at Polaris's own coordinates as the transform origin. At any normal viewing timescale, someone reading a blog post for a few minutes, this reads as completely static. Leave a tab open for an hour in the background and it visibly wheels, the same way the actual sky over the fjord does if you're outside long enough to notice. I only let the real cluster rotate, not the scattered filler stars, since they don't have real positions worth preserving in the first place and most of them sit too far from Polaris for a slow rotation around that pivot to even read as motion.

a moon, and knowing when to stop adding things

There's a faint moon token too, transparent in daylight, a soft pale shape tucked into the header horizon's night sky. That's genuinely it for what's in this scene. No aurora, no shooting stars, no cloud layer drifting past yet (clouds are on the list, sitting in the same "maybe, later" pile as growing the bird pool further). I keep almost adding more and keep talking myself back down, because the actual real sky at this latitude is already doing the interesting part, my job here is closer to getting out of its way than decorating it further.

The homepage in dark mode: the real star field over the fjell

restraint that isn't optional

Every twinkle and the whole 40-minute rotation both respect prefers-reduced-motion, going fully static for anyone who has that set, same as the ambient birds and the fish everywhere else on the site. And this whole layer only exists in dark mode in the first place, day mode gets a transparent sky and no moon at all, because there's no version of a visible constellation field in full daylight that reads as anything but a mistake. Sometimes restraint just means matching what the sky actually looks like instead of finding an excuse to draw more.

why 66 north specifically changes the answer

Most software written anywhere reads as placeless by default, and most star-field decorations I'd seen elsewhere lean into that, evenly scattered points that could sit over any city on earth. That's a fair default for most things, but it throws away the one piece of this that's actually specific to where the site is written from. Somewhere around 60 degrees north, roughly Oslo's latitude, the Big Dipper still rises and sets like most constellations do everywhere further south, it dips below the horizon for part of the night or the year depending on the season. Get up to 66 and past it, and stars close enough to the celestial pole simply never set at all, they're up there wheeling around Polaris regardless of the hour. That's not a small aesthetic difference, it's the actual reason this scene gets to have a slow, endless rotation around a fixed point rather than a constellation that rises, crosses the sky, and sets like the sun does. The 40-minute spin isn't decoration layered on top of the idea, it's the idea, translated directly from an actual fact about this specific latitude into a CSS transform.

the twinkle is doing quieter work too

A handful of stars across both the real cluster and the filler scatter, Polaris included, get their own independent opacity twinkle on a slow, varied cycle, each one offset by its own delay so they never flicker in unison. It's a small touch and it's tempting to write off as pure atmosphere, but it's also doing the same job the randomized animation delays do everywhere else on this site, breaking up anything that could otherwise read as a static image with one CSS animation slapped uniformly across every element. A sky where every star pulsed in lockstep would look like a loading spinner. A sky where a few, staggered, pulse independently reads as something closer to actual starlight.

0 comments

Log in to comment.

Log in

No account?