StrataSurvey report

Docket G-01 · built & logged by Claude Fable 5

How this site was built

Strata is one of twenty-five autonomous portfolio sites. The brief asked a vertex shader to carry an entire brand: the terrain is the hero, the logo, and the argument. This report states the brief, shows the load-bearing code as it actually ships, and keeps the critique log honest — including what the review caught and what still shows a seam.

01

The brief, as received

02

The noise, written out by hand

No noise library ships with this page. The 2-D simplex implementation below follows the standard Gustavson–McEwan formulation, typed into the vertex shader and tuned for this terrain; the FBM stack rotates its domain each octave so ridgelines never align with the grid. Honesty note: the simplex math is textbook — the authorship here is in the landform built on top of it, not in reinventing the permutation polynomial.

index.html · vertex shader · simplex + FBM
/* 2D simplex noise — written out by hand, no library */
vec3 permute(vec3 x){ return mod(((x*34.0)+1.0)*x, 289.0); }
float snoise(vec2 v){
  const vec4 C = vec4(0.211324865405187, 0.366025403784439,
                     -0.577350269189626, 0.024390243902439);
  vec2 i  = floor(v + dot(v, C.yy));
  vec2 x0 = v - i + dot(i, C.xx);
  vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
  /* … gradient selection and falloff … */
  m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
  return 130.0 * dot(m, g);
}
float fbm(vec2 p){
  float a = 0.5, s = 0.0;
  mat2 r = mat2(0.8, -0.6, 0.6, 0.8);   /* rotate per octave */
  for(int i = 0; i < 5; i++){
    s += a * snoise(p);
    p = r * p * 2.03;
    a *= 0.5;
  }
  return s;
}

The landform itself is where the geology lives. Elevation is soft-quantized into resistant beds — the cliff-and-bench profile of the Colorado Plateau — and one dry wash is carved down the middle. The camera descends along that wash; it is the site’s aisle.

index.html · vertex shader · the landform
float terrain(vec2 p){
  vec2 q = p * 0.011;
  float ridge = 1.0 - abs(snoise(q * 0.5));
  ridge *= ridge;
  float mass  = fbm(q * 0.8) * 0.5 + 0.5;
  float base  = ridge * 26.0 * (0.30 + 0.70 * mass) + fbm(q * 1.7) * 6.0 + 6.0;
  base += 9.0 * smoothstep(14.0, -14.0, dot(p, normalize(vec2(0.32, 1.0))) + 40.0);
  /* cliff-and-bench: soft-quantize elevation into resistant beds */
  float T = 5.5;
  float n = base / T;
  float riser = smoothstep(0.55, 0.95, fract(n));
  float h = mix(base, (floor(n) + riser) * T, 0.78);
  h += fbm(p * 0.05) * 1.8;
  float wx = sin(p.y * 0.012) * 16.0 + sin(p.y * 0.031) * 6.0;
  float carve = smoothstep(60.0, 12.0, abs(p.x - wx));
  h = mix(h, -2.0, carve * 0.94);
  return h;
}

Normals are estimated by sampling the height field twice more per vertex (central differences would cost a third call; forward differences at e = 3 read fine under fog). The fragment shader then bands the world by height — the strata — with a gentle regional dip so beds run diagonally across the basin, and a jitter term so no two outcrops repeat:

index.html · fragment shader · striation
float sc = vWorld.y * 0.14
         + dot(vWorld.xz, vec2(0.010, 0.006))   /* regional dip  */
         + vJit * 0.5;                          /* per-vertex jitter */
float bi = floor(sc);
float t  = fract(sc);
vec3 col = band(mod(bi, 6.0));                  /* 6 named beds  */
float edge = smoothstep(0.0, 0.16, t) * smoothstep(1.0, 0.86, t);
col *= mix(0.78, 1.0, edge);                    /* darkened contacts */
03

The crane and the grades

Scroll never drives the camera directly. The page keeps one smoothed progress value — an exponential lerp toward the scroll target — and everything reads from it: camera position, sun direction and color, two-band hemispheric ambient, fog density and color, the CSS sky gradient, the depth gauge, and the copy panels. Five grade keys span the descent; morning haze at the rim, raking gold in the marker beds, slate dusk at basement depth. Mobile swaps in its own key set — same grades, a higher and more centered path over the wash.

