Technical record · updated
The coastal home, a technical record
How the homepage’s 3D coastal home is built: the Blender scripts that author the cave house, the cliff, five cartoon versions of me and their animal neighbours; the browser runtime that loads, lights and animates them; and the light lab that grew out of the onsen. Each technique names its files, the research it borrows from, what it cannot do yet, and the next move worth trying.
The home is an original, stylized browser world in an imagined La Jolla, not a surveyed place. Several techniques adapt published graphics research in small, bounded forms, and each entry says where it simplifies. Measurements come from one Windows desktop unless an entry says otherwise; they are not phone benchmarks.
- 10 systems
- 49 techniques
- 34 design moves
- 11 open problems
- 55 sources
Each system lists its techniques: what each one does, how it works, the files that hold it, the research it borrows from, what it cannot do yet, and the next moves worth trying. The design history keeps every move in order, with what the result said back.
Programs can read the same record as JSON. In the repository, python bin/design_record_brief.py homepage-desk-scene turns it into a brief for the next pass, and new work is appended to _data/design_records/homepage-desk-scene.yml.
Loading and the first frame
What happens between choosing 3D and the first composed view. Models stream in a fixed order, shader programs compile without freezing the page, a loading bar reports only finished work, and a pen sketch fills the wait.
Streaming order and Draco decoding
Shipped · Sep 12, 2026
The shell, the coast, the occupied room and the selected avatar load first; the other rooms and the clifftop planting stream afterwards.
How it works, files and sources
Startup never draws a partial house while the occupied room or the avatar is still loading. The models are Draco-compressed GLBs exported from the Blender sources. model-loader.mjs decodes them through a pinned decoder served as ordinary same-origin files, because the loader’s default inline blob worker conflicts with the site’s Content-Security-Policy. Streamed rooms join the house only once their shader programs are ready, and the clifftop planting (flora.glb, 2.2 MB) loads the first time a visitor goes outside.
- Files
-
-
assets/js/home-scene/model-loader.mjsSame-origin Draco decoding. -
assets/js/home-scene/controller.mjsLoad order and room streaming. -
bin/prepare_draco_worker.pyExtracts the static worker from the pinned loader. -
assets/js/vendor/three-r164/README.mdVendored loader sources and their hashes. -
assets/models/home/manifest.jsonAsset paths, room anchors and camera views.
-
- Sources
- Known limits
-
- The shell (
home-shell.glb, about 1.0 MB) and the selected avatar (0.5 to 0.85 MB) still gate the first view.
- The shell (
Shader programs compiled before the first draw
Shipped · Oct 12, 2026
Every program the first view needs is created ahead of the draw and compiled on the driver’s worker threads, in small batches, so the page keeps scrolling while the house compiles.
How it works, files and sources
Three.js compiles a program the first time it draws a material, and the draw blocks until the driver finishes. On ANGLE/D3D11 each of this scene’s programs took 0.1 to 1.8 s, and the first view froze the page for about 13 s. program-warmup.mjs reproduces each draw’s compile state exactly (target, lights, fog, object, shadow side), so the proxy it compiles is the program the real draw later finds. With KHR_parallel_shader_compile it asks whether a program is done instead of waiting. It creates programs in batches of six materials with a task boundary between them, and submits the next batch only while no more than half the logical cores’ worth of programs are compiling. Polling uses the browser scheduler because test clocks freeze timers. Software rasterizers skip the warmup.
- Files
-
-
assets/js/home-scene/program-warmup.mjsBatched, throttled program creation and completion polling. -
assets/js/home-scene/environment.mjsOne environment generator for the home’s lifetime, with its programs created before the first capture. -
assets/js/home-scene/realism.mjsThe finish passes register their extra programs with the warmup. -
bin/profile_coastal_startup.cjsCold-context startup profiler; set `COASTAL_PROFILE_SEED` for comparable A/B captures. -
docs/evidence/coastal-startup-2026-10-12/README.mdThe October 12 startup checkpoint.
-
- Known limits
-
- A material, pass or override draw that skips the warmup blocks the page on its first draw.
- Throttling trades readiness for responsiveness. Half the cores moved median readiness from 4.66 to 5.17 s; a quarter removed almost every long task but cost 1.3 s.
- Next moves
-
- Measure first Pick the compile throttle per device class (core count, software renderer) from interleaved measurements of readiness and the longest task.
A loading bar that reports finished work
Shipped · Oct 13, 2026
A five-segment progress bar fills only from real events, so it can never run ahead of the work or move backward.
How it works, files and sources
world_controls.liquid renders a role="progressbar" with five segments: the house, the coast, the room, Sirui, and the view’s programs. controller.mjs fills the first four from each model’s streamed bytes and parse, and the last from submitted warmup batches and the programs the driver reports finished. The bar hides once the first view is drawn. Harrison and colleagues found that the same wait feels shorter when a bar keeps moving toward the end; the honest way to earn that is to turn slow, invisible work into visible steps.
- Files
-
-
_includes/home/world_controls.liquidThe progress bar markup. -
assets/js/home-scene/controller.mjsSegment progress from load and compile events.
-
- Sources
- Known limits
-
- GitHub Pages serves the models gzipped, so
Content-Lengthis the compressed size and a model’s segment holds just short of full until its last byte arrives.
- GitHub Pages serves the models gzipped, so
A pen sketch while the house loads
Shipped · Oct 14, 2026
Pen strokes draw the sea with today’s sun or moon, rise into the cliff, open into the cut-away house and settle into the occupied room, then trace the first rendered frame as it fades in.
How it works, files and sources
loading-sketch.mjs hands an OffscreenCanvas to a worker so model parsing never stalls the strokes, and draws on the main thread where a worker cannot. The drawings are traced offline by bin/build_loading_sketch.cjs from object-ID and surface-normal passes of the authored views: object outlines weighted by how much of the frame each object fills, creases sharper than about 57°, and long straight runs favored by the structure tensor’s coherence, after Saito and Takahashi’s line drawings from geometry buffers. Eight drawings pack into 45 KB at three bytes a stroke. Strokes fly on a damped spring (ζ 0.68) along slight arcs, stretch along their velocity, pair with their next targets in Hilbert-curve order so a morph flows instead of tangling, and boil on twos once they land. The render is never held back more than 1.4 s. Reduced motion shows each drawing still, and automation sees the plain loading state unless the URL carries ?cinematic=live.
- Files
-
-
assets/js/home-scene/loading-sketch.mjsPage side, worker hand-off and the first-frame trace. -
assets/js/home-scene/loading-sketch-core.mjsStroke motion, Hilbert pairing and edge tracing. -
assets/js/home-scene/loading-sketch-worker.mjsDraws off the main thread. -
bin/build_loading_sketch.cjsTraces the drawings from the authored views. -
assets/models/home/loading-sketch.jsonThe packed drawings.
-
- Sources
- Known limits
-
- The drawings are fixed captures of the authored views and go stale when a room or camera changes.
- Characters, P and the animals are left out of the traced drawings; the live first frame supplies whoever is home.
- Next moves
-
- Ready Rerun
node bin/build_loading_sketch.cjsafter any room or camera change and check the hand-over on desktop and phone.
- Ready Rerun
Moves in this system: Twenty-six seconds of shaders, moved off the pageA loading bar that only reports finished workStarved, not slowA sketch while the house loads
Rendering and light
Light is layered. A Cycles bake supplies static contact and bounce, a traced probe field relights the sky and lamps, a screen-space pass adds fine contacts, and analytic terms handle the sun, the lamps, the light installations and the light well. A shared finish tone-maps the result.
Baked contact and bounce light
Shipped · Sep 14, 2026
Cycles bakes soft ambient occlusion and indirect diffuse light into vertex colors of the editable house, and the browser adds the time-of-day light on top.
How it works, files and sources
bin/bake_coastal_light.py renders a 24-sample neutral Cycles bake of contact occlusion and indirect diffuse light into the source’s color attributes, as a small clamped modulation, so no time of day is baked in. The glass, the doors and each bed variant are baked with the others hidden, so none shades the rest. bin/optimize_coastal_exports.py then exports byte vertex colors and Draco geometry without overwriting the high-detail source. Both take -- --room=<id> to rebake and export one room.
- Files
-
-
bin/bake_coastal_light.pyThe Cycles bake into vertex colors. -
bin/optimize_coastal_exports.pyCompact web export from the retained source. -
artwork/coastal-home/coastal-home.blendThe editable house.
-
- Sources
- Known limits
-
- Baked vertex light plus procedural normals, not a baked normal-map texture set.
- Vertex density limits how sharp a baked contact can be; small contacts rely on the screen-space pass.
Horizon-based contact light
Shipped · Oct 1, 2026
A visibility buffer computed before the beauty pass darkens only indirect diffuse light near contacts, instead of multiplying the finished image.
How it works, files and sources
contact-occlusion.mjs projects the geometric normal into four fixed view-aligned slices and integrates the cosine-weighted visibility above each slice’s two horizons in closed form, Jimenez and colleagues’ inner integral, with at most 32 depth probes per pixel within 0.36 m. A 3 × 3 reconstruction keeps neighbors on the same tangent plane and rejects separate depth layers and creases. The paper’s cubic fit adds back the light a pale neighborhood would bounce, so linen and stone corners do not turn grey. The result scales only indirectDiffuse inside each material; direct sun, emission and reflections are untouched. There is no random rotation or temporal history, so a still composes identically every time.
- Files
-
-
assets/js/home-scene/contact-occlusion.mjsHorizon integral and edge-aware reconstruction. -
assets/js/home-scene/realism.mjsThe room finish and its material hooks. -
assets/js/home-scene/CONTACT-LIGHTING.mdEquations, controlled comparisons and cost. -
assets/js/home-scene/contact-lab.htmlThe unlisted comparison page. -
assets/js/home-scene/gpu-timer.mjsOptional GPU timing for the lab.
-
- Known limits
-
- Screen-space, so it cannot see hidden or offscreen geometry and overestimates thin occluders.
- Four fixed slices keep a directional bias, and the 480-pixel buffer limits contacts smaller than its footprint.
- No bent normals or specular occlusion; the paper’s GTSO is not implemented.
Traced sky and lamp bounce
Shipped · Oct 2, 2026
A CPU ray tracer bakes static sky visibility and one diffuse bounce into seven probe cages once, then relights them from the actual sky and lamp powers without tracing again.
How it works, files and sources
static-light-field.mjs builds a balanced triangle BVH (light-trace-core.mjs) over the house’s static opaque meshes, traces eight cosine-weighted sky rays per primary hit, and stores irradiance as first-order spherical harmonics in 56 probes with octahedral distance moments, after Ramamoorthi and Hanrahan and the probe visibility of Majercik and colleagues’ DDGI. Unit sky and lamp bases let a time-of-day change recombine the cache with zero rays (0.27 ms in the CPU proof). The field replaces Three’s diffuse hemisphere and environment irradiance once; direct light, PMREM reflections and refraction stay native. The bake runs in yielded slices (3.67 s with an 18.7 ms longest task in the CPU proof) and releases its roughly 37 MB accelerator afterwards.
- Files
-
-
assets/js/home-scene/static-light-field.mjsProbe cages, bake and shader binding. -
assets/js/home-scene/light-trace-core.mjsTriangle BVH and diffuse quadrature. -
docs/static-light-field.mdEquations, ownership and cost. -
docs/warm-house-checkpoint.mdNative scene evidence.
-
- Known limits
-
- Static and one bounce. People, props and wind-moved plants do not affect it.
- Four coefficients lose directional contrast, and sparse probes can leak at thin walls.
- Base colors supply the bounce albedo; texture and procedural detail are not baked again.
Preintegrated skin diffusion
Shipped · Oct 2, 2026
Skin redistributes direct diffuse light across a curved patch with a normalized diffusion profile, so a terminator turns warm instead of hard.
How it works, files and sources
skin-diffusion-profile.mjs integrates Christensen and Burley’s normalized two-exponential profile over a locally spherical patch with 96-node Gauss–Legendre quadrature and stores the result in a 257 × 49 half-float table (about 100 KB) per finish. skin-diffusion.mjs measures mean curvature in meters from view-space derivatives of the skinned position and normal, then replaces only the direct diffuse term of Three’s RE_Direct_Physical with a 70/30 mix of diffused and ordinary response. The default distances, 3.2, 1.2 and 0.65 mm for red, green and blue, are authored values, not a measured fit to anyone’s skin. Specular, sheen, hair, glasses and clothes keep their shaders.
- Files
-
-
assets/js/home-scene/skin-diffusion-profile.mjsProfile integration and the lookup table. -
assets/js/home-scene/skin-diffusion.mjsCurvature and the material hook. -
assets/js/home-scene/SKIN-DIFFUSION.mdDerivation, integration and limits.
-
- Known limits
-
- A local convex-sphere approximation with pointwise shadows. No transport across shadow edges, albedo changes or mesh boundaries, and no ear or backside transmission.
- Anisotropic shapes reduce to scalar mean curvature.
- Next moves
-
- Needs research A spatial, diffuse-only buffer filtered with separable kernels would carry light across shadow edges, at the cost of extra geometry and lighting buffers for small animated subjects.
Sun position and sky
Shipped · Oct 1, 2026
The sun, its shadows and the procedural sky follow NOAA’s solar equations for La Jolla at the scene’s Pacific time.
How it works, files and sources
daylight.mjs uses NOAA’s fractional-year approximation at 32.83° N, 117.27° W, with Pacific daylight saving resolved at local noon. environment.mjs models the sky as geometry and captures it into a prefiltered environment map at twenty-minute steps, keeping one generator for the home’s lifetime so captures never relink programs. On the coast the sun’s shadow map follows the camera, 12 m to each side close up, snapped to whole texels so edges never crawl. Night’s key light is an authored moon.
- Files
-
-
assets/js/home-scene/daylight.mjsSolar position. -
assets/js/home-scene/environment.mjsSky, environment captures, sea and haze.
-
- Sources
- Known limits
-
- Not a high-precision ephemeris or an atmospheric scattering model; the clear-day haze is authored.
- Specular reflections can lag the sun within a twenty-minute step, and there is no lunar astronomy.
Lamps that follow the routine
Shipped · Oct 2, 2026
Eight point lights, at four modeled lamps and four fixtures, brighten where Sirui is and dim to a glow elsewhere; the bedside lamp doubles as a dawn lamp.
How it works, files and sources
warm-practicals.mjs places eight inverse-square point lights at the modeled fixtures and drives each from the routine. The study lamp is at full while reading, working or coding and at 30 % otherwise; the gym light is at full only for workouts; the bedside lamp ramps up over the half hour before waking and stays a night light while Sirui sleeps. No practical casts a shadow map; the static light field carries their traced bounce.
- Files
-
-
assets/js/home-scene/warm-practicals.mjsFixtures and schedules. -
docs/warm-house-checkpoint.mdFixture placement and evidence.
-
- Known limits
-
- Direct practical light is unshadowed; only its cached bounce sees the walls.
Light installations that keep the house’s hours
Shipped · Oct 14, 2026
A terrace festoon, a fire bowl, a gallery soffit, stair step lights and a pool light switch with the time of day and the routine.
How it works, files and sources
light-installations.mjs shades every emitter analytically in every material. Line emitters use the exact irradiance of a uniformly glowing segment on a matte surface, clipped to the surface’s horizon; the test checks it against brute-force integration on 40 random layouts and against the infinite-line limit 2I/h. Each emitter is bounded by a box, so the festoon lights the terrace and the front of the living floor through the glass but not the cliff under the ledge. Schedules fade in and out and wrap past midnight: the festoon from dusk to 23:30, the fire bowl from 19:30 to 22:45, the step lights at 45 % all night. By day one uniform skips the whole term. The layers follow Richard Kelly’s ambient luminescence, focal glow and play of brilliants.
- Files
-
-
assets/js/home-scene/light-installations.mjsEmitters, closed-form line irradiance and schedules. -
bin/coastal_facade.pyWrites the terrace’s festoon and fire positions into the manifest.
-
- Sources
- Known limits
-
- No installation casts shadows or adds a baked bounce.
- A material reads at most ten line and four point emitters.
- Next moves
-
- Needs research Caustics from the pool light onto the onsen vault, the hard light paths the light lab was built to study.
Anti-reflective glass front
Shipped · Oct 14, 2026
The cave front and the terrace balustrade use low-iron, anti-reflective glass, about 1 % reflectance head-on, so the cut-away house stays readable by day.
How it works, files and sources
glazing.mjs draws each pane over what is behind it with premultiplied blending: its color is only what it reflects, and the view through is dimmed by that share plus a little absorption. A Schlick term still turns the pane into sky toward grazing angles, as real glass does. The panes are not transmissive, because a transmission material would refract a copy of the opaque scene and lose the transparent onsen water and steam behind the front. Glass casts no shadow and does not occlude the contact pass, the volume depth or the light field; its frames do.
- Files
-
-
assets/js/home-scene/glazing.mjsThe anti-reflective glass material. -
bin/coastal_facade.pyThe frame, panes and sliding doors.
-
- Known limits
-
- Ordinary glass (4 %) is physically right and turned the cut-away view into sky by day; the 1 % coating is a legibility choice.
A light well with traced shaft light
Shipped · Oct 14, 2026
A shaft drilled from the clifftop brings direct sun into the onsen, and the beam, its sun patch, the shaft’s skylight and the lit steam all share one visibility function.
How it works, files and sources
bin/coastal_lightwell.py drills a shaft of 0.42 m radius, 1.67 m through the rock, leaning 28° toward the noon sun, and exports a shadow-only copy of the onsen’s vault, so the bath receives direct sun only through the shaft even while the cutaway lifts the roof. light-well.mjs follows four light paths. The sun’s shadow map carries the moving patch on the floor or water. Dust in the beam is single scattering with a mildly forward Henyey–Greenstein phase, marched only inside the shaft’s elliptic cylinder, and the same visibility masks the steam’s sunlight. Quasi-Monte Carlo path tracing of the drilled cylinder, on a fixed Halton sequence so it does not flicker, gives the radiance leaving the aperture whenever the sun moves. Lambert’s closed form for a uniformly bright polygon then lights the bath from the aperture and from the sun patch as two small area lights. ?onsen-light=open restores the open sky for comparison.
- Files
-
-
bin/coastal_lightwell.pyThe drilled shaft and the shadow-only vault. -
bin/refresh_coastal_lightwell.pyRebuilds the shaft in the retained house. -
assets/js/home-scene/light-well.mjsShaft visibility, dust beam, traced aperture radiance and area lights. -
assets/js/home-scene/steam-volume.mjsMasks the steam’s sunlight with the shaft visibility.
-
- Tests
- Sources
- Known limits
-
- Only 5.5 % of the sky passes a shaft four radii long (the tracer matches the analytic view factor to within 0.4 %), so outside midday the grotto is darker than the open cutaway was.
- At plausible dust densities the beam is invisible; it shows because the bath air is humid.
- Next moves
-
- Needs a decision Choose between the grotto and an open onsen with the well seen only from the clifftop.
Tone curve, bloom and fabric sheen
Shipped · Oct 13, 2026
The finish tone-maps with ACES Filmic, blooms only true light sources and gives woven fabric a sheen; URL flags preview alternatives for matched comparisons.
How it works, files and sources
realism.mjs owns the room finish: the contact pass, beauty, an optional bloom and the output pass. Bloom is the vendored r164 UnrealBloomPass, loaded on demand, glowing HDR values above 1.6 before tone mapping. That is above sunlit walls, so midday frames are unchanged while the evening lamps glow. Linen, textile and jute use Three’s Charlie sheen, after Estevez and Kulla. look.mjs keeps session-long preview flags (?tone=aces, agx or neutral, &bloom=0|1, &sheen=0|1, ?look=reset), and exposure factors keep AgX and Khronos PBR Neutral within 2 to 3 % of ACES’s mean brightness, so a comparison is not decided by brightness alone.
- Files
-
-
assets/js/home-scene/look.mjsSession-long look flags. -
assets/js/home-scene/realism.mjsFinish pipeline, bloom and sheen materials. -
assets/js/home-scene/controller.mjsExposure factors per tone curve.
-
- Tests
- Known limits
-
- The chosen curve is a judgment from one matched sheet. Neutral kept the truest coast colors at noon but lifted evening shadows into grey; AgX greyed the sandstone and whitened lamplight.
Strand highlights on hair
Shipped · Oct 13, 2026
Hair gets two shifted highlight bands across the strands instead of a round spot.
How it works, files and sources
strandHighlights in realism.mjs adds two lobes to every avatar’s hair material: a near-white primary shifted toward the tips and a broader, hair-tinted secondary shifted toward the roots. They are the R and TRT lobes of Marschner and colleagues in Scheuermann’s real-time form, on Kajiya and Kay’s strand model, broken up by slow clumps. The avatars carry no strand tangents, so each strand direction is combed from the crown of the hair mesh’s bind pose along the surface and skinned like a normal. The lobes join Three’s per-light loop, so shadows and every lamp apply. Widening the lobe by the normal’s variance across the pixel, while keeping its energy, removed sparkle at the hairline (geometric specular antialiasing).
- Files
-
-
assets/js/home-scene/realism.mjsThe strand-highlight material hook.
-
- Sources
- Known limits
-
- Direct light only; reflections of the environment on hair stay a plain rough response.
- Next moves
-
- Measure first Measure sparkle on other bumpy surfaces before applying the same lobe widening everywhere.
Physical-scale surface detail
Shipped · Sep 13, 2026
Wood grain, fabric weave, sandstone bedding, honeycomb weathering and wet intertidal rock are computed in the shader from physical coordinates, with no texture downloads.
How it works, files and sources
realism.mjs derives detail from world-space meters, so its scale survives batching and the many UV layouts in the glTF exports. Close-range sandstone gets bedding planes of uneven thickness that show only where weathering picks them out and fade before they are thinner than a pixel, honeycomb weathering (tafoni) from Worley cells in patches on steep faces, runoff streaks, and a soil-and-grass tint on the clifftop. Rock the high tide reaches stays wet, darker and glossier, with a band of algae low and barnacle specks above. Bumps use derivative bump mapping filtered below a pixel.
- Files
-
-
assets/js/home-scene/realism.mjsProcedural surface detail for every finished material. -
assets/js/home-scene/materials.mjsShared material art direction.
-
- Sources
- Known limits
-
- Procedural detail is not included in the light field’s bounce albedo.
- Next moves
-
- Ready Bark plates for the Torrey pines.
Moves in this system: Contact light on the right termSkin, water and wind as transportSteam and light that know the houseA look lab instead of a guessA highlight that says hairA well for the sunLights that keep the house’s hours
Water and atmosphere
Two bodies of water with different models. The Pacific is a bounded analytic spectrum with a live mirror and a lit shoreline; the onsen is a small simulated height field driven by the bather’s hand. Steam rises from the bath as transported scalar fields.
A bounded Gerstner ocean
Shipped · Oct 1, 2026
Ten deterministic wave components displace the sea geometry and its pixel normals together, and twelve short ripples tilt the normals up close.
How it works, files and sources
ocean-spectrum.mjs authors ten choppy Gerstner components from 24.7 m to 6.1 cm with gravity–capillary dispersion, ω² = gk + (σ/ρ)k³, and analytic tangents of the actual displacement, after Finch’s GPU Gems chapter. Keeping the steepness sum Σqak under 0.3 prevents foldover. Each amplitude fades with a Gaussian weight of the pixel’s footprint, and the slope energy it loses widens the GGX lobe, so a distant sea neither flattens nor sparkles: a scalar version of Bruneton, Neyret and Holzschuch’s transition from geometry to BRDF. Twelve normal-only ripples, spread over the wind’s half-plane by the golden angle from 0.9 m to 2.4 cm, break up the ruled stripes that single plane waves made at walking distance.
- Files
-
-
assets/js/home-scene/ocean-spectrum.mjsWave components, footprint filtering and the CPU wave sampler. -
assets/js/home-scene/environment.mjsThe sea shader. -
assets/js/home-scene/PHYSICS.mdEquations and evidence.
-
- Known limits
-
- A sparse authored spectrum on a finite 400 m surface, with no FFT, measured spectrum, cascades or weather.
- The roughness widening is a scalar isotropic fit, not the paper’s anisotropic slope statistics.
- Next moves
-
- Needs research A spectral FFT ocean after Tessendorf, kept consistent with the CPU sampler that floats objects and aims arrival rays.
Swash, wet sand and lit foam
Shipped · Oct 13, 2026
Where the sea meets the beach, a lacy swash band breathes with each wave’s run-up, broken fronts travel in and fade, thin water shows wet sand, and foam is lit like a white surface.
How it works, files and sources
In the sea shader, foam comes from compression of the horizontal displacement Jacobian and from crest height, masked by the authored shoreline. A swash band follows a periodic run-up, and a broken front starts about 3 m out and fades as it travels. Within a metre or two of the waterline the water shows wet sand under a Beer–Lambert-shaped depth attenuation. Foam takes the pixel’s own diffuse light, so it dims at sunset and turns moonlit blue instead of glowing. Beach width and shoreline samples come from the manifest values Blender also uses (shore.mjs).
- Files
-
-
assets/js/home-scene/environment.mjsFoam, swash and shallow-water color. -
assets/js/home-scene/shore.mjsExported shoreline rows and support polygons.
-
- Known limits
-
- Art-directed. No bathymetry, foam transport, conservation of foam or breaking-wave solver.
- The authored beach profile sits about 1.5 m off the visible waterline on the left side.
- Next moves
-
- Needs research Crest glow and sun glitter, which need back light the default camera rarely sees.
Planar sea reflection
Shipped · Sep 13, 2026
A 384-pixel live mirror reflects the actual cliff and house in the sea, with exact water Fresnel on indirect specular only.
How it works, files and sources
reflection.mjs renders the scene from a camera mirrored across the water plane with an oblique near plane, after three.js r164’s Water, in linear radiance, and filters it with five taps by roughness. Unpolarized dielectric Fresnel with n = 1.333 replaces only the indirect specular term: about 2 % head-on, rising to one at grazing. The mirror skips transmissive meshes (the onsen water and the coffee glass), because each one made Three redraw the whole opaque scene at full canvas size even inside the mirror.
- Files
-
-
assets/js/home-scene/reflection.mjsThe mirror camera, clipping and Fresnel.
-
- Sources
- Known limits
-
- A flat reference plane with slope-based distortion, not a ray-traced curved reflector; five taps approximate the blur.
A height-field onsen
Shipped · Oct 2, 2026
The onsen is a 40 × 40 shallow-water height field. Waves carry water between cells, reflect off the rim and the bather, and fade.
How it works, files and sources
heightfield-water.mjs follows section 2.1 of Chentanez and Müller: staggered heights and face velocities, conservative upwind depth fluxes, pressure from the free-surface gradient and reflecting solid faces, at fixed 1/120 s ticks with CFL subdivision. pool-geometry.mjs measures the real basin with five agreeing interior rays (about 0.127 m deep). The bather is a one-way circular obstacle whose excluded volume is redistributed, so total volume holds to about 10⁻¹⁴. pool-stroke.mjs drives a slow anatomical hand skim, and the palm’s submerged horizontal velocity relaxes nearby face velocities inside each CFL step, so resting hands add no energy. onsen-water.mjs packs elevation, slopes and wet weight into a 40² texture for the existing material. The solver runs only when the camera can see the pool.
- Files
-
-
assets/js/home-scene/heightfield-water.mjsThe conservative shallow-water solver. -
assets/js/home-scene/onsen-water.mjsRenderer adapter, palm coupling and evidence. -
assets/js/home-scene/pool-geometry.mjsMeasures the native basin. -
assets/js/home-scene/pool-stroke.mjsThe prescribed hand skim. -
assets/js/home-scene/ONSEN-WATER.mdEquations, coupling limits and numerical evidence.
-
- Sources
- Known limits
-
- First-order semi-Lagrangian velocity advection, and no wet and dry terrain, open boundaries, buoyancy, splashes or caustics.
- One-way coupling. The water never pushes the hand or the bather.
- Shallow-water dispersion is inaccurate for wavelengths near the depth.
- Next moves
-
- Ready A steady drip from the bamboo spout into the solver at the spout’s mouth.
Thermal steam
Shipped · Oct 2, 2026
Bath mist is two transported scalar fields, droplet concentration and temperature, rendered with emission-free single scattering.
How it works, files and sources
steam-density.mjs advects an 18³ concentration and temperature field at a fixed 30 Hz with midpoint semi-Lagrangian backtracing, after Stam, and lets warmer cells rise faster, after Fedkiw, Stam and Jensen’s separate smoke density and temperature. It diffuses and cools them with exact exponential factors and relaxes the warm surface sources. Wind inside the bath is the shared curl field at 0.16 strength. steam-volume.mjs marches up to 40 samples with Beer–Lambert extinction and a normalized Henyey–Greenstein phase, after Wronski, and splits each ray once at the nearest glass or water interface so the gas behind and in front of it composes in order.
- Files
-
-
assets/js/home-scene/steam-density.mjsConcentration and temperature transport. -
assets/js/home-scene/steam-volume.mjsSingle-scattering ray march and the interface split. -
assets/js/home-scene/volume-depth.mjsOpaque and nearest-transmissive depth for the split. -
assets/js/home-scene/THERMAL-STEAM.mdModel, light integration and cost.
-
- Known limits
-
- Prescribed buoyant rise, with no pressure solve, phase change or mass-conserving gas solver.
- Single scattering and short-range attenuation only; beyond the light-well mask there is no opaque sun-shadow query.
Moves in this system: A bounded ocean and a driven recordWaves from the hand, not a timerA mirror that rendered the house twiceWhere the sea meets the sandMeasure before merging
Wind and plants
Inside, one divergence-free wind field moves the leaves, the dust, the spray and the bath steam. The clifftop planting still sways on its own simpler rule.
A divergence-free wind field
Shipped · Oct 2, 2026
A six-mode curl-noise field gives the room plants, dust, spray and bath steam one coherent wind.
How it works, files and sources
wind-field.mjs takes the analytic curl of a bounded vector potential, a small Fourier basis in place of Bridson, Hourihan and Nordenstam’s Perlin potentials, so the velocity is exactly divergence-free and its speed is bounded by 0.647 m/s with the defaults (a 0.45 m/s gust over a small mean drift). Wavelengths run from 1.2 to 7 m. The same field runs in GLSL (coastalWind) and on the CPU, which agree within the 8-bit quantization step in native fixtures. coastal-wind.mjs owns one field and one active clock for the room.
- Files
-
-
assets/js/home-scene/wind-field.mjsThe curl-potential field, CPU and GLSL. -
assets/js/home-scene/coastal-wind.mjsOne field and clock for the room’s plants and particles. -
docs/coherent-wind-equations.mdField, bound, attachment and particle equations.
-
- Known limits
-
- Procedural, not a pressure-projected flow. No obstacles, building collision or turbulence spectrum, and a spatial mask would break the divergence guarantee.
Leaves attached to real stems
Shipped · Oct 2, 2026
Room-plant leaves bend in the wind around their actual attachment points, with corrected normals and matching shadows.
How it works, files and sources
plant-motion.mjs finds each leaf component’s attachment once, from nearby support vertices of the real stems, and stores a root, a weight and its gradient per vertex. Each component follows the steady periodic response of a critically damped oscillator to the wind, at most 11.7 mm of horizontal deflection, and normals use the inverse transpose of the bend’s Jacobian. The identical hook runs on beauty, depth and distance materials, so shadows bend with the leaves. 110 of 132 olive-leaf components bend; hanging vines and agave stay rigid.
- Files
-
-
assets/js/home-scene/plant-motion.mjsAttachments, damped response and the shared shader hook. -
assets/js/home-scene/coastal-wind.mjsBinds plants to the field.
-
- Known limits
-
- An authored damped response, not calibrated leaf elasticity.
- The indirect contact buffer sees the rest pose, a documented mismatch below 12 mm.
Clifftop planting in the breeze
Shipped · Oct 14, 2026
Sage, pine needles, grass, lemonade berry and palm fronds sway by a weight stored in their UVs, leaning inland in the onshore breeze.
How it works, files and sources
bin/coastal_flora.py writes a sway weight into each leaf, needle and blade’s UV x, 0 at the plant’s base and 1 at its top. swayInBreeze in realism.mjs displaces vertices by that weight squared: a small inland lean plus two sines with a per-plant phase taken from world position. The motion freezes with the scene clock. Because it moves, the planting stays out of the static light field.
- Files
-
-
bin/coastal_flora.pyWrites the sway weights. -
assets/js/home-scene/realism.mjsThe sway shader hook.
-
- Known limits
-
- A separate sine sway that does not sample the curl wind field, so the clifftop and the room plants move in two different winds.
- Only the beauty material sways; shadow and depth passes see the rest shape.
- Next moves
-
- Ready Drive the clifftop sway from the same wind field as the room plants, particles and steam, in the beauty and depth materials.
Dust and spray in the same wind
Shipped · Oct 2, 2026
42 dust motes and 140 surf droplets advect through the shared wind field on fixed ticks; spray also falls under gravity and drag.
How it works, files and sources
wind-particles.mjs integrates dust with midpoint RK2 in both position and time, and spray ballistically with gravity, air drag, seeded launches and lifetimes, at fixed 1/60 s ticks, so different frame rates give the same history. A deterministic prewarm from negative time gives a mature still composition, including under reduced motion. The original 18 steam points were replaced by the thermal field.
- Files
-
-
assets/js/home-scene/wind-particles.mjsBounded particle integration. -
assets/js/home-scene/environment.mjsEmitters, bounds and drawing.
-
- Known limits
-
- Bounds respawn particles; there is no collision with the house.
Moves in this system: One wind for leaves and particles
Characters and animation
Five cartoon versions of me live one at a time on an authored San Diego routine. Their walks, activities, eyes and bed are fitted to the house’s actual geometry.
Five original avatars
Shipped · Sep 12, 2026
Lizard, South Park, Simpsons, Ghibli and Rick and Morty interpretations share one bone convention and fourteen authored clips; one appears at a time, new on each refresh.
How it works, files and sources
bin/coastal_characters.py builds the four human models with shaped jaws, inset eyes and side-parted hair. coastal_hair.py and coastal_clothing.py make continuous hair and garment surfaces. coastal_anatomy.py lengthens long bones in rest space to adult proportions while keeping hands, feet, bone names and activity endpoints. coastal_hands.py adds Ghibli’s twenty finger joints and anatomical wrist targets for the pull-up and dip grips, and coastal_expression.py adds paired eyelid surfaces with BlinkL and BlinkR morphs. Likeness is judged in front, profile and full-body Cycles renders, then at homepage size during an activity.
- Files
-
-
bin/coastal_characters.pyThe four human models. -
bin/coastal_anatomy.pyAdult rest proportions. -
bin/coastal_hair.pyContinuous swept-back hair. -
bin/coastal_clothing.pyContinuous garments. -
bin/coastal_hands.pyFinger joints and grip wrist targets. -
bin/coastal_expression.pyEyelids and blink morphs. -
artwork/coastal-home/portraits/Front, profile and full-body review renders.
-
- Sources
- Known limits
-
- Authored stylization, not a likeness scan; my own judgment is the acceptance test.
- No physical hair or cloth simulation on the bodies.
- Next moves
-
- Needs research Film-studio character surfacing and close-up detail, which the brief names as the higher visual target.
An authored San Diego routine
Shipped · Sep 12, 2026
A pure clock maps Pacific time to an activity and a room. Breakfast comes late, coding runs into the night, and the onsen faces the Pacific.
How it works, files and sources
routine.mjs resolves America/Los_Angeles with daylight saving and reads weekday and weekend schedules from the manifest; weekends extend coding to 04:00 and sleep to 12:30, and the evening drink rotates by date. The theme panel’s sun arc can preview an hour through setTimePreview(minute), and Now clears both previews. A theme chosen anywhere else never sets a preview. It is a little imagined day, not a location feed.
- Files
-
-
assets/js/home-scene/routine.mjsThe pure clock and exploration state. -
assets/models/home/manifest.jsonSchedules, activities and room anchors. -
assets/js/sun-arc.mjsHour previews from the theme panel.
-
- Known limits
-
- Authored fiction, with no telemetry by design.
- Next moves
-
- Needs a decision A sunset hour on the new terrace.
Activities fitted to equipment
Shipped · Sep 14, 2026
Coffee, strength sets, typing, meals and sleep use exported equipment contacts, so cups and weights travel between surfaces and hands.
How it works, files and sources
activities.mjs runs short deterministic phases against equipment anchors from bin/coastal_furnishing.py: grinding, brewing, pickup, carrying, drinking and returning the same cup; pull-ups, dips, a rack-to-hand dumbbell transfer and rests. A runtime arm correction follows the mixed pose to remove residual drift, and grip-targets.mjs lets an articulated avatar name its own anatomical wrist target per clip. bin/review_coastal_models.py measures deformed source vertices and wrists at five key times per clip.
- Files
-
-
assets/js/home-scene/activities.mjsActivity phases and contact correction. -
assets/js/home-scene/grip-targets.mjsPer-avatar wrist targets. -
bin/coastal_furnishing.pyKitchen and gym equipment and their contacts. -
bin/review_coastal_models.pyContact measurements on the deformed sources.
-
- Known limits
-
- Authored poses with a small contact correction, not whole-body dynamics.
Walking on the real floor and treads
Shipped · Sep 13, 2026
Walks follow authored corridors and every stair tread, and a short two-bone solve keeps each foot on its support.
How it works, files and sources
navigation.mjs routes between any two rooms along straight segments that stay inside the corridor, through the 15-riser stair and its landing; a spline could cut through furniture or overshoot the first riser. locomotion.mjs constrains the mixer’s gait to the exported floor polygons and treads, so a climbing foot never passes through a riser or hovers above it.
- Files
-
-
assets/js/home-scene/navigation.mjsAuthored circulation over floors and treads. -
assets/js/home-scene/locomotion.mjsTwo-bone foot fitting. -
bin/coastal_section.pyExports the floors, treads and corridors.
-
- Known limits
-
- Authored paths, not a general collision or crowd solver.
Eyes, blinks and attention
Shipped · Oct 2, 2026
An additive layer adds asymmetric blinks, quiet breathing and a short eyes-first glance at the visitor’s pointer, then returns to the routine.
How it works, files and sources
character-performance.mjs turns the pointer into a ray through the real camera, aims at a point halfway between the eyes and the visitor in the head’s own optical frame, and lets the eyes lead the head with a short hold and habituation, after the eye and head bandwidths in Pan and colleagues’ robot gaze work. Contact-sensitive clips (strength, walking, coffee, carrying) cancel head motion; eating and drinking allow the eyes only. Sleep closes the lids, and pause, reduced motion and hidden views compose neutral lids. Breathing moves the head joint at most 0.65 mm, and no spine, wrist, finger, foot or root transform changes.
- Files
-
-
assets/js/home-scene/character-performance.mjsBlinks, breathing and attention. -
artwork/coastal-home/CHARACTER_PERFORMANCE.mdEyelid authoring and retention checks. -
docs/evidence/character-performance-2026-10-02/README.mdAcceptance evidence.
-
- Sources
- Known limits
-
- Pointer proximity stands in for a visitor; there is no camera perception or face detection.
- There is no continuous eyelid and glasses collision proof.
Bedding fitted to each sleeper
Shipped · Oct 14, 2026
Six bed variants, a made bed and one fitted pillow and duvet per avatar, replace a rigid pillow that heads sank into and a cover that cut through chests.
How it works, files and sources
bin/refresh_coastal_bedding.py dents each down pillow under its avatar’s head with vertical ray casts against the sleep clip at its deepest and shallowest breath. coastal_bedding.py drops each duvet over its sleeper with an original position-based cloth solver, colored Gauss–Seidel distance constraints over the upper envelope of sleeper, pillow, mattress, frame and bedside table, then trims it across the chest, quilts it and gives it 3 cm of fill. The page shows the made bed until the sleep clip plays, then the active avatar’s pair.
- Files
-
-
bin/coastal_bedding.pyThe position-based drape. -
bin/refresh_coastal_bedding.pyBuilds the six variants in the retained house. -
bin/review_coastal_models.pyAsserts the body, cover and pillow measurements.
-
- Known limits
-
- Authored drapes, not runtime cloth. The duvets do not move with breathing; the 3 cm fill covers that.
- The sleep room grew from 57 KB to 260 KB.
Moves in this system: Closed solids and fitted activitiesFingers that hold the barEyes that notice, then returnA bed someone can sleep in
Wildlife
Brush rabbits, California sea lions, harbor seals, a raccoon, western gulls and sandpipers share the coast. Their acting is authored and sampled from time, and their feet meet the same terrain the house stands on.
Articulated animal masters
Shipped · Sep 14, 2026
Every animal is an original Blender model with a continuous body, named acting pivots and vertex-color coat marks.
How it works, files and sources
bin/build_coastal_wildlife.py builds the rabbit, sea lion and seal masters, and bin/build_coastal_neighbors.py builds the raccoon, gull and sandpiper with canonical head, eye, muzzle, limb and wing pivots. Marine flipper roots overlap the torso through sampled acting extremes. Habitat choices follow the City of San Diego’s Children’s Pool information: sea lions on rock haul-outs, harbor seals on sheltered sand.
- Files
-
-
bin/build_coastal_wildlife.pyRabbit, sea lion and seal masters. -
bin/build_coastal_neighbors.pyRaccoon, gull and sandpiper masters. -
artwork/coastal-home/NEIGHBORS.mdNeighbour assets, pivots and rebuild commands. -
assets/js/home-scene/wildlife.mjsInstances, habitats and inspection.
-
- Known limits
-
- Authored acting, not measured biomechanics or a trained animal model.
Acting sampled from active time
Shipped · Oct 2, 2026
Rabbits hop with held contacts, the raccoon plants its paws, sandpipers probe the wet edge, and gulls glide between perches.
How it works, files and sources
wildlife-motion.mjs holds rabbit travel at takeoff and landing and advances only while airborne, with quintic travel so velocity and acceleration never jump. wildlife-neighbor-motion.mjs keeps the raccoon’s stance paws on world anchors while two-joint legs follow, pivots sandpipers gradually at each zero-speed reversal, and joins gull flight and perch with C2-continuous transfers and headings from the flight derivative. Nothing integrates frame by frame, so a pause or a slow renderer cannot accumulate drift. The rabbit loops follow clear scrub edges sampled against the finished ground (rabbit_habitats.py).
- Files
-
-
assets/js/home-scene/wildlife-motion.mjsRabbit and marine acting. -
assets/js/home-scene/wildlife-neighbor-motion.mjsRaccoon, sandpiper and gull acting. -
bin/rabbit_habitats.pyClifftop rabbit loops.
-
- Known limits
-
- Deterministic choreography, with no aerodynamics, general collision solver or feather simulation.
Gull feet fitted to real perches
Shipped · Oct 2, 2026
Gull soles fit bounded patches of the retained coast and the gallery rail, with fixed tarsus lengths and hidden hip accommodation.
How it works, files and sources
bin/build_coastal_perch_support.py exports 51 local support triangles in four patches, matching the GLB’s tessellation, and 192 projected sole samples per foot. wildlife-perch-contact.mjs fits each webbed foot to that surface through flare and toe-off with a one-sided clearance. Across 337 sampled poses the worst penetration was 1.91 micrometres. Plants are excluded from perch support, because gulls had sampled grass blades as footing.
- Files
-
-
bin/build_coastal_perch_support.pyExports the perch patches and sole samples. -
assets/js/home-scene/wildlife-perch-contact.mjsRuntime foot fitting. -
assets/models/home/wildlife-perch-support.jsonThe exported contact data.
-
- Known limits
-
- Sampled geometric checks, with no guarantee between samples or over future terrain.
Moves in this system: Neighbours on the coastAnimals that plant their feet
Terrain, architecture and flora authoring
Everything on the page is exported from editable Blender sources by Python scripts run in Blender 4.5 LTS’s background mode, then baked, optimized and reviewed. Focused refresh scripts change one aspect of the retained house without rebuilding it.
Rebuildable Blender sources
Shipped · Sep 12, 2026
One build script writes the house, the characters, their GLBs and review portraits; bake, export and review scripts follow every geometry change.
How it works, files and sources
bin/build_coastal_home.py writes coastal-home.blend, the character sources, the GLBs and review portraits in one run, and flags such as --house-only, --avatar=ghibli, --section-only, --coast-only and --export-only rebuild part of it. After geometry changes, bake_coastal_light.py and then optimize_coastal_exports.py rebake and export. render_coastal_review.py renders neutral-clay and material views from the manifest cameras, and review_coastal_models.py records contacts, hashes and triangle counts under artwork/coastal-home/reviews/. Focused refresh_coastal_*.py scripts change finishes, decoration, the facade, the planting or the light well in the retained house.
- Files
-
-
bin/build_coastal_home.pyThe full authoring entry point. -
bin/coastal_sculpt.pyContinuous sculpt surfaces. -
bin/coastal_craft.pyFloorboards, joinery, shelves and small objects. -
bin/render_coastal_review.pyMatched-camera review renders. -
artwork/coastal-home/reviews/Review renders and measurement reports. -
artwork/coastal-home/PROVENANCE.mdAuthorship, references and rebuild commands.
-
- Sources
- Known limits
-
- Blender ran through its Python API; no hand sculpting in the interface is claimed.
- Generated concept boards guided direction and are not finished models.
One section for house, cliff and beach
Shipped · Sep 13, 2026
Two storeys at 0 and 2.6 m are carved into one continuous mainland cliff that meets a sloping beach about seven meters below the lower floor.
How it works, files and sources
bin/coastal_section.py carves the cave from closed solids with exact Boolean operations, welds and caps the mainland and the removable roof, and rejects nonmanifold edges; only the local ceiling lifts for cutaways. coastal_landscape.py composes headlands, coves and ledges and exports habitat paths, shoreline samples, perches and tidal basins into the version-5 manifest, so the browser’s animals and surf stand on the same coast. Planting, paths and perches are ray-cast against the carved mesh. The sandstone layering and fallen rock follow UC San Diego’s beach-walk notes and Scripps’ cliff-failure research as reference, not as a survey.
- Files
-
-
bin/coastal_section.pyThe carved two-storey section. -
bin/coastal_landscape.pyHeadlands, beach and exported contact data. -
assets/models/home/manifest.jsonShared floors, treads, habitats and shoreline.
-
- Known limits
-
- An imagined home, not a surveyed reconstruction of a real place or building.
Glass front, terrace and alcove screens
Shipped · Oct 14, 2026
A bronze-framed glass front sits inside the cave lip, a corbelled sandstone terrace is carved below it, and cedar screens turn the bedroom and onsen into alcoves.
How it works, files and sources
bin/coastal_facade.py sets the frame 0.4 m inside the rock lip, where a ray survey found the jamb continuous (a cut-stone pier fills one eroded notch), and puts the two lift-and-slide doors where the kitchen and lounge cameras look in; their view frusta cross the glass line at x −3.3 to −0.9 m and 2.1 to 3.7 m. By day the browser slides the doors into the rock. The terrace is 3.1 m deep against the sheltered south rock and narrows to a walk at the north, with a bench and fire bowl, loungers, a bistro table, an olive, rosemary and a festoon. The plan runs from prospect to refuge, after Christopher Alexander’s Intimacy Gradient, Six-Foot Balcony and Half-Open Wall patterns.
- Files
-
-
bin/coastal_facade.pyFrame, doors, terrace and screens. -
bin/refresh_coastal_facade.pyRebuilds them in the retained house.
-
- Sources
- Known limits
-
- The shell grew by about 140 KB.
River stones and things on the walls
Shipped · Oct 14, 2026
24 rounded river stones replace the onsen’s faceted slabs, and each room gains objects chosen for its use.
How it works, files and sources
bin/coastal_decor.py adds a bamboo spout, a bucket and stool and a cedar slat panel to the onsen, a framed seascape over the bed, a pinboard of sketches above the desk, and a coiled-rope sun and three thrown pots in the lounge. Each piece sits on a wall the room’s authored camera actually sees, found by casting rays through that camera’s image, and the pieces join into 14 meshes, one per room and material. A pinboard of sketches says researcher before any label does, which is Henry Jenkins’s environmental storytelling.
- Files
-
-
bin/coastal_decor.pyStones and decoration. -
bin/refresh_coastal_decor.pyReplaces them in the retained house.
-
- Sources
- Known limits
-
- Re-exporting the onsen re-quantized its stone and moved the solver’s measured depth by 0.02 mm, which a five-decimal test caught.
A clifftop planted from photographs of the bluffs
Shipped · Oct 14, 2026
Sage scrub, sagebrush, wild oats, lemonade berry, prickly pear, yucca, iceplant, daisies, Torrey pines and one Canary Island date palm, each placed by its distance from the cliff edge.
How it works, files and sources
bin/coastal_flora.py builds code-authored plants (leaf sprays on woody stems, needle tufts on leaning trunks, grass tufts, succulent mats) and keeps them off the doorway, the stepping stones, the rabbit paths, the perches and the 28 agaves. The second planting draws from its own random stream, so every plant from October 14 stays where it was. Species and October colors follow photographs of Torrey Pines, Goldfish Point and the Coast Walk, credited in the provenance file, and Cabrillo National Monument’s bluff plant communities: succulents at the rim, grey scrub behind them, lemonade berry in sheltered pockets.
- Files
-
-
bin/coastal_flora.pyPlant builders and placement. -
bin/refresh_coastal_flora.pyReplants the retained clifftop. -
artwork/coastal-home/PROVENANCE.mdThe photographs used as reference.
-
- Sources
- Known limits
-
- The inland hill is still mostly bare sand.
- Mats stop where the ground falls 1.2 m below their centre; plants are not fitted to arbitrary slopes.
- Next moves
-
- Ready Sparse sagebrush and oats on the inland hill, kept clear of the stepping stones.
Moves in this system: An inhabited home from Blender sourcesTwo floors carved into one cliffStones you could sit on, and things on the wallsA front for the cave, and a terrace below itA coast you can walk up toMore kinds of plant, from photographs of the real bluffs
The light-paths lab
A separate site experiment at /projects/light-paths/ that renders the onsen’s sunlit pool twice in the browser, to show when Metropolis light transport beats independent path sampling on light the house actually has.
One path sampler, two estimators
Experimental · Oct 14, 2026
A dependency-free JavaScript renderer draws a simplified onsen with path tracing and with primary-sample-space Metropolis light transport from the same path sampler.
How it works, files and sources
assets/js/light-lab/core.mjs traces a box grotto, a round pool with seven frozen analytic ripples and a radial drip ripple, a disc light well and a 0.8° sun. Every random decision reads a fixed coordinate of a sampler. Path tracing feeds it fresh numbers; Kelemen and colleagues’ Metropolis feeds it mutated ones, evaluated lazily with PBRT’s bookkeeping. Both use next-event estimation to the sun and sky with power-heuristic multiple importance sampling, after Veach. Water hits use safeguarded Newton iteration and a certified march that cannot skip a crossing, and hot loops write into typed arrays so tracing allocates nothing.
- Files
-
-
assets/js/light-lab/core.mjsScene, path sampler and both estimators. -
assets/js/light-lab/worker.mjsRuns one estimator per worker. -
_projects/light-paths.mdThe public lab page.
-
- Tests
- Known limits
-
- A simplified scene. Frozen ripples, no steam, a round opening instead of the tilted shaft, and a sun three times the real size by default.
- Metropolis’s total brightness rests on independent samples that usually miss the sun, and chains start in skylight and need about 20,000 mutations each to settle.
- Next moves
-
- Needs research Manifold exploration or specular manifold sampling, to win the race on the caustic paths outright.
A race with the same minute each
Experimental · Oct 14, 2026
The lab page runs the two renderers in module workers for the same wall time and charts their error against a converged reference.
How it works, files and sources
page.mjs starts one worker per renderer only on request, pauses after a minute, off screen and in hidden tabs, and shows still frames before a run and where a browser cannot run one. Error is the luminance RMSE on an 80 × 60 grid divided by the reference mean. After a minute in the recorded browser run, path tracing was off by 984 % and Metropolis by 180 %; across three seeds Metropolis ended 4 to 15 times closer.
- Files
-
-
assets/js/light-lab/page.mjsWorkers, controls, chart and still frames. -
_sass/_light-lab.scssLab layout. -
bin/capture_light_lab_frames.mjsRenders the still frames and the teaser.
-
- Known limits
-
- Wall time per worker depends on the visitor’s machine; the dashed lines are one laptop’s run.
A reference split by path type
Experimental · Oct 14, 2026
The 24-minute reference on 18 threads splits path space at the camera ray’s first hit and gives each part the estimator that suits it.
How it works, files and sources
bin/make_light_lab_reference.mjs uses light tracing with camera connections where the camera first sees a dry surface (748 million particles), which converges the reflected caustics, and Metropolis restricted to the water with a path-traced brightness correction where it sees the pool (1.63 billion steps). It writes the 80 × 60 luminance grid and the 240 × 180 reference image.
- Files
-
-
bin/make_light_lab_reference.mjsThe split reference renderer. -
assets/js/light-lab/onsen-reference.jsonThe luminance grid behind the error measure.
-
- Known limits
-
- Brightness in the pool is uncertain by a few percent; the walls are much tighter.
Moves in this system: Two renderers, one minute
Design history
Each move follows the loop from Donald Schön's The Reflective Practitioner: frame the problem, make a move, let the result talk back, then reframe. The dimensions are the variables the move changed. Oldest first; new moves are added at the end.
-
A paper desk learns a 3D view
- Frame
- The paper collage introduced the papers, portrait and records quickly, but visitors could not wander through that little world.
- Dimensions
- view (2D or 3D); state shared between views; camera framing
- Move
Added the visible 2D 3D switch and a first Three.js room that copied the desk into a small stage. - Backtalk
- At ordinary screen sizes the furniture disappeared, dropped cards floated, rear turns exposed blank space, and the outside looked like a separate dollhouse.
- Reframe
- Make inside and outside two camera views of the same room, and share the record player and discoveries with the 2D desk.
-
One room, two cameras
- Frame
- The exterior was a hand-matched miniature that drifted from the room it was meant to show.
- Dimensions
- one scene graph or two; camera position; raycast targets
- Move
- Replaced the miniature with the actual room graph under reciprocal cameras. The July 14 checkpoint (
1b07cea4c) accepted the organic shell, the high cliff aperture and live-geometry raycasts across the viewport matrix. - Backtalk
- The same stopped record and empty discovery pile now carried into the room seen from inside or outside. Material realism remained open.
- Reframe
- Keep the room shape, then test stone, lighting, materials and controls on more phones and high-density laptops.
-
An inhabited home from Blender sources
- Frame
- The September brief asked for an inhabited coastal home with connected rooms and a daily rhythm, built as real geometry.
- Dimensions
- authoring tool; rooms; avatars and clips; load order
- Move
- Split the 3D runtime out of
home.jsinto schedule, controller, material and environment modules, and authored six rooms, coastal headlands and five skinned Sirui variants in Blender throughbin/build_coastal_home.py. - Backtalk
- I rejected the first face. It read as a doll with paired hair locks, not my swept-back long hair, exposed forehead, glasses and clean-shaven face.
- Reframe
- Judge likeness in front, profile and full-body renders before lighting, and connect the house to the cliff.
-
Two floors carved into one cliff
- Frame
- A single-level pedestal and arch read as a freestanding stage, not a home carved into a cliff.
- Dimensions
- floors and stair; continuity of the cliff, beach and shoreline; what the cutaway lifts
- Move
-
bin/coastal_section.pycarved two storeys at 0 and 2.6 m into one mainland mesh with a 15-riser stair, andnavigation.mjsandlocomotion.mjsbegan following the exported corridors and treads. - Backtalk
- Hiding the roof could erase the mountain behind the rooms, so only the local ceiling lifts. Walking contacts moved from an approximate stair equation to the exported tread surfaces.
- Reframe
- Judge the section before the lighting. Stairs must be walkable, the mainland continuous and the beach at its foot.
-
Neighbours on the coast
- Frame
- The home sat in an empty landscape.
- Dimensions
- beach depth; animal species and counts; atmosphere
- Move
- Widened the beach from 6.6 to 14 m, with Blender terrain, surf and animal contacts sharing one set of shoreline parameters, and added rabbits, a raccoon, gulls and sandpipers in browser geometry with spray, dust and vapor particles.
- Backtalk
- The coast read as inhabited, but the animals were simple inline shapes without articulated acting or planted contacts.
- Reframe
- Give the animals articulated bodies and contacts on the same terrain the house stands on.
-
Closed solids and fitted activities
- Frame
- The roof split left open boundaries, the camera could look behind unfinished geometry, and activities did not touch their equipment.
- Dimensions
- solid topology; camera envelope; equipment contacts
- Move
- Rebuilt the cave from exact Boolean solids, exported camera envelopes from the finished terrain, furnished the kitchen and gym, and drove coffee and strength sequences from exported equipment contacts.
- Backtalk
- Review renders caught a missing trouser weighting branch, sideways sleeve cuffs, wrongly oriented typing hands, an obsolete stair equation and groundcover crossing the terrain. Each was fixed in the authoring source and the runtime together.
- Reframe
- Keep the full exterior orbit instead of hiding rear geometry with a front-only camera.
-
Walks that keep time when rendering slows
- Frame
- On slow software renderers, walks and gestures stretched with the frame interval.
- Dimensions
- active time against render cadence; catch-up after a stall
- Move
- Route and clip timing now follow active elapsed time, independent of rendering cadence, with spring integration bounded separately.
- Backtalk
- A Linux probe with 4 to 5 seconds between frames kept the authored walking duration and the stair and seated contacts.
- Reframe
- Every new motion system samples the same active clock and never queues catch-up work.
-
A bounded ocean and a driven record
- Frame
- The sea had analytic vertical waves and the record a prescribed rotation; neither held up close.
- Dimensions
- wave components and steepness; footprint filtering; rotor and tonearm dynamics
- Move
- Added choppy Gerstner displacement with analytic tangents, footprint filtering that moves unresolved slope into roughness, exact water Fresnel on indirect specular, and a driven-rotor record with critically damped tonearm coordinates shared by the 2D and 3D players.
- Backtalk
- The ocean grew from 28,800 to 51,200 triangles with no new draw call or pass, and frame intervals stayed at a 16.7 ms median on the desktop test machine.
- Reframe
- Contact light was next, because the old SSAO pass dimmed direct sun as well as ambient light.
-
Contact light on the right term
- Frame
- The full-image SSAO pass multiplied the finished image, dimming sunlight, highlights and emission, and its blur left grey halos.
- Dimensions
- which lighting term is occluded; reconstruction weights; determinism
- Move
- A horizon-based visibility buffer, computed before the beauty pass, now scales only indirect diffuse light inside each material.
- Backtalk
- In a controlled sun-only capture the old pass changed 4.96 % of pixels and the new one 0.00 %; with ambient light only, the new pass changed 13.51 %. Repeated stills became identical.
- Reframe
- Five rendering stages replaced seven. The footer coast keeps its older pass until a matched comparison.
- Sources
-
Fingers that hold the bar
- Frame
- Ghibli’s open hands floated above the pull-up bars, and the contact targets put wrist skin inside the handles.
- Dimensions
- finger joints; wrist target against skin offset
- Move
-
coastal_hands.pyadded twenty finger joints and fitted the two grip clips to anatomical wrist positions beside the bars. - Backtalk
- A skin-offset prototype produced flared wrists and was rejected. In 132 sampled grip frames every digit came within 2 mm of its bar, the deepest intersection was 2.09 mm, and wrist error stayed under 0.33 mm.
- Reframe
- Move the true wrist target, not the skin. The other avatars kept byte-identical files.
-
Eyes that notice, then return
- Frame
- The avatars never acknowledged a visitor, and their eyes could not close.
- Dimensions
- eye and head bandwidth; habituation and cooldown; which clips may move the head
- Move
- Added native eyelids and blink morphs to all five models and an additive layer for blinks, breathing and a short eyes-first glance toward the pointer.
- Backtalk
- The first lids, at the sclera’s depth, left the protruding irises visible and were rejected. Integration review corrected the exported gaze axes, so horizontal attention turns about the vertical axis instead of rolling the head.
- Reframe
- Contact-sensitive clips cancel head motion immediately, and eating and drinking allow the eyes only.
- Sources
-
Animals that plant their feet
- Frame
- The animals were simple inline shapes, with no articulated acting and no planted contacts.
- Dimensions
- contact holds; trajectory continuity; support surfaces
- Move
- Rebuilt the raccoon, gull and sandpiper as articulated Blender masters and sampled their acting from active time. Gull soles were fitted to real perch triangles the same day (
7bd235902). - Backtalk
- Rabbit loops had physically crossed the planting and moved to clear scrub edges. Gulls had sampled grass blades as footing until plants were excluded from perch support.
- Reframe
- Animals, feet and foam all read the same exported terrain.
-
One wind for leaves and particles
- Frame
- Leaves moved by a small material sway, and dust, spray and vapor each drifted by their own rule.
- Dimensions
- spatial coherence of the wind; where a leaf is attached; what shadows see; determinism across frame rates
- Move
- A bounded curl-noise field shared by the room plants and every particle population, with each leaf bending around its actual stem attachment and the same bend in its shadow passes.
- Backtalk
- Only 110 of 132 olive-leaf components had a supporting stem nearby, and one agave component happened to come close to wood, so the automatic resolver is limited to olive leaves. The first shadow version computed the bend inside the depth shader’s displacement guard and lost it (
c7e779a1d). - Reframe
- Run the identical hook on every pass that draws a leaf, or report the mismatch.
-
Skin, water and wind as transport
- Frame
- Skin, the bath and the leaves were art-directed effects. The terminator was hard, the pool bobbed on a sine, and the leaves swayed unattached.
- Dimensions
- diffusion distance per channel; conservation and boundaries; divergence-free wind
- Move
- Integrated three bounded, tested adaptations of published methods. Preintegrated skin diffusion after Christensen and Burley, a conservative height-field pool after Chentanez and Müller, and curl-noise wind for rooted leaves and particles after Bridson and colleagues.
- Backtalk
- The pool held its volume to about 10⁻¹⁴ over a ten-second pulse, and 20 sampled GPU wind velocities matched the CPU within the 8-bit quantization step. A first leaf-shadow compile attempt failed and is kept out of the accepted evidence.
- Reframe
- The pool should move only when something moves it, so drive it from the bather’s hand.
-
Waves from the hand, not a timer
- Frame
- Periodic height pulses made the bath ripple whether or not anyone moved.
- Dimensions
- forcing source; coupling strength; rest without energy
- Move
-
pool-stroke.mjsdrives a slow anatomical hand skim, and the palm’s measured submerged velocity relaxes nearby face velocities inside each CFL step. - Backtalk
- Resting hands now add no energy. First samples, stalls and avatar changes cannot turn a placement jump into a fluid kick.
- Reframe
- Keep the coupling honest in the notes. The water never pushes the hand.
-
Steam and light that know the house
- Frame
- Eighteen vapor points did not read as steam, and diffuse light ignored the walls between rooms.
- Dimensions
- scalar transport; volume compositing around glass; static visibility and bounce
- Move
- Replaced the points with an 18³ thermal density and temperature field rendered by single scattering, and added a CPU-traced static light field with one diffuse bounce and four warm fixtures.
- Backtalk
- The bath shirt read as skin, so it became opaque teal fabric with diffusion off, and privacy no longer depends on steam or angle. Trials with dense steam and misplaced fixtures were excluded from the accepted evidence.
- Reframe
- Keep native refraction and reflections separate from the custom diffuse cache.
-
Twenty-six seconds of shaders, moved off the page
- Frame
- Opening the 3D home froze the page for about 13 seconds, then again when the rooms streamed in and on the first Look around.
- Dimensions
- programs first used in a frame; blocking time per program; time to the first view; changed pixels (must stay zero)
- Move
-
program-warmup.mjscreates every program a frame needs and waits for the driver without blocking, and streamed rooms join the house once their programs are ready. - Backtalk
- Three’s
compileAsynccut blocking from 26 s to 5 s, but each first frame still linked ten programs; a define diff showed the shadow pass is keyed without fog. The result was 0.6 s of blocking reads, readiness from 11.3 to 7.5 s, and 0 changed pixels. - Reframe
- Next was the outside view’s cost, about 2,000 draws a frame.
- Sources
-
A mirror that rendered the house twice
- Frame
- Stepping outside cost about 1,900 draws a frame, and the sea’s reflection was a large share.
- Dimensions
- draws and triangles per pass; mirror resolution; changed pixels
- Move
- The 384 px mirror skips transmissive meshes, and the house and footer canvases stopped multisampling because their composers already multisample off screen.
- Backtalk
- A transmissive mesh made Three redraw the whole opaque scene at full canvas size even inside the mirror. The outside frame fell from 1,934 to 1,574 draws and from 4.45 to 3.57 million triangles, with 0 changed pixels.
- Reframe
- A profile then showed draw submission takes about 11 of the 16 ms outside, and merging static meshes became the next suspect.
-
A look lab instead of a guess
- Frame
- A new tone curve or bloom changes every frame of the home, so the choice needed matched evidence.
- Dimensions
- tone curve; exposure matched to mean brightness; bloom threshold; fabric sheen
- Move
- URL flags became session-long previews (
95f1f27e7). I asked for the call to be made from the matched sheet, and the shipped look is ACES Filmic, bloom above 1.6 and fabric sheen. - Backtalk
- At equal exposure AgX and Neutral rendered 4 % and 12 % darker than ACES, so exposure factors of 1.08 and 1.24 came first. Neutral was truest at noon outside but greyed the evening shadows. Raising the bloom threshold from 0.92 to 1.6 removed the midday haze while the evening sconce still glows.
- Reframe
- Hair highlights, smoother edges and the ocean are now judged against this look.
- Sources
-
A loading bar that only reports finished work
- Frame
- Opening the 3D home showed an empty frame and a status line for 2 to 6 seconds, with nothing to say whether it was working.
- Dimensions
- work known up front; progress source per piece; whether the bar can run ahead; longest main-thread task
- Move
- A five-segment progress bar fills from real events, and the warmup creates programs in batches of six materials with a task boundary between them.
- Backtalk
- The first bar sat at 80 % for 2.2 s because one task created every program. A profile blamed the shadow variants, but timing each section showed
renderer.compile(scene)took 0.1 to 1.6 s in a single task. Batches cut the longest frame from 658 to 196 ms, with the same 48 programs and 0 changed pixels. - Reframe
- A sampling profile is a lead, not a verdict. Time the sections before changing one.
- Sources
-
Starved, not slow
- Frame
- The page still froze in steps of up to 0.7 s while the bar filled, and the plan was to spread out buffer and texture uploads.
- Dimensions
- wall time against CPU time per task; concurrent compiles; programs created after the warmup; readiness
- Move
- The warmup submits a batch only while no more than half the logical cores’ worth of programs are compiling, and gives custom depth materials the side and alpha state the shadow pass copies onto them.
- Backtalk
- The uploads were not the problem. The worst tasks used 3 to 7 ms of CPU across 1.0 to 1.5 s of wall time, because the GPU process ran up to 21 compiles at once on 22 cores and the page waited for a turn. Interleaved runs moved the longest task from 658 to 209 ms and readiness from 4.66 to 5.17 s; an earlier batch-against-batch gain was drift.
- Reframe
- Compare variants interleaved, never batch against batch.
- Sources
-
A highlight that says hair
- Frame
- Long black hair rendered as a dark helmet; roughness alone gave no sign of strands.
- Dimensions
- highlight shape; lobe shift and width; strand direction without tangents; sparkle at the hairline
- Move
- Two shifted strand highlights on every avatar’s hair, with strand directions combed from the crown of the bind pose.
- Backtalk
- All five avatars gained a glossy band across the crown, and a dotted strip at the hairline where fine modeled detail turned the strand direction faster than a pixel. Widening the lobe by the normal’s variance removed most of it.
- Reframe
- Measure sparkle on other bumpy surfaces before applying the same widening everywhere.
-
Where the sea meets the sand
- Frame
- From the coast the sea met the beach in a hard teal-to-sand cut, with no surf.
- Dimensions
- swash width; travel and fade of a broken front; depth that hides the sand; foam lighting
- Move
- A swash band that breathes with each run-up, a broken front that travels in from about 3 m, thin water that shows wet sand, and foam lit like a white surface.
- Backtalk
- A debug tint showed the authored beach profile sits about 1.5 m off the visible waterline on the left. The first foam was a frothy patch, and the old unlit foam glowed white at night.
- Reframe
- A spectral ocean is still the real upgrade.
-
Measure before merging
- Frame
- The outside view ran near 30 fps on an integrated GPU, and the plan was to merge static furniture into fewer draws.
- Dimensions
- draws per pass; which meshes never move; main-thread time per function
- Move
- Measured first. The onsen solver now runs only where the pool can be seen, saving about 2 ms a frame outside.
- Backtalk
- Only 12 of 390 drawn meshes were static, opaque and shared a material, so merging would save 3 draws. A quarter-pixel camera shift changed the image by 1.2/255 on average, so 4× multisampling already holds, and temporal antialiasing would ghost on everything that moves.
- Reframe
- The remaining frame-rate moves change pixels slightly and need an A/B.
- Sources
-
Room to wander
- Frame
- Moving around the home felt limited. Zoom stopped short, and nothing moved the viewpoint itself.
- Dimensions
- meaning of each gesture per view; zoom range; pan radius; how a visitor discovers the gestures; ordinary page scrolling
- Move
- Semantic zoom from room to house to coast, panning within bounded radii, room names on hover, and ⌘/Ctrl+scroll zoom that leaves ordinary scrolling alone.
- Backtalk
- Keeping the visitor’s heading when zooming out framed the house badly, so views settle on authored framings. Room pans of 1.8 m pushed the camera into walls. One Ctrl-held mouse notch zoomed from 4.4 to 2.4 m until whole notches got the gentler rate.
- Reframe
- The loading wait was the next empty space.
-
A sketch while the house loads
- Frame
- The middle of the scene stayed empty while the house loaded, and I asked for something that morphs.
- Dimensions
- what the wait shows; how it changes with each finished piece; motion under a busy main thread; phone legibility; what reduced motion sees
- Move
- Pen strokes in a worker-owned canvas draw the sea, the cliff, the cut-away house and the occupied room, then settle onto the first frame’s edges as it fades in.
- Backtalk
- Edges of the rendered colors drew bedding planes and leaves as heavily as walls. Object outlines weighted by frame coverage, creases above about 57° and the structure tensor’s coherence made legible line drawings, and eight drawings fit in 45 KB.
- Reframe
- The drawings are fixed captures, so rerun the tracer after a room or camera changes.
- Sources
-
A bed someone can sleep in
- Frame
- In my screenshot the sleeping avatar sank about 9 cm into a rigid box pillow, and the cover’s hem cut through the chest by up to 24 cm.
- Dimensions
- contact between body, pillow and cover; five very different bodies; how a filled duvet folds; the empty bed; download size
- Move
- Six bed variants, a made bed and a pillow and duvet fitted to each avatar by an original position-based solver.
- Backtalk
- Blender’s cloth solver exploded into spikes. The first custom drape fell to the floor, then hovered like a stiff plate under a Jacobi solver, until colored Gauss–Seidel batches with a soft bend draped like a filled duvet. One cover for all five would have had to stop below South Park’s chin.
- Reframe
- The rest of the house came next, with wall decoration, the onsen’s blocky stones and the light well.
-
Stones you could sit on, and things on the walls
- Frame
- The onsen’s rim was 22 faceted slabs, and most walls were bare plaster.
- Dimensions
- what each room is for; what the camera actually sees; polygon and draw cost; fit with the person who lives there
- Move
- 24 rounded river stones and decoration for four rooms, each piece placed by casting rays through that room’s camera onto the walls it sees, joined into 14 meshes.
- Backtalk
- The cutaway keeps the whole back wall, so every upper-room piece hangs there. Re-exporting the onsen moved the solver’s measured depth by 0.02 mm, which a five-decimal test caught.
- Reframe
- The bamboo spout is waiting for water.
- Sources
-
A well for the sun
- Frame
- I asked for a hole drilled from the clifftop to bring natural light into the house like a well, and for more interesting light-path simulation.
- Dimensions
- which room and how the shaft leans; hours of sun through the year; the cutaway’s open-roof lighting; cost per frame; the estimator for each light path
- Move
- A shaft into the onsen’s vault leaning 28° toward the noon sun, followed along four light paths. The shadow map carries the sun patch, dust scatters the beam, quasi-Monte Carlo tracing gives the shaft’s radiance, and the patch bounces once into the room.
- Backtalk
- Keeping the roof in the shadow map everywhere made every room gloomy at noon, so only the onsen is roofed, and the open study let noon sun in sideways until a curtain joined the shadow copy. At plausible dust densities the beam was invisible. Only 5.5 % of the sky passes the shaft, so outside midday the onsen is darker than before.
- Reframe
- The grotto or the open onsen is my call. Metropolis sampling belongs where paths are hard to find, such as caustics from the water onto the vault.
-
A front for the cave, and a terrace below it
- Frame
- I asked for floor-to-ceiling windows onto the ocean, a patio, and rooms that are more open or more intimate, designed the way an architect would.
- Dimensions
- where the rock jamb exists; what each camera sees through the glass; where the doors can travel; how deep a usable outdoor room must be; glass reflectance against legibility; the sequence from public to private
- Move
- A bronze-framed glass front inside the cave lip with two sliding doors where the kitchen and lounge cameras look in, a corbelled terrace 3.1 m deep, and cedar alcove screens.
- Backtalk
- The parked kitchen door’s pull touched the dining table, so the glass line moved 6 cm out. Ordinary 4 % glass turned the cut-away view into sky by day, so the front became anti-reflective glass. The olive’s leaves floated free of its branches until it grew twigs.
- Reframe
- The terrace is waiting for a sunset hour in the routine.
- Sources
-
Lights that keep the house’s hours
- Frame
- Keep the light well, and add light installations inside and out that come on at different times of day.
- Dimensions
- what each light is for; when it is on; which surfaces it may reach; cost when it is off
- Move
- A terrace festoon, a fire bowl, a soffit wash, stair step lights and a pool light, each on its own schedule. The eight lamps now follow the routine, and the bedside lamp works as a dawn lamp.
- Backtalk
- Line lights use the exact irradiance of a glowing segment, checked against brute-force integration on 40 random layouts. Bounding boxes keep the festoon off the cliff under the ledge, and by day one uniform skips the whole term.
- Reframe
- The pool light is where the Metropolis experiment meets the house, because its caustics on the vault are the hard paths.
- Sources
-
A coast you can walk up to
- Frame
- With visitors walking around outside, the cliff, plants and waves had to hold up at 5 to 10 m.
- Dimensions
- what 5 to 10 m reveals; which effect causes each artefact; texel and pixel footprint; first-load bytes
- Move
- A camera-following sun shadow on the coast snapped to whole texels, twelve normal-only sea ripples, close-range sandstone detail, and a replanted clifftop of sage scrub, iceplant, grass and Torrey pines that loads after the first frame.
- Backtalk
- Diagonal stripes on the near water survived turning off the shadows and the mirror; they were the swells themselves, one plane wave per octave. The first planting was 90 % bare and its pines read as dead snags. Streaming the planting shrank the first model from 1.65 to 1.00 MB.
- Reframe
- Up close the tide-pool rocks looked dry (wetted the same day in
570bd80c8), and the pines need bark plates.
-
Two renderers, one minute
- Frame
- I agreed to the Metropolis experiment. Show, on light the house actually has, when Markov chain Monte Carlo beats independent sampling and when it does not.
- Dimensions
- paths next-event estimation cannot connect; the sun’s size against a minute’s budget; what equal means; how error is measured; where the reference comes from; how much of a visitor’s processor the page may use
- Move
- A dependency-free renderer of a simplified onsen whose single path sampler is fed independent numbers (path tracing) or mutated ones (Kelemen’s Metropolis), raced in module workers on its own page.
- Backtalk
- After a minute path tracing was off by 984 % and Metropolis by 180 %. Metropolis’s total brightness is itself a rare-event estimate, because the bootstrap usually holds no sun path. Lazily replayed small steps left a few-percent bias until unread coordinates were redrawn, and a slab shortcut created ghost water hits that tests now catch.
- Reframe
- The engine is ready for the pool light’s caustics, and for manifold-based mutations if the race should be won outright.
-
More kinds of plant, from photographs of the real bluffs
- Frame
- I kept the first replanting but found one kind of shrub boring, and asked what the landscape actually looks like.
- Dimensions
- which species grow on the bluffs; their October colors; where each grows relative to the cliff edge; silhouette contrast; bytes for a lazily loaded planting
- Move
- A second planting on its own random stream. Sagebrush and wild oats form the matrix, lemonade berry fills the slope, prickly pear, yucca and rust-red iceplant grow on the rim, daisies line the walks, and one trimmed Canary Island date palm stands by the doorway.
- Backtalk
- The rust iceplant read as a dark tarp because every mat had been wound clockwise, so its normal pointed into the ground; rewound, the old mats brightened to real iceplant green. A rim mat had stretched over a 7 m drop, the first oats looked like confetti, and the first palm like a spider.
flora.glbgrew from 1.18 to 2.2 MB. - Reframe
- If the inland hill needs cover, sparse sagebrush and oats are the cheapest step.
- Sources
Open problems
-
A spectral ocean
The Pacific is ten authored Gerstner components. A spectral FFT ocean would give realistic wave statistics, but WebGL2 has no compute shaders, so the transform needs several ping-pong passes, cascades to hide tiling, and a CPU sampler that agrees with it, because floating objects and arrival rays read the same wave heights.
Candidate moves
- Prototype a 128² FFT height field on a separate page and compare it with the authored spectrum from the same camera.
- Keep the authored components for CPU sampling and use the FFT only for normal detail, so contacts stay exact.
Read
-
Caustics on the onsen vault and floor
Light reaching the vault or the pool floor through rippled water is a chain of specular bounces that must end in a small sun or a small pool light. Next-event estimation cannot aim through the water, which is the case the light lab measured, and the house has no runtime ray tracer.
Candidate moves
- Add the pool light to the lab scene and render its caustics on the vault.
- Precompute a caustic pattern from the lab and project it in the onsen, animated by the solver’s slopes.
- Try manifold exploration or specular manifold sampling in the lab before anything reaches the house.
Read
-
The outside view on integrated GPUs
Outside, about 1,600 draws a frame go through four passes: the view, the shadow map, the opaque copy refraction needs, and the sea mirror. Most objects are interactive, animated, wind-swayed or skinned, so batching could save 3 draws. Every remaining saving changes pixels.
Candidate moves
- A distant, non-refracting onsen material outside, which drops the opaque copy (about 400 draws).
- Skip objects smaller than a pixel (about 180 draws).
- Judge each with a matched A/B before shipping.
Read
-
Temporal antialiasing
Without per-object motion vectors, temporal antialiasing would trade a little shimmer for ghosting on the characters, animals, plants and waves, which move on almost every frame. 8× multisampling cost about 3 ms of GPU time for no visible gain.
Candidate moves
- Motion vectors for skinned and wind-deformed meshes.
- A still photo mode where a temporal filter can accumulate.
-
One wind for the whole coast
The room plants, particles and steam share the curl field, but the clifftop planting sways on its own sine rule and its shadows see the rest shape. Unifying them means patching the planting’s beauty and depth materials with the same field and response, without breaking the divergence guarantee or the shader cache keys the warmup relies on.
Candidate moves
- Bind the clifftop materials to
coastalWindwith the damped response fromplant-motion.mjs. - Give the planting a custom depth material that runs the same bend.
Read
- Bind the clifftop materials to
-
Cloth that moves
The duvets are offline drapes and nothing in the scene is runtime cloth. A curtain in the breeze or a duvet that breathes needs a solver that stays stable within the frame budget, attached to animated bodies.
Candidate moves
- Start from the colored Gauss–Seidel solver in
bin/coastal_bedding.pyand port a small extended position-based version to the browser for one curtain. - Drive it with the shared wind field.
Read
- Start from the colored Gauss–Seidel solver in
-
Light that sees people and plants
The traced light field is static and has one bounce, so the avatar, P and the swaying plants never shade it. Retracing on the CPU every frame is too slow, and probe updates in the style of DDGI rely on GPU ray tracing that WebGL does not offer.
Candidate moves
- Subtract soft analytic occluders (capsules) for the avatar and P from probe visibility.
Read
-
Grotto or open onsen
The light well makes the bath a grotto that gets sun only through the shaft, so outside midday it is darker than the open cutaway was. Both are defensible; it is a taste decision.
Candidate moves
- Keep the grotto.
- Open the onsen and show the well only from the clifftop;
?onsen-light=openpreviews it.
-
A sunset hour on the terrace
The terrace has loungers, a bench and a fire bowl but no activity in the routine. A new activity needs fitted contacts, a camera that looks through a door opening, and a place in the schedule.
Candidate moves
- Add an evening terrace activity with contacts on a lounger or the bench, timed with the festoon and fire bowl.
-
Architectural and Illustrated treatments
In September the three treatments read as too similar, and Realistic worked best. A shader swap alone does not make a different way of seeing the same home.
Candidate moves
- A same-camera comparison where each treatment has its own form, light, mark-making and motion, kept behind
?scene-lab=1.
- A same-camera comparison where each treatment has its own form, light, mark-making and motion, kept behind
-
Close-up character and environment detail
The scene is a stylized browser world. Film-studio character surfacing and close-up environmental detail remain a higher target, bounded by download size and frame budget.
Candidate moves
- Pick one close-up (a face at the desk, the onsen rim) and compare a detail pass against the current frame at the same camera.
Before changing anything
Read first
-
docs/homepage-desk-scene-brief.mdThe active brief. Priority order, interaction contracts, the San Diego routine and the acceptance evidence map. -
artwork/coastal-home/PROVENANCE.mdWhat was authored, generated or referenced, and every rebuild command. -
assets/js/home-scene/CONTACT-LIGHTING.mdContact-light equations, controlled comparisons and measured cost. -
assets/js/home-scene/ONSEN-WATER.mdThe height-field pool, its hand coupling and its numerical evidence. -
assets/js/home-scene/PHYSICS.mdThe ocean spectrum, sea reflection, sun position and record mechanics. -
assets/js/home-scene/SKIN-DIFFUSION.mdThe skin diffusion profile, curvature and its limits. -
assets/js/home-scene/THERMAL-STEAM.mdSteam transport, volume compositing around glass and the light-well mask. -
docs/static-light-field.mdThe traced sky and lamp bounce, its probes and its cost. -
docs/coherent-wind-equations.mdThe curl wind field, leaf attachment and particle integration. -
docs/studio-log-2026-10.mdThe October design moves in full, with dimensions, backtalk and concepts. -
.codex/skills/homepage-desk-scene/SKILL.mdThe agent overlay that routes desk-scene work.
Keep intact
- The homepage opens in 2D on every screen. 3D loads only after a visitor chooses it, a deliberate choice holds for the session, and any WebGL or model failure returns the 2D desk.
- Keep the five adult male Sirui likenesses (long black hair, round glasses, clean-shaven), one at a time and new on each refresh.
- The routine runs on
America/Los_Angeles. Previewing an hour never lets a later clock tick seize the camera, and Now clears both previews. - One connected house, cliff and Pacific. No pedestal, detached arch, image backdrop or scenic photograph; the cutaway lifts only the local ceiling.
- The selected record, its spin, the discovered cards and the avatar survive every mode switch.
- Reduced motion and Pause motion compose still poses. Hidden or offscreen views stop their loops and add no catch-up time when they return.
- Every new material, pass or override draw joins
program-warmup.mjs, or its first draw blocks the page again. - Render-cost changes must change 0 pixels in seeded, paused-clock captures. Look changes need a matched A/B.
- Compare performance variants interleaved, never batch against batch.
- A new camera angle on the kitchen or lounge looks through a door opening, not a mullion.
- Rerun
bin/build_loading_sketch.cjsafter a room’s geometry or camera changes, and the bedding refit after an avatar’s body or sleep clip changes. - Keep the Draco decoder marked binary in
.gitattributes.
Keep these checks green
-
test/visual/desk-scene.spec.jsNonblank WebGL, visible orbit and zoom changes, room continuity, records, recovery, reduced motion and the four viewports.npm.cmd run test:visual:scene -
test/visual/scene-groups.jsonThe scene cases grouped for one-worker runs (scene-core-1, scene-exploration, scene-footer-light and others).node bin/run_visual_scene_group.cjs scene-core-1 --project desktop-1440 -
test/coastal-routine.test.mjsSchedule boundaries, weekends, daylight saving, previews and Now.node --test test/coastal-routine.test.mjs -
test/test_coastal_assets.pyExports and editable sources, room anchors, activity clips, the first scene’s compressed budget and the local Draco decoder.python -m unittest test.test_coastal_assets -
test/test_scene_program_warmup.pyThe shader warmup, its batches and core throttle, and the loading bar’s sources of progress.python -m unittest test.test_scene_program_warmup
Sources
- Jorge Jimenez, Xian-Chun Wu, Angelo Pesce and Adrian Jarabo. Practical Realtime Strategies for Accurate Indirect Occlusion. Activision technical report ATVI-TR-16-01, 2016. www.activision.com/cdn/research/PracticalRealtimeStrategiesTRfinal.pdf
- Khronos Group. EXT_disjoint_timer_query_webgl2, WebGL extension specification. registry.khronos.org/webgl/extensions/EXT_disjoint_timer_query_webgl2/
- Ravi Ramamoorthi and Pat Hanrahan. An Efficient Representation for Irradiance Environment Maps. SIGGRAPH 2001. graphics.stanford.edu/papers/envmap/envmap.pdf
- Zander Majercik, Jean-Philippe Guertin, Derek Nowrouzezahrai and Morgan McGuire. Dynamic Diffuse Global Illumination with Ray-Traced Irradiance Fields. Journal of Computer Graphics Techniques 8(2), 2019. jcgt.org/published/0008/02/01/
- Matt Pharr, Wenzel Jakob and Greg Humphreys. Physically Based Rendering, 4th edition, the chapter on bounding volume hierarchies. www.pbr-book.org/4ed/Primitives_and_Intersection_Acceleration/Boundin...
- Per H. Christensen and Brent Burley. Approximate Reflectance Profiles for Efficient Subsurface Scattering. Pixar Technical Memo 15-04, 2015. research.pixar.com/docs/2015.TechnicalReport.CB.pdf
- Jorge Jimenez, Károly Zsolnai, Adrian Jarabo, Christian Freude, Thomas Auzinger, Xian-Chun Wu, Javier von der Pahlen, Michael Wimmer and Diego Gutierrez. Separable Subsurface Scattering. Computer Graphics Forum 34(6), 2015. www.iryoku.com/separable-sss/
- Xi M. Chen, Timothy Lambert and Eric Penner. Pre-Integrated Deferred Subsurface Scattering. SIGGRAPH 2014 poster. history.siggraph.org/wp-content/uploads/2022/12/2014-Poster-77-Chen_P...
- NOAA Global Monitoring Laboratory. General Solar Position Calculations. gml.noaa.gov/grad/solcalc/solareqns.PDF
- Richard Kelly. Lighting as an Integral Part of Architecture. College Art Journal 12(1), 24–30, 1952. doi.org/10.1080/15436322.1952.11466295
- Alejandro Conty Estevez and Christopher Kulla. Production Friendly Microfacet Sheen BRDF. SIGGRAPH 2017 course on physically based shading. blog.selfshadow.com/publications/s2017-shading-course/imageworks/s201...
- Troy Sobotka. AgX, a picture formation for scene-referred rendering. github.com/sobotka/AgX
- Khronos Group. PBR Neutral tone mapper, Khronos ToneMapping repository, 2024. github.com/KhronosGroup/ToneMapping
- three.js contributors. three.js release r164 (MIT), vendored with its loaders and post-processing passes. github.com/mrdoob/three.js/releases/tag/r164
- Google. Draco 3D geometry compression (Apache 2.0). github.com/google/draco
- Blender Foundation. Blender 4.5 LTS, run through its background Python API. www.blender.org/
- James T. Kajiya and Timothy L. Kay. Rendering Fur with Three Dimensional Textures. SIGGRAPH 1989. doi.org/10.1145/74334.74361
- Stephen R. Marschner, Henrik Wann Jensen, Mike Cammarano, Steve Worley and Pat Hanrahan. Light Scattering from Human Hair Fibers. ACM Transactions on Graphics 22(3), SIGGRAPH 2003. doi.org/10.1145/882262.882345
- Thorsten Scheuermann. Hair Rendering and Shading. Game Developers Conference 2004, ATI Research (slides, course mirror). web.engr.oregonstate.edu/~mjb/cs519/Projects/Papers/HairRendering.pdf
- Anton S. Kaplanyan, Stephen Hill, Anjul Patney and Aaron Lefohn. Filtering Distributions of Normals for Shading Antialiasing. High Performance Graphics 2016. research.nvidia.com/publication/2016-06_filtering-distributions-norma...
- Yusuke Tokuyoshi and Anton S. Kaplanyan. Improved Geometric Specular Antialiasing. ACM SIGGRAPH Symposium on Interactive 3D Graphics and Games, 2019. doi.org/10.1145/3306131.3317026
- Steven Worley. A Cellular Texture Basis Function. SIGGRAPH 1996. doi.org/10.1145/237170.237267
- Mark Finch. Effective Water Simulation from Physical Models. GPU Gems, chapter 1, 2004. developer.nvidia.com/gpugems/gpugems/part-i-natural-effects/chapter-1...
- Eric Bruneton, Fabrice Neyret and Nicolas Holzschuch. Real-time Realistic Ocean Lighting using Seamless Transitions from Geometry to BRDF. Computer Graphics Forum 29(2), 2010. evasion.inrialpes.fr/Publications/2010/BNH10/
- David Algis, Bérenger Bramas, Emmanuelle Darles and Lilian Aveneau. Arc Blanc: A Real Time Ocean Simulation Framework. arXiv:2503.03326, 2025. arxiv.org/abs/2503.03326
- Jerry Tessendorf. Simulating Ocean Water. SIGGRAPH course notes, revised 1999–2004. jtessen.people.clemson.edu/reports/papers_files/coursenotes2004.pdf
- Nuttapong Chentanez and Matthias Müller. Real-time Simulation of Large Bodies of Water with Small Scale Details. Symposium on Computer Animation 2010. matthias-research.github.io/pages/publications/hfFluid.pdf
- Jos Stam. Stable Fluids. SIGGRAPH 1999. www.dgp.toronto.edu/public_user/stam/reality/Research/pdf/ns.pdf
- Ronald Fedkiw, Jos Stam and Henrik Wann Jensen. Visual Simulation of Smoke. SIGGRAPH 2001. graphics.stanford.edu/papers/smoke/smoke.pdf
- Bartlomiej Wronski. Volumetric Fog. SIGGRAPH 2014, Advances in Real-Time Rendering course. bartwronski.com/wp-content/uploads/2014/08/bwronski_volumetric_fog_si...
- Robert Bridson, Jim Hourihan and Marcus Nordenstam. Curl-Noise for Procedural Fluid Flow. SIGGRAPH 2007. www.cs.ubc.ca/~rbridson/docs/bridson-siggraph2007-curlnoise.pdf
- Matthew K. X. J. Pan, Sungjoon Choi, James Kennedy, Kyna McIntosh, Daniel Campos Zamora, Günter Niemeyer, Joohyung Kim, Alexis Wieland and David Christensen. Realistic and Interactive Robot Gaze. IEEE/RSJ IROS 2020, Disney Research. la.disneyresearch.com/wp-content/uploads/root.pdf
- Matthias Müller, Bruno Heidelberger, Marcus Hennix and John Ratcliff. Position Based Dynamics. VRIPHYS 2006; Journal of Visual Communication and Image Representation 18(2), 2007. matthias-research.github.io/pages/publications/posBasedDyn.pdf
- Marco Fratarcangeli, Valentina Tibaldo and Fabio Pellacini. Vivace: A Practical Gauss-Seidel Method for Stable Soft Body Dynamics. ACM Transactions on Graphics 35(6), SIGGRAPH Asia 2016. doi.org/10.1145/2980179.2982437
- Miles Macklin, Matthias Müller and Nuttapong Chentanez. XPBD: Position-Based Simulation of Compliant Constrained Dynamics. Motion in Games 2016. matthias-research.github.io/pages/publications/XPBD.pdf
- City of San Diego Lifeguard Services. Children’s Pool. www.sandiego.gov/lifeguards/beaches/pool
- UC San Diego, SIO 15. Beach walk summary, on the Scripps Sandstone, the Ardath Shale and cliff erosion along the local coast. geowiki.ucsd.edu/sio15/pdfs/beachwalk-summ.pdf
- Scripps Institution of Oceanography. Exposing Failures, on coastal cliff erosion research. scripps.ucsd.edu/news/exposing-failures
- Christopher Alexander, Sara Ishikawa, Murray Silverstein, Max Jacobson, Ingrid Fiksdahl-King and Shlomo Angel. A Pattern Language. Oxford University Press, 1977. en.wikipedia.org/wiki/A_Pattern_Language
- Henry Jenkins. Game Design as Narrative Architecture. In First Person: New Media as Story, Performance, and Game, MIT Press, 2004. web.mit.edu/~21fms/People/henry3/games&narrative.html
- National Park Service, Cabrillo National Monument. Plant Communities. home.nps.gov/cabr/learn/nature/plant-communities.htm
- Khronos Group. KHR_parallel_shader_compile, WebGL extension specification, 2019. registry.khronos.org/webgl/extensions/KHR_parallel_shader_compile/
- Brendan Gregg. Systems Performance, 2nd edition. Addison-Wesley, 2020. www.brendangregg.com/systems-performance-2nd-edition-book.html
- Chris Harrison, Brian Amento, Stacey Kuznetsov and Robert Bell. Rethinking the Progress Bar. UIST 2007. doi.org/10.1145/1294211.1294231
- Takafumi Saito and Tokiichiro Takahashi. Comprehensible Rendering of 3-D Shapes. SIGGRAPH 1990. doi.org/10.1145/97880.97901
- Ken Perlin and David Fox. Pad: An Alternative Approach to the Computer Interface. SIGGRAPH 1993. doi.org/10.1145/166117.166125
- Benjamin B. Bederson and James D. Hollan. Pad++: A Zooming Graphical Interface for Exploring Alternate Interface Physics. UIST 1994. doi.org/10.1145/192426.192435
- Gene M. Amdahl. Validity of the Single Processor Approach to Achieving Large Scale Computing Capabilities. AFIPS Spring Joint Computer Conference, 1967. doi.org/10.1145/1465482.1465560
- Eric Veach and Leonidas J. Guibas. Metropolis Light Transport. SIGGRAPH 1997. graphics.stanford.edu/papers/metro/
- Csaba Kelemen, László Szirmay-Kalos, György Antal and Ferenc Csonka. A Simple and Robust Mutation Strategy for the Metropolis Light Transport Algorithm. Computer Graphics Forum 21(3), 2002. doi.org/10.1111/1467-8659.t01-1-00703
- Eric Veach. Robust Monte Carlo Methods for Light Transport Simulation. PhD thesis, Stanford University, 1997. graphics.stanford.edu/papers/veach_thesis/
- Matt Pharr, Wenzel Jakob and Greg Humphreys. Physically Based Rendering, 3rd edition, section 16.4, Metropolis Light Transport. pbr-book.org/3ed-2018/Light_Transport_III_Bidirectional_Methods/Metro...
- Tzu-Mao Li, Jaakko Lehtinen, Ravi Ramamoorthi, Wenzel Jakob and Frédo Durand. Anisotropic Gaussian Mutations for Metropolis Light Transport through Hessian-Hamiltonian Dynamics. ACM Transactions on Graphics 34(6), SIGGRAPH Asia 2015. people.csail.mit.edu/tzumao/h2mc/
- Wenzel Jakob and Steve Marschner. Manifold Exploration: A Markov Chain Monte Carlo Technique for Rendering Scenes with Difficult Specular Transport. SIGGRAPH 2012. www.cs.cornell.edu/projects/manifolds-sg12/
- Tizian Zeltner, Iliyan Georgiev and Wenzel Jakob. Specular Manifold Sampling for Rendering High-Frequency Caustics and Glints. SIGGRAPH 2020. rgl.epfl.ch/publications/Zeltner2020Specular
Compiled on October 15, 2026 by Anthropic’s Claude from the code, the scene notes, the provenance files and the October studio log, for Sirui Tao to review and extend.