# Portable beach objects and activity integration

`objects/beach` contains geometry, generated woven textures, materials and classes for a family beach. Copy the folder alongside any compatible engine and inject the engine namespace into constructors. Its only imports are local. It requires `THREE`, `ObjectState` and `OBJECT_PROTOCOL === 1`; a supplied `WindField` is optional. There are no network assets or browser-only canvas requirements.

```js
import * as api from './engine/index.js';
import {BeachUmbrella, SunLounger} from './objects/beach/index.js';
const umbrella = new BeachUmbrella(api, {radius:1.4, color:0x638b8e, wind});
const chair = new SunLounger(api, {backAngle:Math.PI/6});
scene.add(umbrella.root, chair.root);
```

All classes expose `root`, `properties`, position/rotation/quaternion/scale getters, `setPosition`, `update`, `getBounds`, `serialize`, `restore` and idempotent `dispose`. Units are metres and radians. `serialize` captures constructor properties and root transforms. Reconstruct with `new Class(api, data.properties).restore(data)`; `restore` itself applies transforms. Each instance owns all its GPU resources; shared wind uniforms are borrowed and are not disposed by an object.

| Class | Constructor properties | Specific methods and state |
|---|---|---|
| `BeachUmbrella` | `radius=1.42`, `height=2.42`, `color`, `accent`, `open=true`, `tilt=.10`, `wind=null` | `setOpen(boolean)`, `setTilt(radians)`; clamps tilt to ±.35 rad |
| `SunLounger` | `width=.72`, `length=1.94`, `seatHeight=.43`, `backAngle=π/6`, `color` | `setRecline(radians)`, `setOccupied(boolean)`, `getReclinePose({height=1.76,build=1})` for a Person |
| `VolleyballCourt` | `length=16`, `width=8`, `netHeight=2.20`, `lineColor` | `setScore(left,right)`, `conformToTerrain(terrain)`; score is metadata for a consumer scoreboard |
| `BeachBall` | `radius=.16`, `colors`, `style='beach'` | `setKinematic(boolean)`, `throw(Vector3)`, `velocity`; `update(dt,time,{terrain,gravity=9.81,restitution=.72})` integrates a released ball |
| `Sandcastle` | `radius=.60`, `height=.62`, `seed=11`, `wind=null` | `setProgress(0..1)` scales construction height |
| `BeachTowel` | `width=.9`, `length=1.9`, `color` | Woven stripes, small surface folds and fringe |
| `SandBucket` | `radius=.13`, `height=.22`, `color`, `filled=true` | `setFilled(boolean)`; includes a separate shovel mesh and handle |

Every class also accepts `id` and `name`. Positive dimensions are validated. The lounger points its head end along local -Z and foot end along +Z. The volleyball court length runs along local X, and its net spans local Z. The 2.20 m default net is a recreational configuration, not a claim of regulation adult competition dimensions.

The umbrella canopy has eight separately colored panels, radial ribs, supports, a valance and metal pole. Its cloth receives the supplied shared wind field through wind vertex data. The same deformation patches its shadow-depth material, so visible cloth and shadows move together. Frames stay rigid. Towels and lounger fabric are held against their supports. Nets contain actual openings built from thin cords, rather than an opaque or translucent rectangle.

The court boundary can be conformed after positioning the court; it samples the actual terrain triangles. Court geometry does not modify the terrain. For a steep site, applications should provide a level playing surface. Balls support external kinematic choreography or a simple gravity/bounce update; the portable ball does not decide which person should hit it.

## Application layer

`shell/world/beach-activities.js` exports:

```js
const activities = createBeachActivities(api, {landscape, wind, scope});
scene.add(activities.root);
activities.update(dt, time);
```

`landscape` must supply `terrain.heightAt(x,z)`, `seaLevel`, and `beach.{shoreX,z}`. The root is at world origin; every ground anchor samples the terrain. The court is centered at `(shoreX-65, beach.z-25)` on the dry west side. Child ball play, sand play, parents, lounge chairs and umbrellas occupy separate nearby areas. There are 16 people, including four children, and 14 props. This composition can be included in the flight scene or used independently.