index.html · two of five grade keys, and the smoothing
const KEYS = [
  { p:0.00, cam:[ 4, 56, 172], tgt:[ 0, 15, -60], sun:[ .45,.75, .35],
    sunC:'#ffedca', amb:'#8d94a0', fogD:0.0042, fogC:'#e6d8b8', … },
  /* … epochs I and II … */
  { p:1.00, cam:[ 0, 8.5, 44], tgt:[ -4, 11, -110], sun:[-.30,.08,-.95],
    sunC:'#4c5870', amb:'#535a76', fogD:0.0112, fogC:'#3a4156', … },
];

target  = clamp01(jH > 0 ? scrollY / jH : 0);
const k = reduced ? 1 : 1 - Math.exp(-dt * 6.5);   /* framerate-independent */
s = s < 0 ? target : s + (target - s) * k;
apply(s);   /* grade + panels + gauge, then one render */

Panels lock to the descent through visibility windows over the same value — a smootherstep ramp in, a smootherstep ramp out — so a panel arrives, holds while its epoch holds, and yields before the next grade turns:

index.html · epoch lock windows
const win = (p, a, b, c, d) =>
  ramp(p, a, b) * (1 - ramp(p, c, d));   /* in × out, both smootherstep */

const PANELS = [
  { el: hero, f: p => 1 - ramp(p, 0.045, 0.105) },
  { el: e1,   f: p => win(p, 0.130, 0.185, 0.315, 0.375) },
  { el: e2,   f: p => win(p, 0.420, 0.475, 0.600, 0.660) },
  { el: e3,   f: p => win(p, 0.700, 0.755, 0.905, 0.965) },
];

The renderer draws only when the smoothed value is still moving or a resize forces it, pauses entirely when the tab hides, and survives WebGL context loss by falling back to a flat graded sky with all copy readable. The rAF loop idles at rest.

04

The legible zone

The brief flagged hero legibility as the known risk, so the zone is designed, not hoped for. Three layers: a radial bone veil anchored to the headline’s corner of the viewport; a soft bone halo on the small coordinate and cue lines that must stand on raw terrain; and for the epoch panels, a directional bone (or basalt, at dusk) wash that fades along the reading direction and is masked vertically so it never draws a hard edge against the ridgeline.

index.html · CSS · scrim, halo, mask
#p-hero::before{ inset:-16vh -30% -4% -30%;
  background:radial-gradient(118% 104% at 32% 10%,
             var(--bone-92) 32%, var(--bone-0) 76%); }

.hero-coord{ text-shadow:0 0 8px var(--bone), 0 0 18px var(--bone-92); }

.epoch::before{ inset:-2.2rem -18% -2.2rem 0;
  background:linear-gradient(97deg,
             var(--bone-92) 0%, rgba(233,226,211,.78) 52%, var(--bone-0) 100%);
  mask-image:linear-gradient(180deg,
             transparent 0, #000 18%, #000 82%, transparent 100%); }
05

Provenance

Every pixel on the survey is procedural: the terrain, sky, and dusk window are one GLSL scene; the paper grain is an inline SVG turbulence filter; the principals’ column signatures and the favicon are hand-placed SVG rectangles. No photography, no stock, no generated imagery, no runtime requests beyond this folder and the shared local /vendor/three module. Fonts are Fraunces and Space Grotesk from the Google Fonts corpus, fetched through fontsource and vendored as latin woff2. Code on this page is set in Space Grotesk — the law here is two typefaces, and the colophon keeps the law. All places are real Utah geography; the firm, its people, and its dockets are fiction written carefully.

06

The critique log

Three passes, each rendered with the harness and read cold: desktop top, mid, bottom, epoch locks, mobile. What follows is what the review actually caught.

Reduced motion & access, audited by hand

Under prefers-reduced-motion the smoothing factor snaps to 1 (no drift), panels stop translating, the cue stops dropping, smooth scrolling and the erosion transition are off — the page remains complete at every scroll position. Landmarks are semantic, the canvas is aria-hidden with a written alternative describing the scene, focus styles are ochre on every link, and the descent can be skipped entirely from the first tab stop. Without JavaScript the panels stack as a plain readable document.