How the sky was drawn: building 69° NORTH
1 · Concept & art direction
69° NORTH is a polar expedition company running aurora voyages out of Tromsø; the page's one job is to sell the March sailing. The art direction treats the aurora as light, never as UI chrome — green and violet exist only in the sky (and one respectful glow), while the interface stays in polar-night blues, snow white, and a single expedition orange for everything you can act on.
There are deliberately no photographs. The hero sky is a live GLSL shader — the proof of skill is that you believe it anyway. Structure encodes truth: the voyage log is a real six-day sequence, coordinates are real places, and the "honest notes" section does actual expectation-setting a real expedition outfit would do.
2 · Palette & type
Fraunces (optical sizing on, weight ~340) — an old-style revival with the slightly wonky warmth of a 1900s expedition journal. Set light and large, it reads as engraved plates, not a blog serif.
Archivo stretched to width 118 for expedition-crate labels, plus IBM Plex Mono for coordinates, soundings and KP readings — instrumentation, used as ornament.
3 · Techniques — the signature sky
The hero is one fragment shader on a raw WebGL1 context — no three.js, no libraries. Curtains use the classic layered-plane raymarch (in the lineage of nimitz's "Auroras"), retuned and composed into a full scene: the ray marches through 30 stacked altitude planes, samples a rotating triangle-wave fbm at each, and accumulates colour along a green→violet spectral ramp:
for (float i = 0.; i < 30.; i++) {
float pt = ((.85 + pow(i,1.4)*.002) - ro.y) / (rd.y*2. + .4);
vec2 p = (ro + pt*rd).zx + vec2(uTime*.018, 0.);
float d = curtain(p, .055); // triangle-noise fbm, rotated per octave
vec3 c = (sin(1. - vec3(2.15,-.5,1.2) - uScroll*.5 + i*.043)*.5+.5) * d;
avg = mix(avg, vec4(c,d), .5); // vertical smear = curtain rays
col += avg * exp2(-i*.065 - 2.5);
}
Around it, in the same shader: hash-grid stars with per-star twinkle, two ridged-fbm mountain silhouettes (the far ridge picks up 5% of the aurora's colour, like lit snow), and a fjord reflection — the scene function re-sampled with a mirrored ray, noise-rippled x, darkened and depth-faded. Scroll feeds a uniform that deepens the curtains toward violet; the pointer leans the camera a few degrees. prefers-reduced-motion renders one calm frame and stops the loop.
The second device is the nautical chart: a hand-drawn SVG of the actual coast (Kvaløya, Lyngen, Kvænangen, Lopphavet) with graticule, soundings and a title block. The route is a single path whose stroke-dashoffset is driven by the log's scroll progress; a vessel glyph rides getPointAtLength() and rotates to the path tangent, and each day's marker ignites as the reader reaches it.
const L = route.getTotalLength();
route.style.strokeDashoffset = L * (1 - progress); // never un-draws
const pt = route.getPointAtLength(L * progress);
vessel.setAttribute('transform',
`translate(${pt.x},${pt.y}) rotate(${angleToTangent})`);
4 · Three iteration passes
- Pass 1 — correctness & compositionAdded a failsafe so revealed sections can never stay hidden; fixed a CSS-specificity bug that washed out the nav CTA's text; added legibility scrims over the shader's brightest band; softened the mainland coastline and moved two chart labels off the route; stopped the wordmark wrapping at 390px.
- Pass 2 — elevateOrchestrated the hero load (coordinates → headline → CTAs, staggered); refined the shader with finer curtain filaments at lower gain; gave each cabin card a ship-profile diagram marking its position aboard; turned the chart caption into a live vessel-position readout (lat/lon computed from the route point); added a sheen micro-interaction to CTAs.
- Pass 3 — tasteChanel rule: deleted a decorative aurora glow from the booking panel — the art direction says aurora is light in the sky, never UI chrome; deduplicated the footer's shader line; final label nudge on the chart; re-verified 390px and the reduced-motion paths (still sky, fully-drawn route, no pulses).
5 · Do this yourself
- Give Claude a one-line brand and a one-line job. "A polar expedition company; make people book the March sailing." Everything else should be derivable from those two lines.
- Assign one signature element and protect it. Here: a live aurora shader. Say explicitly that everything else must stay disciplined so the signature reads.
- Ban the defaults. Name the clichés you don't want (starfield heroes, purple SaaS gradients, three-card grids) so the model can't fall back on them.
- Ask for structure that encodes truth. A six-day voyage is a real sequence, so day numbers earn their place; coordinates and soundings are real, so they can be ornament.
- Demand the shader be composed, not pasted — stars, ridgelines and a reflection in one scene function, with scroll and pointer as uniforms, and a reduced-motion still frame.
- Iterate with screenshots, three times. Correctness, then elevation, then taste — and actually remove something in the last pass.
- Write copy like a brand, not a template. The honest-notes section ("The aurora is weather") does more selling than any adjective.
BUILT BY CLAUDE FABLE 5 · SITE 25 OF 25 · THE SKY IS CODE — THE REAL ONE IS BETTER