Returned values are `root`, `people`, `props`, `players`, `children`, `diggers`, `sunbathers`, `court`, `volley`, `childBall`, `contacts`, `tossContacts`, `inspectionPresets`, `update(dt,time)`, `dispose()` and `getStats()`. Passing `scope` registers cleanup; direct repeated disposal is also safe. `getStats` returns people/children/prop counts, mesh drawable count, geometric triangle count, active contact indices, current time and disposal status. Render passes and shadows can multiply actual rendering work beyond this geometry count.

Four stationary adult volleyball players alternate cross-net contacts at 1.45 second intervals. Contact anchors are shared by the ball and two-arm IK. Between contacts the ball follows a gravity-consistent parabola. Anticipation/jump pulses occur around the corresponding player's contact; the other participants hold ready poses. Children exchange their ball at .95 second intervals using separate shared contact targets. Cycle-boundary handling avoids floating-point leg-index discontinuities. Sand play uses arm IK and articulated crouches; lounging bodies use the matching chair pose.

`shell/scenes/beach.js` exports `beach(context,settings)`. Context uses `{engine,scene,camera,scope,signal}`. Settings use `weather` (or `preset`), `hour`, `windStrength`, `windDirection` in radians, `water`, `reduced` and optional `telemetry`. The scene owns a shared `Environment`, so weather transitions, water, fabric, daylight and rain use the same environment state. It returns `environment`, `controls`, `light`, `water`, `wind`, `landscape`, `activities`, `inspectionPresets`, `inspect(name)`, `setWind(values)`, `update`, and `configure`. Inspection keys are `court`, `volleyContact`, `shore`, `children`, `waterline`, `sunbathers`, and `overview`. Reduced motion freezes activity and shared wind time.

## Evidence and limitations

`tests/beach-object.html` is both a visible inspection page and a deterministic behavior harness. Numerical checks cover contacts, terrain anchors, net clearance and disposal. Browser inspection should separately examine anatomy, clothing, chair support, hands, ball trajectories, net visibility and wind/shadow agreement.

Current people and props are procedural art. Volleyball uses a repeatable recreational rally, without scoring AI, fatigue, tactical positioning or body collision simulation. The actor route API is not used to slide players beneath the ball: their stations are fixed and jumps are vertical. Wind changes cloth deformation, while the ball's scripted contact path is kept stable for synchronization.

## Chrome fixture inspection, 2026-09-07

The confirmed Chrome extension instance rendered the court, fixed volley contact, child catch, sand play, lounger and umbrella views without console warnings/errors. The initial prop review passed 64 assertions; the completed body review now passes 104. Visible refinements made during inspection include shaped barefoot heels/toes, a continuous upper-arm skin surface, age-appropriate default child hair, a closer sand-play camera, actual tower-patting hand targets, and a smaller irregular sand mound. Reclining people stay supported by their chair backs, with no visible torso/chair intersection in the inspected views. The general style remains procedural and visibly stylized.

Static parts are combined by material within each fixed transform group; cloth wind attributes and custom depth materials, lounger back joints, umbrella tilt/open groups, bucket fill visibility and world interaction anchors stay separate. Towel fringes are instanced. Before the new expressive heads and body rebuild, this reduced the beach composition from 262 to 65 mesh drawables with 214,426 triangles. The final composition including the new bodies and heads has 142 mesh drawables and 550,074 geometric triangles. In that earlier prop comparison, observed rendering work fell from roughly 950 to 322–324 calls and median CPU frame time from roughly 24.5 to 11–12 ms. Those are local observations with the full environment and shadows, not cross-device performance guarantees.

Engine revision env4 restored person and umbrella ground shadows in the fixture. Umbrella valances visibly change with the shared wind setting; the flag uses the same wind service. The initial waterline review exposed angular water/sand intersections and oversized foam patches in the shared WaterSurface. The env7 shoreline recheck still exposed large angular water/sand wedges and broad foam patches at frozen time zero. That engine-owned issue remains under separate correction; the body/prop review does not count it as visually accepted.


Root integration update: the shoreline defect recorded during env7 review was isolated to coarse water-wave tessellation. The v020-review2 runtime uses 0.5×0.75m shore water cells, retains terrain refinement/reflections and passed direct close-view review at two wave phases. See verification/shoreline-v2-summary.json (relative to docs/) for evidence and limits.
