20 · Hollowback 50K: a trail race you fly through on a real mountain
Hollowback 50K is a race website for a made-up 31 mile trail race on the real terrain of Mount Greylock, Massachusetts. The race, its stations, cutoffs, prices and waves are fictional, and the footer says so. The mountain, the trails and the towns are real. What makes people stop: one 3D terrain map sits behind the whole top of the page, and as you scroll the red course line fills in, the camera follows a runner dot along it, and an elevation strip at the bottom lets you drag the runner anywhere on the course.
Inspiration
This is the batch three approach: a tested kit plus a theme, with the page itself written by hand. The map comes from the lab tools MapLibre and OpenFreeMap, wrapped by the kit part maplibre-file.js (terrain, a growing route line, and a restyle function that recolors the street map to the brand). The hand-drawn accents in the aid section are four Kitbitz drawings (Tent, Backpack, Lantern, Bonfire). GSAP with SplitText, ScrollTrigger and DrawSVG does the intro, card reveals, bib animation and the drawing-in of the art. The glass legend uses the kit's SVG-filter glass.
Added on top, written for this page: contour lines computed in the browser from a terrain grid with marching squares (OpenFreeMap has no contours), a camera that blends from a slow hero orbit into a route-following camera, a pace calculator that sets arrival times against cutoffs, and a bib generator that exports a PNG.
Visual direction
- Palette: dusk
#141a26, topo tan#c8b48a, trail red#e5484d, snow#f4f1ea. Map layers are pushed to dark blues and greys so the red route and tan contours carry the picture. The aid section flips to snow with faint ink contours behind it. - Fonts: Big Shoulders Display (tall, tight, 900 weight) for the title, big numbers and bib; Inter Tight for body text and labels.
- Motion: the hero title splits into characters and rises through a mask. The map orbits slowly at about 3 degrees a second. After the hero, the camera drops to a 64 degree pitch and follows the runner, easing toward each target so scrolling feels weighted. Mile markers appear as you pass them and aid stations light up red.
- Layout: the map is one sticky 100vh stage. Content scrolls over it: the hero with a stats panel, seven chapter cards (one per section of the course), then a snow-colored aid section and a dusk entry section. Cards are solid, never blurred.
- Signature moments: the orbiting hero map; the route filling as you scroll; the draggable elevation profile with grade readout; the pace slider turning table rows green, amber or red; the tilted bib that drops in with a number roll.
Theme, kit and licenses
Theme: Map, at the Loud end of its budget. Runners and entrants need to see the course and its climbs first, so the map is the whole show and every other element serves it. One line of the theme recipe changed: contours are drawn from the builder's own terrain grid because the tile source has none.
Kit parts used: maplibre-file.js (map, terrain, route, restyle), kitbitz.js (the four drawings, recolored to the palette), glass.js and glass.css (one legend panel, not sticky), fps-chip.js (the ?fps readout). No new libraries.
| Source | License |
|---|---|
| MapLibre GL JS 6.15.0 | BSD-3-Clause |
| OpenFreeMap tiles and "liberty" style | Free, attribution required (shown) |
| OpenStreetMap data | ODbL, attribution shown |
| Mapzen terrain tiles on AWS | Open data, credited in the map attribution |
| FOSSGIS foot router (OSRM) | Public service on OSM data; used once while building, nothing called at runtime |
| Kitbitz drawings | CC0, credited anyway |
| GSAP 3.15.0 and plugins | GSAP standard no-charge license |
| Big Shoulders Display, Inter Tight | SIL Open Font License |
| Glass, map and chip kit parts | Our own code |
References
- MapLibre GL (https://maplibre.org): the 3D terrain map and camera.
- OpenFreeMap (https://openfreemap.org): street tiles and base style, recolored in code.
- Kitbitz (https://kitbitz.art): Tent, Backpack, Lantern, Bonfire, drawn in on scroll.
- GSAP (https://gsap.com): SplitText intro, ScrollTrigger reveals, DrawSVG.
- SVG-filter glass (lab 05): the one legend panel.
Why these choices
A race site has two jobs: show what the course is like, and get people to enter. The terrain map does the first better than any photo, and the pace check and entry form do the second. The pace check is the practical hook: it answers "can I make the cutoffs?", which is the real question every entrant has.
Trade-offs worth knowing. The live map depends on a free tile service with no uptime promise, so a drawn topo map (same contours, route, aid markers and runner dot) shows first and takes over if WebGL or tiles fail. Glass is used once, in the hero, because it costs frames over moving content; the HUD and profile are solid for the same reason. With reduced motion the page does not fly: the camera shows the whole course and scrolling still reads progress. The cards sit left so the runner stays clear of text, and on phones the camera padding moves the runner above the cards.
Who this kind of site is for
It wins for trail and road races, cycling events, guided tour companies, ski areas, hiking guides and anything sold by "where it goes". It is wrong for businesses with no route or place to show, and for pages that must load fast on weak phones, since it pulls a map library and terrain tiles. To pitch it: "Your course, as a flyover, with a calculator that sells the race." A real client would swap the traced line for their own GPX file and keep everything else. The pace-and-wave logic reuses for any timed event with checkpoints.
How to make it
Stack and key techniques
- One HTML file, about 1,000 lines (roughly 260 CSS, 200 HTML, 510 JS) plus a 45 KB course data block (route line, elevation profile, station positions, terrain grid).
- MapLibre with terrain and hillshade; route line with a progress setter; mile markers as a filtered layer.
- Marching squares plus segment joining and Chaikin smoothing for contours, drawn on the live map, the fallback SVG and behind the aid section.
- A scroll-to-mile mapper, a smoothed camera loop that runs only while something moves, a draggable and keyboard-operable elevation slider.
- An effort model (distance plus climb and descent weights, with a slowdown in the second half) for arrival times.
The prompt
Build a single-file website (index.html, no build step) for a FICTIONAL 31 mile trail race, "The Hollowback 50K", run on the REAL terrain of Mount Greylock, Massachusetts. Say plainly in the footer that the race, stations, cutoffs, prices and waves are made up and the mountain is real.
Theme: "Map, Loud". The map is the whole show. Palette: dusk #141a26, topo tan #c8b48a, trail red #e5484d, snow #f4f1ea. Fonts: Big Shoulders Display (headlines, big numbers) and Inter Tight (body).
Structure: (1) A hero over one sticky full-screen 3D map (MapLibre with OpenFreeMap, restyled dark, terrain and hillshade) that orbits slowly. Include one glass stats panel: 30.7 miles, 7,800 ft, 6 aid stations, 14 hour limit. (2) A fly-through: seven solid (never blurred) chapter cards, one per section of the course. As the user scrolls, the camera drops in and follows a runner dot along the route; the red route fills as they go, mile markers appear as they pass, aid stations light up. A solid HUD shows mile, feet and trail name. A sticky elevation profile at the bottom shows the runner; hover, drag or arrow keys move the runner and fly the camera. Add a camera toggle (follow / overview). (3) An aid-station section on snow-colored paper with faint contour lines behind it: a table of six stations plus finish with cutoffs, and a pace check (goal-time slider, start wave chips) that marks each arrival OK, tight or missed, with a timeline graphic. (4) An entry form with real validation that shows a tilted race bib with a generated number and lets the user save it as a 1400x980 PNG.
Data: trace the course once along real OSM foot paths, measure it against open terrain tiles, and store the line, elevation profile and a coarse terrain grid in an inline JSON block. Draw contour lines in the browser from that grid (marching squares) and add them to the map, because OpenFreeMap has none.
Motion: GSAP with SplitText, ScrollTrigger and DrawSVG; split-character title intro; Kitbitz (CC0) drawings in the aid section, recolored to the palette and drawn in.
Rules: show a drawn SVG topo map first and as a full fallback if WebGL or tiles fail, with the same moving runner. Honor prefers-reduced-motion (no orbit, show the whole course). No blur on sticky elements. Keyboard operable, visible focus, no horizontal scroll at 390 or 1440 px. No em dashes. Credit all sources with licenses.
Verify: screenshot at several scroll stops on desktop and mobile, with and without reduced motion; click through the pace check and the form; test the fallback with the map disabled; check the console; fix and repeat.
Build notes
- The course data was prepared offline once; nothing is routed at runtime. The measured result (30.7 miles, 7,784 ft) is shown as 7,800 ft.
- MapLibre writes opacity on marker elements, so the runner dot lives inside its marker wrapper.
- The camera loop only runs while the page is visible and something is still moving, and skips redundant camera moves.
- The kit's Kitbitz credit helper leaves "%20" in file names; this page decodes them first.
- Builder-flagged and untested: real-device frame rate (
?fpsexists for it), and the live map took 16 to 90 seconds to appear in headless Chrome while nine other builds ran. The drawn map covers that wait. The fallback check failed once in a long test run, then passed alone. The thumbnail was shot with reduced motion.
Stepping down: what you would likely lose
This is an informed estimate, not a measurement. No public benchmark compares these models on one-shot frontend work, and the kit helps more than model tier does here.
| Feature | Sonnet 5.5 with this kit | Sonnet 5.5 from a blank page |
|---|---|---|
| Live 3D map, terrain, restyle | Does fine: the kit part already handles the file-open worker problem and styling | Attempts it with likely bugs: a blank map from a double-clicked file, or a muddy default style |
| Route fill and mile markers | Does fine: the kit's route helper has a progress setter | Likely bugs in line gradients or progress animation; may swap in a plain line |
| Contours from the terrain grid | Attempts it with likely bugs: stitched lines with gaps or jagged edges (the joining step is the hard part). Data prep is separate work | Probably simplifies away, or uses a flat background image |
| Follow camera blending from the orbit | Attempts it with likely bugs: jitter, a spin the long way round when bearing wraps at 360, and the runner hiding behind cards | Probably a simple jump to a fixed zoom |
| Draggable profile with keyboard and readout | Does fine in the main case; touch capture and blur cleanup are the weak spots | Attempts it with likely bugs: a mouse-only drag |
| Pace check and bib PNG | Does fine: this is ordinary logic, though the second-half slowdown model may be simpler | Does fine, probably with a plainer effort model |
| Drawn fallback map | Attempts it with likely bugs: contours not matching the live map | Probably simplifies away |
Rough overall estimate, a judgment call: with the kit and the same prompt, perhaps 70 to 85 percent of the visual impact in one pass; from a blank page, perhaps 40 to 60 percent, mostly lost in the map camera and contours. What would close the gap: a screenshot loop at several scroll stops, and splitting the build so the course data and contour code are done and checked before the page is assembled.