1 · The idea: the water is the interface
The brief was a booking site where a turquoise, see-through Caribbean sea sits behind everything — including the live Guesty calendar. So the home page is built as a water column: a sunlit sand seafloor at the very back, a real-time WebGL water surface in front of it, and the content floating between them in panels of glass. Scrolling doesn’t move past the ocean; it descends through it, until the page finally washes ashore onto sand.
2 · A transparent Gerstner ocean
The sea is a Three.js ShaderMaterial on a 340×340 plane. Five Gerstner waves are composited in the vertex shader — the classic trochoidal wave model, where each point moves in a circle, so crests sharpen the way real water does:
void gerstner(vec2 dir, float steep, float len, vec3 p0, …){
float k = 6.28318 / len;
float c = sqrt(9.8 / k); // deep-water wave speed
float f = k * (dot(d, p0.xz) - c * uTime);
float a = steep / k;
disp += vec3(d.x * a * cos(f), a * sin(f), d.y * a * cos(f));
…
}
The trick that makes it Mexican-Caribbean and transparent is all in the fragment shader. Color mixes from a deep petrol teal into bright turquoise by crest height, then toward a pale-aqua sky by fresnel. Alpha is lowest looking straight down into troughs — so the seafloor glows through the clear water — and rises at grazing angles, on crests, and with foam:
vec3 col = mix(uDeep, uShal, crest); // #0b7c96 → #3fd6d9
col = mix(col, uSky, fresnel * 0.6); // horizon goes pale
float alpha = 0.34 + fresnel * 0.5
+ crest * 0.18 + foam * 0.5; // clear in the troughs
gl_FragColor = vec4(col, alpha);
The renderer is created with alpha: true and clears to nothing, so whatever the DOM puts behind the canvas — here, an AI-generated sunlit seafloor with caustic light webs — reads through the surface. Scroll position feeds a uDepth uniform that darkens the water and lowers the camera: the descent.
3 · The calendar beneath the surface
The Guesty search widget and full booking calendar sit in a glass panel (backdrop-filter: blur(20px) saturate(1.5)) over the living water. Both are lazy: an IntersectionObserver injects Guesty’s script only when the panel approaches the viewport, and the calendar iframe gets its src the same way. If either fails, a plain link to the booking page remains — availability and pricing always belong to Guesty, never to this site.
new IntersectionObserver((entries) => {
if (entries.some((e) => e.isIntersecting)) loadGuesty();
}, { rootMargin: "600px 0px" }).observe(panel);
4 · Palette & type
Eight tokens run the whole site — a water column from abyss to foam, warm sand for the shore, and one coral accent used sparingly:
- Abyss
#032f3d - Deep
#0b7c96 - Turquoise
#1fc4cc - Aqua
#a9f0e9 - Foam
#f2fffb - Sand
#f6eedd - Ink
#06323c - Coral
#ff6a52
Display type is Fraunces, self-hosted as a variable font with its WONK axis switched on — the slightly drunken letterforms give the serifs a hand-set, salt-warped character. UI text is Manrope. Both load as single variable woff2 files.
h1 { font-family: "Fraunces";
font-variation-settings: "SOFT" 0, "WONK" 1; }
5 · AI-generated textures (and a hard rule)
Three decorative images were generated with the OpenAI Images API: the sunlit seafloor sand, an open-sea horizon (the no-WebGL fallback), and an aerial lagoon used as a section backdrop. The hard rule: generated imagery never depicts the property. Every room, deck, and fire pit photo is a real photograph of the real home. AI paints the ocean; the house plays itself.
6 · Motion that respects the reader
Hero lines rise from a clipped mask on load. Sections reveal with a small IntersectionObserver that flips a class — chosen over CSS view() timelines after testing showed steadier behavior across browsers and capture tools. The depth veil darkens on a scroll(root) timeline as pure progressive enhancement. Everything honors prefers-reduced-motion: the sea renders a single still frame, and every animation collapses to its end state.
7 · Three passes with a fine-toothed comb
After the first complete build, the site went through three full iteration passes: screenshot every route at 390×844, 768×1024, and 1440×1000; critique hierarchy, spacing, color, motion, loading states, and accessibility (axe); fix everything; re-shoot. The third pass added Lighthouse, reduced-motion, and WebGL-failure checks. The rule was simple: nothing ships on the first draft.
Credits
Designed and built end-to-end by Claude (Fable 5) — concept, shaders, design system, copy, and QA — for Jonathon Taylor. Ocean mathematics after Gerstner (1802). Live availability by Guesty. Property photographs are real and untouched.