← Northbound THE GUIDE · how it was built
Design showcase · build notes

Drawing a road across a mountain, in a browser.

Northbound looks like a native EV trip planner, but it's one HTML file, one stylesheet and one module — no framework, no build step. The whole identity rests on one moment: a route line drawing itself across a 3D topographic map.

The idea

Every EV routing app shows you the same thing: a flat 2D map with pins. Northbound's bet is that the thing you're actually anxious about isn't the map — it's the landscape. The climb that eats your range. The gap between chargers over a pass. So we made the terrain the hero, gave it real elevation, and let the route draw itself across it while a plain-English planner answers the only question that matters: what battery will I arrive with?

The interaction layer borrows two patterns from shipped apps (Rivian, Tesla, Google Maps, My BMW): a route-summary header — "512 km · 2 stops · arrive 6:40pm" — and a vertical charge-stop timeline with drive segments between stops. On top of those we invented a curation layer the real apps don't have: good coffee, a short trail, a fast plug.

The stack

Signature technique — the topographic terrain + self-drawing route

The terrain is a PlaneGeometry displaced by fractal value-noise, plus two Gaussian bumps so there are real ridges for the route to weave between. Each vertex carries a normalised height so a shader can paint elevation contour lines — the topographic look — straight in the fragment stage:

// fragment — contour bands from normalised height vH
float f = vH * 15.0;
float w = fwidth(f);                      // screen-space derivative
float line = abs(fract(f - 0.5) - 0.5) / max(w, 1e-4);
float contour = 1.0 - clamp(line, 0.0, 1.0);
vec3 col = mix(base, uContour, contour * 0.8);  // earth base, sage lines
col = mix(col, uBg, vFog);                 // fade into the forest ink

The route is a CatmullRomCurve3 whose control points sample the same height function, so the line hugs the surface. It's a TubeGeometry, and it draws itself by animating the geometry's draw range from zero to full — no shaders, no line libraries:

const dp = clamp((now - start - 500) / 2600, 0, 1);
const draw = 1 - Math.pow(1 - dp, 3);      // ease-out
tubeGeo.setDrawRange(0, Math.floor(tubeCount * draw));
// charging nodes switch on as the line passes their curve position
const on = draw >= node.u - 0.02;

Scrolling advances the camera along the corridor (the eye and look-at targets interpolate down the valley), with a slow idle drift when you're parked at the top. A second, fatter additive tube gives the line its electric-green glow.

Details that matter

Ship it on GitHub Pages

Every path is relative and there's no build, so deploying is three commands:

gh repo create bswxyz/northbound-ev --public --source . --push
gh api --method POST /repos/bswxyz/northbound-ev/pages \
  -f 'source[branch]=main' -f 'source[path]=/'
# live at https://bswxyz.github.io/northbound-ev/ in ~2 min

A .nojekyll file keeps Pages from touching the folder structure. The three.js import map resolves from jsDelivr at runtime, so there's nothing to bundle.


Northbound is a design-showcase concept — the routing, charge curves and charger data are modelled for the demo, not wired to a live network. See the repository README for the full demo-vs-real map.

← Back to Northbound