# Paradise Pond: Handoff

A relaxing, no-fail canoe game set on Smith College's Paradise Pond, starring **Senda Bear**, the college's new mascot. This document captures everything decided so far so work can continue in Claude Code (or anywhere else) without re-deriving context.

Status: **the game is playable in 3D (`play/`, three.js) with top, side and first-person views.** It has rhythm paddling with generative music, wildlife with habits and a photo album, and four friends with small errands. The 2D SVG map (`play-2d/`) still runs the shared game but shows none of the new content. Code lives in a Vite + TypeScript project.

---

## 1. TL;DR

- **Concept:** drift a canoe around Paradise Pond, and flowers bloom as you pass. No score, no timer, no losing. Meditative.
- **Look:** soft, flat, outline-free "storybook" style. Warm pastel palette. Film-like motion (easing, overlapping action, parallax).
- **Built so far:**
  1. `scenes/hero-side-view`: animated side view of Senda paddling (the "hero" / title-screen candidate).
  2. `scenes/map-top-down`: animated top-down map traced from the *real* pond layout, with an autopilot canoe lap, blooming lily pads, ducks, and a flower counter.
  3. `play-2d/`: the map ported to TypeScript modules and made playable (section 6.4).
  4. `play/`: the same game in three.js with three camera views (section 6.5).
  5. Gameplay loop (section 6.6): rhythm paddling and music, wildlife and a photo album, friends and errands.
- **Next milestone:** playtesting on real phones for feel and performance (see Roadmap).

---

## 2. Verified facts about Smith (do not re-guess these)

Source: Smith College article *"A Fierce New Face for the Smith Bears"*, John MacMillan, published Feb 18, 2026.
<https://www.smith.edu/news-events/news/fierce-new-face-smith-bears>

- Teams are now the **Smith Bears** (the name was chosen in spring 2025 after a community process with 1,200+ submissions). Early in this project I wrongly said "Pioneers"; that is outdated.
- Mascot: **Senda Bear**, named for Senda Berenson, who came to Smith as director of physical training in 1892 and introduced basketball; the first intercollegiate women's basketball game was hosted at Smith in March 1893.
- New logo (announced Jan 2026, designer Jaison Williams): a bear head in Smith blue and gold plus a secondary bear-paw mark. The paw is shaped like the outline of **Paradise Pond, island included**, and both the eyes and paw are tilted **18.92 degrees** (a nod to 1892 to 1893).
- Athletics explicitly did **not** want a "warm and fuzzy, cartoonish" bear for the official mark. Our game Senda is deliberately softer and friendlier. Confirm this is acceptable if the project ever becomes official.
- **Trademark note:** we did *not* copy the official logo. Senda here is an original character. Do not ship the official logo or marks without permission.
- Real-world map reference: `assets/reference/paradise-pond-google-map.jpeg` (Google Maps labels the water "Mill River Reservoir"; students call the pond Paradise Pond).

---

## 3. Decision log

