a real night sky

September 5, 2026 meta devlogpixelart

the mountain goes soft at night

Been sitting on this one for a bit. Wanted a night version of the header scene, and the pixel mountain that works fine in daylight looked wrong once everything went dark, too blocky against a sky that's supposed to feel deep and far away.

So the background mountain gets swapped for a smoothed version at night, traced over from the same ferry photo I used for the pixel one but drawn as a soft filled shape instead of pixels. Everything closer to the water, the birds, the fish, the little runner, all stay pixel. Just the far background softens up. Gives it some depth, near stuff sharp and blocky, far stuff blurred into the dark like it actually would be.

actual constellations, not random dots

Could've just scattered random stars and called it done, actually started that way for about an hour:

const stars = Array.from({ length: 40 }, () => ({
	x: Math.random() * width,
	y: Math.random() * height
}));

But I wanted the sky to actually be a sky. Looked up where Cassiopeia and the Big Dipper (Karlsvogna, growing up) sit relative to the North Star and laid out both constellations as actual point patterns instead of random noise. They rotate slowly around the North Star over time same as the real sky does, just sped up a lot so you can actually notice it happening during a page load instead of over a full night.

Small detail nobody's required to notice, but I know it's there and that's enough for me.

remembering where it was

Last thing I added: the scene now remembers roughly where everything was if you come back within five minutes. Bird positions, star rotation, all of it, dropped into local storage with a timestamp:

export const SCENE_MAX_AGE_MS = 5 * 60 * 1000;

export function loadScene<S>(
	storage: StorageLike | null,
	key: string,
	now: number = Date.now(),
	maxAgeMs: number = SCENE_MAX_AGE_MS
): StoredScene<S> | null {
	if (!storage) return null;
	try {
		const raw = storage.getItem(key);
		if (!raw) return null;
		const parsed = JSON.parse(raw);
		if (!parsed || parsed.v !== 1 || typeof parsed.savedAt !== 'number') return null;
		const age = now - parsed.savedAt;
		if (age < 0 || age >= maxAgeMs) return null;
		return parsed as StoredScene<S>;
	} catch {
		return null;
	}
}

Wander off to read a post and come back to the homepage and it doesn't just reset to a fresh random scene, it picks up close to where it left off. After five minutes it lets go and reshuffles.

That's the redesign basically done for now. Moss green paper, a mountain that actually looks like the mountain, way too many birds, a sidebar, and a sky that knows where the Big Dipper actually is. Probably going to keep poking at small things but this is the shape of it.

0 comments

Log in to comment.

Log in

No account?