Guidance for Claude Code (and humans) working in this repo.
An interactive, scroll-driven educational web app that teaches emergent dynamics — how simple local rules produce complex global patterns — to non-technical "curious coffee drinkers," using latte art as the familiar entry point. The payoff is a real, interactive WebGL/GLSL latte-pour simulator. It is also a portfolio submission for an Anthropic engineering assignment.
Audience: zero physics background. A reader should leave able to explain, in plain words: simple rules → complex patterns, sensitivity to initial conditions, laminar flow, and double-diffusive convection — and should have played with the simulator.
This should be the best visual presentation of these resources available. If a draft does not clear that bar, iterate until it does. The
presentation-criticagent is empowered to say "not good enough" and dispatch fixes to the specialist agents.
Hard requirements: reading comfort (generous type, strong hierarchy, calm
pacing); accessibility (keyboard nav, semantic landmarks, WCAG AA contrast,
prefers-reduced-motion fallback); the simulator targets ~60fps and degrades
gracefully (static fallback when WebGL2 is unavailable).
- Hero — "the heart in your latte is emergence in a cup."
- A Short History — origins (Italy + Seattle/Schomer, Lupi), microfoam, heart/rosetta/tulip.
- The Micro Level — the "agents": microfoam, crema, density, buoyancy, surface tension → simple local rules.
- The Macro Level — emergence; sensitivity to initial conditions; laminar flow & viscoelastic boundaries; double-diffusive convection.
- The Latte Art Simulator — the real WebGL pour sim (pour height, speed, angle, flow rate, milk temp…).
- Recap — ties simulator behavior back to the named concepts.
- Stack: Next.js 16 (App Router) · React 19 · TypeScript (strict) · Tailwind
CSS v4 ·
motion(Framer Motion) for scroll animation · a faithful raw-WebGL port of the PourMyFriends fluid sim for the simulator · shadcn/ui conventions (cn(), new-york, lucide). Fully static/client; no backend, db, or auth.
app/ App Router (one long scroll page + section anchors), layout, globals.css
research/ /research page — renders the validated research doc (markdown viewer)
components/
sections/ Hero, History, Micro, Macro, Simulator, Recap
demos/ Sections 1–4 teaching micro-demos (SVG / Canvas2D / CSS)
simulator/ Section 5 WebGL fluid sim (raw-WebGL), controls, fallback
nav/ persistent jump-to-section nav + scroll progress
research/ markdown-viewer for the /research page
ui/ shared primitives (shadcn conventions) — buttons, reveal, citations, demo figure
content/
research/ latte-emergence.md (VALIDATED) + citations.ts (typed)
sections/ typed TS content modules (copy + citation refs)
lib/ utils (cn, canvas/observer helpers), hooks
shaders/ GLSL ported from the PourMyFriends approach (raw-string imports)
.claude/agents/ the agent fleet
docs/input/ source material + the TS/React rule files
Defined as Tailwind v4 @theme tokens in app/globals.css. RGB values are
derived directly from the PourMyFriends fluid-shader uniforms:
Token (--color-…) |
Hex | Use |
|---|---|---|
espresso |
#381F0D |
deepest brown / near-black |
espresso-ink |
#241308 |
body text |
crema |
#7A4A24 |
primary accent |
crema-deep |
#40261A |
crema tint |
milk |
#FFFAF5 |
surfaces/cards |
foam |
#EBE0D1 |
borders/dividers |
cream |
#F7EFE4 |
page background |
milk-rim |
#73471F |
warm rim accent / focus ring |
rim-shadow |
#594738 |
shadow band |
specular |
#FFF7EB |
highlights |
muted |
#6B5341 |
muted text (AA on cream) |
Use as bg-espresso, text-muted, border-foam, etc. Always verify AA contrast.
- All app copy must trace to
content/research/latte-emergence.md. No unverified scientific claim ships. Section copy lives in typed TS modules undercontent/sections/*.tsand references citation IDs fromcontent/research/citations.ts— never hardcode prose or citations inline in JSX. - Teaching analogies (e.g. "laminar flow," "microfoam is weakly elastic") are labelled as analogies and not presented as cited measurements — see the research doc §4 and §6.
- The
research-validatoragent ownscontent/research/and citation quality.
Follow docs/input/typescript-practices.mdc and
docs/input/react-practices.mdc (committed to the repo). Key points:
- TS strict;
interfaceovertypefor object shapes; noany(ESLint error → useunknown+ guards); TSDoc on exported types/functions/ props; functions with >2 params take a single typed object param. - React: named exports for components (no default exports except Next
app/**pages/layouts, which require them); noReact.FC; pure components; import hooks from"react";useCallback/useMemoonly where it earns its keep; refs are escape hatches (the simulator is the main legit user). - Tailwind: utility-first with complete static class names (never
string-concatenate classes); inline
styleonly for truly dynamic values (scroll transforms, simulator readouts); usecn()from@/lib/utils. - Naming: files kebab-case (
fluid-simulator.tsx); components PascalCase; hooksuseX; constantsSCREAMING_SNAKE_CASE.
motion (Framer Motion): whileInView/useScroll for reveals and pinned
sequences; IntersectionObserver for nav active-state and lazy-mounting the
simulator. All motion is gated behind useReducedMotion() — reduced-motion
users get static content, no parallax/auto-play. Animation should feel like an
intentional guided presentation, never gratuitous.
A GPU "stable fluids" Navier–Stokes solver, ported directly from
PourMyFriends (raw WebGL): dual-resolution sim/dye grids, a milk+crema dye
model (R = milk, G = crema), MacCormack advection, a circular cup mask, and the
full latte-art display shading (foam absorption, valley/petal darkening, beveled
ceramic rim, specular). The actual .vert/.frag files live in
shaders/glsl/* (imported as raw strings via a Turbopack rule in
next.config.ts); the JS pipeline is in components/simulator/fluid-engine.ts,
with DEFAULT_SIM_CONFIG matching the original's parameters and palette. A React
hook (use-fluid-simulator.ts) drives it: pointer pouring, a scripted demo pour
(so it's alive on first load), offscreen pause, and WebGL feature detection.
Fallback: a static SVG latte when WebGL is unavailable or
prefers-reduced-motion is set. Owned by simulator-engineer.
npm run dev # local dev (http://localhost:3000)
npm run lint # ESLint (flat config)
npm run typecheck # tsc --noEmit
npm run test # vitest run
npm run build # next build (production)
npm run format # prettier --write .Validate with lint + typecheck + test + build after each major step (this is
also the CI gate, .github/workflows/ci.yml). Deployed to Vercel at
https://emergence-in-a-cup.vercel.app/ (zero-config Next.js; production =
public URL). Repo is private; grant access to ehsanik per the
assignment.
Note: in sandboxed shells,
npm installmay need a project-local cache (npm install --cache ./.npm-cache) andnext buildneeds network fornext/font/google.
See AGENTS.md and .claude/agents/*. Specialists: research-validator,
content-writer, simulator-engineer, teaching-visuals-engineer,
motion-designer, design-system-guardian, accessibility-auditor,
presentation-critic (adversarial reviewer / dispatcher).