| # | Decision | Why |
|---|----------|-----|
| 1 | Game theme = Paradise Pond canoe + flowers (Smith's botanic garden / Lyman Conservatory as flavor) | Meditative, campus-specific, fits "relaxing" |
| 2 | Mascot = Senda Bear (after fact-check, see section 2) | Correct, current, and gives a character |
| 3 | Chose **Style A: soft storybook** out of 3 options (A soft, B top-down paw, C vintage navy/gold print) | Owner picked A |
| 4 | Tried "Zelda-like" cartoon/cel-shaded variant with bold outlines, HUD, thick line work | **Rejected.** Owner preferred the original outline-free look |
| 5 | Requirement: **fluid, filmic bear motion** | Led to the rig + overlapping-action approach (section 6) |
| 6 | Senda's face: went from angled line-brows (read as miserable) -> arched brows (still off) -> soft fur-patch brows (still off) -> **no brows** | Brows kept misreading emotionally. Eyes with highlights, rosy cheeks and a smile carry the expression better |
| 7 | Added a **Smith-blue scarf** | Brand-color nod, and gives secondary motion (trailing tail) |
| 8 | Canoe must sit **in** the water, not on it; later raised "a little" | Added waterline occlusion overlay, foam, reflection and bow/stern rings; baseline bob offset is now `+3` (was `+6`) |
| 9 | Top-down map was first an **invented paw-shaped pond** (from the logo article), then rebuilt from the **real Google Maps layout** | The owner supplied the real map; the paw was a guess |
| 10 | Map is **portrait, 380x540 viewBox**, traced by eye from the screenshot | Mobile-first; matches the reference aspect |
| 11 | Autopilot lap, bloom-on-proximity, ducks, cloud shadows | Shows the core loop before adding controls |
| 12 | Migrate to a Claude Code project | This handoff |
| 13 | Vite + TypeScript, still SVG, no framework | Smallest step that keeps the look pixel-identical; renderer can be swapped later |
| 14 | Tap/drag sets a destination; arrow keys/WASD paddle directly | Works one-handed on phones and on desktop |
| 15 | Canoe starts at rest by the dock; the autopilot lap is a toggle | Player is in control; autopilot kept as attract mode / demo |
| 16 | Shore contact slides the canoe along the bank; unreachable taps are dropped after 3 s without progress | No-fail: Senda never gets stuck or punished |
| 17 | Move to three.js with switchable top / side / first-person views; keep the SVG version at `play-2d/` | Owner wants to switch views; 2D kept for comparison and as a no-WebGL fallback |
| 18 | 3D keeps the flat storybook look: unlit colour for flat surfaces, hard-stepped toon ramps (highlight / base / shade) for rounded shapes, no real-time shadows or outlines | Same palette and "layered flat shapes" feel as the 2D art |
| 19 | Senda rebuilt in 3D from rounded primitives, no eyebrows, blue scarf ribbon | Needs owner review, like the 2D face did |
| 20 | Owner feedback: "the boat looks too big, the bear looks ugly, the mechanics are boring; calming but not boring" | Led to 21 to 26 |
| 21 | Canoe scale 0.7 -> 0.3 (~13 map units); speed, stroke, collision, cameras and props rescaled; top view follows the canoe with zoom | Believable size on the pond |
| 22 | Senda redesigned to match the hero scene's front-facing face; three styles to choose from (`?senda=a|b|c`, `/lab/`) | The first 3D face (eyes on the sides, snout) read as off |
| 23 | Owner chose three mechanics: wildlife & photo album, rhythm paddling & music, friends & small errands (not flower journal & seasons) | "Calming but not boring": gentle goals, light skill, no failure |
| 24 | Rhythm paddling: the beat is Senda's own stroke (2.6 s, about breathing pace); good timing adds glide and a note; misses cost nothing | Skill without stress; the music always matches the picture |
| 25 | All audio synthesized in WebAudio (no files) | Zero downloads, consistent soft timbre, easy to tune |
| 26 | Friends are original animals (otter, rabbit, duck, beaver), not Smith marks | Warm characters without trademark issues |
| 27 | Owner picked Senda style C (plush proportions, Smith-blue sun hat) as the default; A and B stay in `/lab/` for comparison | Owner choice |
| 29 | Ducks (and the lost duckling, the white ducks, lily pads) move out of the canoe's way (`hullPush` in `canoe.ts`) | Owner: "I am boating right through the ducks" |
| 30 | Sound starts on any tap or key (iOS accepts only some events as gestures), plays through the iPhone silent switch, and has an audible bed (pad, water, a soft note every stroke) before any rhythm play; Sound on/off label and a confirmation chime | Owner heard nothing |
| 31 | The turtles' log is solid (a capsule in `world.obstacles`, handled like the shore in `canoe.ts`); moved to (326, 360), between the autopilot's two lanes | Owner: the log needs collision |
| 32 | Paddling sounds natural: a soft water swish on every stroke, silence on off-beat taps (was a pitched "bloop"), notes only for on-beat taps and flow; dragonfly is a faint wing flutter (was a buzzy sawtooth); kalimba overtone softened | Owner: "what is this weird sound when I paddle?" |
| 33 | Chord bed rebuilt: sine voices an octave up, no detuning, chords crossfade instead of sliding in pitch, much quieter | Owner heard "an engine sound that varies with speed": the detuned low triangle pad beat like a motor and its pitch glides on each chord change (every two strokes while paddling) sounded like revving. Measured with an in-browser analyser: pad removed 12-16 dB below 400 Hz |
| 34 | No continuous noise bed; the pond is heard as separate laps and drips with quiet between. A soft melody plays on its own (short phrases with rests); rhythm paddling makes it fuller. Chord bed swells on each change, then breathes out to a whisper | Owner: "there's like a wind sound, it doesn't go away! is it supposed to be music?" The water noise read as wind, and music only played on rhythm taps, so there was barely any |
| 35 | Owner switched Senda to style A (plush, no hat) | Owner choice (replaces 27) |
| 36 | All water sounds (laps, drips, paddle swish, splashes, plops) scaled to 10% (`WATER_LEVEL` in `music.ts`, about -20 dB) | Owner: doesn't want to hear the water, or only barely |
| 28 | Top view zooms by pinch (two fingers), on-screen + / − buttons, mouse wheel and +/- keys; range frames 60 to 600 map units | Owner asked for zoom on mobile |

---

## 4. Art direction / design system

**Principles:** flat shapes, no outlines, no gradients or blur (all "depth" comes from layered flat shapes and opacity), warm pastels, gentle contrast, nothing harsh or fast.

**Palette (hard-coded hex; artwork does not react to dark mode):**

| Role | Hex |
|------|-----|
| Sky (dawn) | `#FBE9D0` |
| Sun / halo | `#F6C177` / `#F9D59A` |
| Far hills / near hills | `#A8C9A0` / `#86B08A` |
| Water / wave lines / foam | `#8EC5CF` / `#B9E0E6` / `#F2FBFC` |
| Bear fur / inner ear | `#7A5238` / `#A87550` |
| Muzzle / nose / eyes | `#D9B48F` / `#3A2618` / `#2A1B10` |
| Cheeks | `#E59A8A` (60% opacity) |
| Scarf (Smith blue nod) | `#3A64C8` |
| Canoe hull / highlight | `#B5714A` / `#D89A6E` |
| Paddle | `#C9A26B` |
| Lily pad / notch | `#5E9C6B` / water color |
| Bloom petals / center | `#F4A7C0` / `#F6E27A` |
| Map ground / shore rim | `#CFE3B8` / `#EAF2D0` |
| Island grass / sand rim | `#A5C48B` / `#EFE3B8` |
| Fields / mow stripes | `#A9D19A` / `#9CC98C` |
| Garden maples | `#E28D4B`, `#D9603B`, `#F0B04A` |
| HUD pills | `#2A3F5F` at 80% opacity, text cream `#FFE9A8` |

**Character:** round bear, big soft ears, cream muzzle, closed-mouth smile, blink every 2.5 to 5.5 s, eye highlights that hide mid-blink, no brows.

Style C (navy `#1F3A5F` / gold `#D9A93A` vintage print) was not chosen for gameplay but is a good candidate for a **title screen or menus**.

---

## 5. File map

```
paradise-pond/
  handoff.md                       this document
  CLAUDE.md                        pointer for Claude Code
  README.md                        quick start
  package.json                     `npm run dev` / `test` / `build`
  index.html                       gallery linking everything
  play/index.html                  3D game page (three.js)
  play-2d/index.html               2D game page (static SVG artwork + module entry)
  src/game/                        renderer-independent game (section 6.4)
  src/svg/                         SVG renderer + 2D entry
  src/three/                       three.js renderer, cameras, Senda rig + 3D entry (sections 6.5, 6.6)
  src/audio/music.ts               synthesized music and sound effects (section 6.6)
  lab/index.html                   Senda style lab: the three styles side by side (?angle=front|three|side&paddle=1)
  public/favicon.svg               favicon
  test/                            vitest: geometry, spline, layout parity, steering simulations
  vite.config.ts                   multi-page build; copies handoff/concepts/assets into dist
  shared/theme.css                 recreates the CSS variables the Claude widget host used to provide
  scenes/
    hero-side-view/index.html      FINAL animated side view (single file, ~200 lines)
    map-top-down/index.html        FINAL animated top-down real-layout map (single file)
  concepts/
    style-a-soft-storybook.svg              chosen direction (early static sketch)
    style-b-paw-pond-top-down-SUPERSEDED.svg   invented paw pond, kept for history
    style-c-vintage-print-NOT-CHOSEN.svg    possible title-screen style
    style-a-cartoon-adventure-REJECTED.svg  outlined cel-shaded variant, rejected
  assets/reference/
    paradise-pond-google-map.jpeg  the real layout used to trace the map
```

**Not included** (superseded, only described here): intermediate hero versions v1 to v3 (differences: v1 had angled brows and the hull drawn *on* the water; v2 added the waterline and arched brows; v3 had fur-patch brows and the boat raised), and the first animated paw-pond map.

---

## 6. What's built and how it works

Both scenes are single-file HTML + inline SVG + vanilla JS, driven by one `requestAnimationFrame` loop that calls `render(t, dt)`.

### 6.1 Shared runtime behavior
- **Time model:** `t` accumulates `dt * scale`. `scale` eases toward a target (1 normal, 0.3 slow motion, 0 paused), so pause and slow-mo *ramp* instead of snapping. This felt much more filmic.
- **Deterministic motion:** almost everything is a **closed-form function of `t`** (sums of sines with phase offsets). Only things that genuinely need memory (bloom state, ripple pool, blink timer) use `dt` or state.
- **Loop stops** when paused and fully eased out; restarts on Play.
- **Accessibility:** `prefers-reduced-motion` starts paused on a rendered still frame; an `sr-only` heading describes the scene; buttons have `aria-label`s.

### 6.2 Hero side view (`scenes/hero-side-view`)
- **ViewBox** 380x240. Layers back to front: sky, sun (+halo), clouds, far hills, near hills, dragonfly, water, sun reflection, back waves, **boat group** (torso, head, ears, scarf, hull), **hull waterline overlay** (wavy top, foam line, faint hull reflection, bow/stern rings), **paddle group** (`boat2`, same transform as the boat), blade-submersion rect, front waves, ripples, lily pads.
- **Paddle cycle** `P = 4.6 s`, phase `ph = 2*pi*t/P`.
  - Blade tip: `tx = 264 + 22*cos(ph)`, `ty = 196 - 12*max(0, sin(ph))^2` (squared so entry/exit are smooth, no kink).
  - Hand: `hx = 222 + 0.4*(tx-264)`; `hy = ty - sqrt(90^2 - (hx-tx)^2)` (fixed shaft length 90, so the paddle never stretches).
  - Blade rotation = `-atan2(tx-hx, ty-hy)` in degrees. Shoulder position is computed through the torso rotation so the arm always connects.
- **Overlapping action** (the "filmic" part): torso lean `2.4*sin(ph+0.6)`, head lags `3*sin(ph-0.5)` plus slow look-around, ears lag further, scarf tail flutters at independent frequencies (2.3 / 1.7 / 2.0 rad/s). Boat bobs and rolls on its own slow swell.
- **Sitting in the water:** the hull is occluded by a wavy water-colored path at `opacity 0.8` with `WL = 174`; the paddle group is drawn *after* this overlay so the shaft is in front of the waterline, while a semi-transparent water rect at `y = 200` makes only the blade look submerged.
- **Depth/parallax:** camera drift `cam = 6*sin(0.11 t)` moves hills at 0.35x / 0.7x, sun at 0.15x. Waves scroll at speeds proportional to depth (`v = 2.5 + (y-148)*0.16`), wrapping horizontally.
- **Extras:** dragonfly on a Lissajous path with smooth direction flip (`tanh`), sun-reflection shimmer, lily pads that drift/bob/rotate, expanding ripple rings on paddle entry / mid-stroke / exit.

### 6.3 Top-down map (`scenes/map-top-down`)
- **ViewBox** 380x540. Source image is 1092x1632, so `K = 0.348` converts image coords to viewBox coords. Water outline, island, trail and canoe route are all authored as **image-space point lists** then scaled.
- **Shapes:** point lists become smooth closed paths via **Catmull-Rom -> cubic Bezier** (`smooth()`), and are also **sampled into polygons** (`sample()`) for point-in-polygon tests.
- **Procedural placement** (all seeded, deterministic via an LCG `rnd()`):
  - `wet(x,y)` = inside water polygon and not inside island; `wetM(x,y,m)` also checks 8 points at radius `m` (a margin).
  - Trees, shore flowers, glints, decorative pads, and bloomable pads are all placed by rejection sampling against `wet/landM/zoneBad` (road, right-hand buildings, fields are excluded zones).
  - Bloomable pads are placed at offsets (+-15, +-18) from evenly spaced points along the canoe route, so the route *always* passes near them.
- **Canoe route:** closed Catmull-Rom spline through ~40 waypoints (basin around the island, down the reservoir arm and back, across the south, into the left inlet pocket). **Arc-length table** (`tab`, `sAt(d)`) gives constant speed; speed is modulated with the paddle stroke: `d = V0*t - 0.25*V0*(P/2pi)*cos(ph)` (closed-form integral of `V0*(1+0.25 sin ph)`), `V0 = 17`, `P = 3.4 s`.
- **Canoe:** drawn in local coordinates facing +x, group transform `translate rotate scale(0.7)` (scaled down so it fits the narrow arm). Heading = spline tangent + small yaw wobble; lateral sway; single-blade paddle sweeping on one side (`a = 0.7*cos(ph)`; blade shrinks and fades slightly when "lifted"); V-shaped wake; stern ripples every 0.7 s; head sways; scarf tail flutters.
- **Blooming:** if the canoe is within 20 units of a bud, it blooms (`target = 1`), a sparkle plays, and the HUD counter increments. Bloom animates by `b += (target-b)*(1-exp(-dt*2.5))`. Buds close again after 40 s once the canoe is more than 60 units away, so laps stay rewarding.
- **Life:** 3 ducks on slow elliptical orbits (heading from the orbit tangent), 18 shimmering glints, cloud shadows drifting across land and water, lily pads bobbing, dashed shoreline trail, flower-lined shores, maples in the Japanese Garden zone.
- **HUD:** flower counter pill (top left) and area banner "Paradise Pond" (bottom), both soft navy pills. Labels: "Japanese garden" only.

### 6.4 Game logic and 2D map (`src/game/`, `play-2d/`)
Port of `scenes/map-top-down` into modules. The static artwork is the same SVG markup; the procedural layout is identical to the original (checked in a browser: beds, trees, glints, pads and outlines match exactly).

| Module | Role |
|--------|------|
| `geometry.ts` | `smooth` (Catmull-Rom -> Bezier path), `sample`, `inPoly`, closest point on polygon, angle helpers |
| `path.ts` | `Loop`: closed spline, arc-length table, `sAt`, `nearest` |
| `rng.ts` | seeded LCG |
| `data.ts` | traced point lists, palette bits, dock position |
| `world.ts` | `buildWorld()`: water/island polygons, `wet`, `shore`, and all seeded placement (same `rnd()` order as the original) |
| `canoe.ts` | steering physics, shoreline collision/sliding, `toWater` for tap targets |
| `rig.ts` | `pose()`: paddle sweep, head, scarf, wake, sway, all scaled by paddling effort |
| `autopilot.ts` | pure pursuit around the lap with a line-of-sight check |
| `state.ts` | bloom state per pad |
| `game.ts` | `Game`: canoe, blooms, autopilot, tap targets, ripple events; `Clock`: eased pause/slow-mo |
| `input.ts` | pointer (tap/drag) and keyboard, with a renderer-supplied screen-to-map function |
| `src/svg/scene.ts` | SVG renderer: builds layers, updates per frame |
| `src/svg/main.ts` | 2D loop and buttons |

- **Steering:** heading follows a critically damped spring toward the desired heading (`k = 5`, turn rate capped at 1.8 rad/s). Speed eases toward the target (faster up than down, so she glides to a stop). When the target is behind, she turns mostly in place first. Arrival slows over the last ~45 units.
- **Paddle driven by speed:** stroke phase advances at 45 to 100% of the 3.4 s cycle depending on requested speed; `effort` (0..1) eases in and out. At rest, the paddle lifts and angles back, the wake fades, stern ripples slow down, and Senda floats and looks around slowly. The stroke surge (`1 + 0.25 sin ph`) from the original is kept.
- **Shore:** three probe circles (radius 4) along the hull are pushed out of land and the frame. While in contact, the *desired* heading is bent along the shore if it points into it, so she slides around corners. (Rotating the hull directly fought the steering and could wedge her in concave corners.)
- **Taps on land** are moved to the nearest water point 6 units off the shore. If distance to the target stops shrinking for 3 s, the target is dropped.
- **Autopilot:** chases a point up to 26 units ahead on the lap, but only one reachable in a straight line with 5 units of shore clearance. If it stalls for 2.5 s, it restarts from the nearest point on the lap. A lap takes a bit longer than the original's ~72 s.
- **Tests** (`npm test`): geometry and spline units; layout counts (11 buds, 16 decorative pads, 18 glints); a 3-minute random-steering run that asserts the hull never touches land; autopilot laps from five start points (dock, basin, arm, south shore, inlet) that must bloom all 11 buds in under 100 s.

### 6.5 3D game (`play/`, `src/three/`)
Same `Game` as the 2D version; only rendering, cameras and input mapping differ. World units are map units: map (x, y) is world (x, height, y).

| Module | Role |
|--------|------|
| `palette.ts` | palette from section 4, plus pre-blended versions of the 2D map's translucent layers |
| `materials.ts` | `flat()` unlit colour; `toon()` with a 3-step ramp (highlight where the surface faces the light, base, darker far side) calibrated so "base" is the exact palette colour; stencil helpers so grass patches and the trail only draw on grass |
| `terrain.ts` | water sheet; ground extruded with a pond-shaped hole (bank walls); island; shore rim, shallows and beach bands; fields, roads, buildings, dock, trail; glints, cloud shadows; distant hills, sun and clouds for low views |
| `props.ts` | trees (canopy, trunk, flat shadow), flower beds (instanced), lily pads with opening flowers, ducks; hides trees between the camera and Senda |
| `senda.ts` | canoe (lens-shaped hull, inside/outside colours, gunwale, thwart) and Senda (body, head, ears, muzzle, eyes with highlights, cheeks, smile, scarf ribbon, arms, paddle); wake and hull shadow |
| `effects.ts` | ripple rings, sparkle sprites, destination marker |
| `cameras.ts` | one perspective camera with three framings; switching eases over 1.4 s from the current shot to the live new one |
| `bear.ts` | Senda's body and face, from a style config (three styles) |
| `creatures.ts` | duck family, heron, turtles and log, fish/koi, dragonfly, frog |
| `friends3d.ts` | Otis, Mabel (with tea table), Bruno (with dam), errand items, beacons, the lost duckling |
| `scenery.ts` | reeds and cattails, willows, wave lines, gabled buildings, garden lanterns, boathouse |
| `main.ts` | renderer, loop, raycast steering, views (1/2/3/V), zoom (wheel, +/-), paddle button, camera and album, speech bubbles, errands list |

- **Views:** *Top* frames the whole map straight down (fov 30, so it reads like the 2D map). *Side* sits low on the paddle side so Senda faces screen-right like the hero scene; it backs off to fit the canoe across narrow portrait screens and rises into a higher shot when the ground under it is land. *First person* is from her eyes, pitched down enough to see the bow and paddle; her head is hidden when the camera is inside it. Follow cameras use a smoothed heading, not the raw one.
- **Steering in 3D:** taps are raycast onto the water plane. A tap on the sky (low views) steers toward that direction, 80 units out. Keys work the same in every view.
- **In the water, not on it:** the water is an opaque sheet at height 0 and the hull dips below it, so the waterline and the submerged paddle blade come for free (lesson 6).
- **Motion:** paddle kinematics are 3D but driven by the same phase/effort as 2D (sweep fore-aft, dip below the waterline, top hand at chin height); torso lean, head lag, ear lag and scarf flutter on separate frequencies; boat bob/roll/pitch on slow swells; blinks every 2.5 to 5.5 s.
- **Fog** only in low views (sky-coloured, for depth like the hero scene's paler far hills); cloud shadows only from above; hills, sun and clouds only in low views.
- **Performance:** about 150 KB gzipped for three.js. Not yet profiled on real phones (headless checks run on a software renderer).

### 6.6 Gameplay loop (renderer-independent logic in `src/game/`)
- **Scale:** canoe drawn at 0.3 (`rig.ts` `CS`), cruise speed 10 map units/s, stroke period 2.6 s, collision probes at ±4.5 with radius 2. Top view follows the canoe (frames ~180 units by default), kept inside the map. Zoom with a two-finger pinch (the first finger's steering is undone when the second lands), the on-screen + / − buttons (top view only), the mouse wheel or the +/- keys.
- **Rhythm paddling (`game.ts`):** `stroke()` grades the tap against the catch (blade entry, phase 0 of the stroke): perfect within 0.12 s, good within 0.26 s, one try per stroke. Good strokes add glide (`canoe.surge`) and flow (+0.10 / +0.16). Flow raises cruise speed (`canoe.drive` 0.8 to 1.35) and fades slowly (faster after about two missed strokes). From rest, the first tap starts the rhythm. With nowhere to go, a stroke paddles straight ahead. Paddle button (orbiting dot, top = catch, gold arc = flow) and Space.
- **Music (`src/audio/music.ts`):** has no continuous noise (a looping water hiss read as wind); instead water laps every 1 to 4 s (more often while moving) and the odd drip, a self-playing melody (phrases of 3 to 6 plucks from the pentatonic walk, 0.55 to 1.1 s apart, 2.5 to 5.5 s rests; longer and brighter with flow), and a chord bed that swells in on each change and breathes out to 20%. It starts on the first gesture (tries pointerdown, pointerup, touchend, click and keydown until the AudioContext runs; a toast says "Tap anywhere to turn on the sound"). On iPhone it sets `navigator.audioSession.type = "playback"` or plays a silent looping `<audio>` so the silent switch doesn't mute it. Every stroke plays a soft water swish (filtered noise, panned to the paddle side); off-beat taps are silent; `level()` meters the output (used by checks). Water bed (filtered noise with swell), birdsong every 6 to 16 s, a quiet sine chord bed through F, Dm, Bb, C an octave up, crossfading every two strokes (every 7 s when resting); never detuned and never pitch-gliding, which read as an engine, kalimba-like plucks on good strokes (F major pentatonic random walk; perfect adds a harmony), a water droplet for off-beat taps. Flow layers: bass above 0.3, arpeggio across the stroke above 0.6, high bell above 0.85. Bloom run, errand fanfare, wildlife sounds, shutter. Mute is remembered.
- **Wildlife (`wildlife.ts`):** duck family circling the basin (ducklings follow a canoe that drifts within 22 units under speed 5 for 2.2 s; leave if it rushes above 13 for 1.5 s); heron on three bank perches (flies to the farthest when approached within 12, or within 28 at speed); turtles on a solid log in the river arm (slide off within 14, return after 10 s once you're 30 away); fish leap near the canoe every 5 to 12 s (koi near the garden); a dragonfly lands on the bow after 4 s of stillness; a frog on a pad near the dock croaks and hops off within 7.
- **Photos (`album.ts` + `main.ts`):** camera button (P) captures the view and credits the largest subject that is in front of the camera, within 120 units, inside the central 85% and at least 3.5% of the view height. 13 album moments (9 wildlife, 4 friends), with hints for missing ones; saved in localStorage.
- **Friends (`friends.ts`):** drift within 16 units at low speed to chat. Otis (dock): his sun hat floats east of the island. Mabel (garden bank): glide slowly over an open lily to pick one. Hazel (the mother duck): lead the lost duckling from the western inlet back to the family. Bruno (river arm): three floating twigs. Delivery completes the errand, shows the result (hat on Otis, lily on the table, bigger dam), plays a fanfare and adds a thank-you photo to the album. Progress is saved; items for unfinished errands reset on reload.
- **Camera during chats:** the side camera swings to frame Senda and the speaker, and Senda looks at them.
- **Tests:** rhythm timing/flow/beat, each wildlife habit, the album, each errand end to end, save/restore.

---

## 7. Lessons learned

**Process**
1. **Verify facts before designing around them.** The mascot was wrong on the first pass; a single source link fixed it. Ask for or search real references early.
2. **Ask for the real reference early.** The invented paw-shaped pond was fun but wrong; the real map (supplied later) changed the whole layout. Get the ground truth before building the map.
3. **Show style options before committing.** Three small sketches settled direction in one turn; the outlined "Zelda" variant was a cheap experiment that proved the owner's real preference.
4. **Iterate on small visual decisions with the owner.** Faces are subjective; expect several rounds.

**Design**
5. **Brows are risky.** Both line and patch brows read as angry/worried/off at small sizes and mid-motion. Removing them and relying on eye highlights + cheeks + smile was more robust. Judge expressions at rest, at slow motion, and mid-blink.
6. **"On" vs "in" the water is a layering problem**, not a drawing problem: draw the hull, then a wavy semi-opaque water shape over its lower part, then re-draw anything that should stay above (paddle shaft).
7. **Filmic = overlapping action + asynchronous rhythms.** Different body parts on different phases and frequencies matter more than adding more animation.
8. **Ease everything, including pause/slow-mo.**
9. **Scale the actor to the world.** The canoe had to be scaled to 0.7 to navigate the narrow river arm; decide actor scale against the tightest channel early.

**Technical**
10. **Closed-form time functions beat stepped simulation** for looping ambient motion: trivially pausable, scrubbable, and reproducible.
11. **Arc-length parameterize any path** you want constant speed on.
12. **Smooth-curve the outlines and sample them for geometry tests.** One source of truth gives both the drawn shape and the collision/placement test.
13. **Seeded RNG** keeps procedural layout stable between reloads (essential for tuning).
14. Avoid keyframe-style "pop" paths: use `sin^2`-style easing (zero derivative at entry/exit) for contact events like paddle dips.
15. **Pursuit targets need line of sight with a margin.** A look-ahead point that is "visible" over water but grazes a headland will pin a hull in a concave corner. Check clearance along the sight line, and recover from stalls by going back to the path, not further along it.
16. **Flat 2D art translates to 3D with unlit colour + hard toon steps.** Calibrate the light so the middle step is the exact palette colour. Pre-blend the 2D map's translucent layers into solid colours.
17. **Decals over a hole need a mask.** Grass patches drawn above the pond covered the water in 3D; a stencil written by the ground's top face keeps them on grass.
18. **Follow cameras need occlusion handling.** Rise over land rather than pulling in (pulling in near the shore gave cramped shots), and hide trees on the line of sight.
19. **Scale everything against the actor.** A canoe sized to fit the narrowest channel in a top-down map looked huge in 3D; real proportions plus a follow camera read better.
20. **Match the reference's face, not just its parts.** The hero scene's Senda faces forward; putting eyes on the sides of a sphere looked wrong even with the same pieces.
21. **Tie music to the action.** Using the paddle stroke as the beat keeps rhythm play and animation in sync for free.
22. **"In the water" needs a dry cockpit.** An opaque water sheet through a sunken hull fills the canoe. An invisible gunwale-shaped stencil mask stops water (and anything floating) drawing inside it, and pads drift aside as the hull passes.
23. **Photo detection needs an in-front test.** Projected points behind the camera can land on screen.
24. **Host-provided CSS variables don't exist outside Claude.** `shared/theme.css` recreates them; scenes use hard-coded hex for artwork so they render identically anywhere.

---

## 8. Known issues and caveats

- **Map is traced by eye** from a screenshot; it is approximate. For accuracy, trace from real geodata (e.g. OpenStreetMap; note ODbL attribution requirements) or re-trace over the reference image.
- **Dock position is a placeholder** at the south shore; the real boathouse/launch spot is unknown. Ask the owner.
- The **canoe route through the narrow inlet neck/pocket is tight**. In `play/` the hull is collision-checked and slides, but the autopilot may brush the bank there. The original scene is unchanged.
- Collision uses three probe circles, not the true hull outline, so the bow tip can slightly overlap the painted shore rim.
- Hero scene and map use **different canoe geometry, paddle timing (4.6 s vs 3.4 s) and scale**; unify when moving to a shared rig.
- The SVG scenes and `play-2d/` update many SVG attributes per frame via `setAttribute`.
- **3D (`play/`):** not yet tested on real phones. Every tree is its own mesh (fine for ~50). The water outline ends in straight edges where it leaves the traced map (south end of the arm, west end of the inlet), which low views can show; the canoe can't go there (frame clamp). The ground is flat, so hills exist only as distant scenery. The dragonfly and wave lines from the hero scene aren't in 3D yet.
- The 2D version (`play-2d/`) runs the shared game (so its canoe is now small) but draws none of the wildlife, friends, rhythm HUD or album.
- Sound and rhythm feel are tuned on paper; headless checks only confirm the output level. Needs a listen on real devices (especially iPhone with the silent switch on).
- Frame rate on phones is untested (headless runs on a software renderer at ~3 fps). Scenery adds ~100 draw calls.
- Errand items for unfinished errands reset on reload (the friend's introduction is remembered). Fine for these scenes; consider Canvas 2D / PixiJS or CSS transforms for the full game, especially on low-end phones.
- `Math.random()` drives blink timing (non-deterministic); everything else is deterministic.
- Of 16 candidate slots along the route, only **11 bloomable buds** actually get placed (the rest fail the water-margin test, mostly in the narrow arm and inlet). Widening the margin tolerance, trying more offsets, or hand-placing pads would give a more even rhythm. A headless smoke test (stub DOM, 120 s simulated) confirmed both scenes run without errors and about 19 blooms fire per 2 minutes.
- Bloom counter is in memory only (resets on reload). No persistence, no audio, no tests.
- The map's portrait 380x540 layout is tall. In `play/` it is capped to the viewport height (letterboxed), and the map swallows touch scrolling so drags steer.
- Icons in the two `scenes/` load from the Tabler webfont CDN; offline they will be blank (labels remain). `play/` uses inline SVG icons.
- Label mismatch: banner says "Paradise Pond", Google says "Mill River Reservoir". Fine for a game, but decide deliberately.

---

## 9. Roadmap (suggested order)

1. **Make it playable (P0). Done** (collectibles became the photo album and errands): Pointer/touch steering with smoothed heading and speed (critically damped), paddle animation driven by actual speed, stationary idle behaviors (float, look around), **shoreline collision by sliding along the polygon** (reuse `wet()`), keep the no-fail rule. Buds become true collectibles; add a small collection screen.
2. **Project scaffolding (P1). Done for the map** (no `audio` yet; the hero scene isn't ported): Vite + TypeScript (or plain modules if preferred). Extract: `geometry` (smooth, sample, inPoly), `path` (spline + arc length), `rig` (paddle kinematics, overlapping action), `world` (procedural placement), `render` (SVG or Canvas), `input`, `state`, `audio`. Add unit tests for geometry/spline. Pick one renderer and stick with it.
3. **Audio (P2). Done** (section 6.6):: slow pentatonic chimes on bloom, soft water/ambient bed, mute toggle; start audio only after first user gesture.
4. **Title screen and menus (P3).** Use the 3D side view (or the hero scene) as the title backdrop, Style C (navy/gold) for menus.
5. **Senda expression sheet (P4).** A few poses/expressions (content, sleepy, surprised, joyful) and idle variations; keep the no-brows rule unless revisited.
6. **Content (P5).** Regions to explore (Japanese Garden edge, reservoir arm, inlet pocket), flower species per region, time-of-day and seasons (fall leaves, spring blossoms), weather, gentle daily "garden" progression.
7. **Fidelity (P6).** Replace the eyeballed outline with traced geodata; place the real dock and landmarks; consider a labeled minimap.
8. **Polish (P7).** Performance profiling on low-end phones, reduced-motion and contrast audits, haptics on bloom, save/restore progress.

---

## 10. Open questions for the owner

1. Where is the real boathouse / launch point on the pond?
2. Target platform: mobile web, desktop web, installable app? (affects renderer and input)
3. Is this **official** Smith work? If so, get sign-off on Senda's design and any use of Smith names/marks; otherwise keep it clearly unofficial.
4. Is there a goal beyond blooming flowers (collection book, seasons, gentle story), or purely ambient?
5. Sound: yes/no, and any preferred musical mood?
6. Should Senda stay this soft and friendly, given athletics' "not cartoonish" stance for the official mark?

---

## 11. How to run

```bash
npm install
npm run dev            # Vite; open /play/ or the gallery at /
npm test
npm run build          # -> dist/ (Cloudflare Pages: build `npm run build`, output `dist`)
```

Scenes are also just files: double-clicking `scenes/*/index.html` works, apart from the icon webfont needing internet.

---

## 12. Suggested next prompt for Claude Code

> Read `handoff.md`. Playtest `play/` on a phone-sized viewport: tune rhythm windows, flow decay and music levels, and check frame rate; lower draw calls (instance trees and wave lines) if needed.
