---
name: cocktail-physics
description: Build an interactive photoreal cocktail on white that spins, sloshes, sips, drains and refills, with a slot-filled meta prompt, a runnable sample, headless checks and one HTML file.
version: 0.1.1
---

# Cocktail physics

## Example

Open `results/pink-coupe/index.html` in a desktop browser. A clear coupe of bright pink drink stands on white. A lemon wheel drops onto the rim, bounces and splashes. Drag to spin the glass around its stem: a flick keeps it coasting, the drink lags behind and sloshes in springy waves, and four ice cubes bob on the surface. Click or tap to take a sip (the level drops and the surface jumps and ripples), press and hold to drain it, and press R or the Refill button to pour it back up. As the drink empties, the ice settles on the bottom; an empty glass refills itself after a moment. All sound is synthesized after your first click; the button at the top right mutes it.

- The file is one self-contained HTML page with no network access and no storage, built from `assets/pink-coupe/`.
- `results/pink-coupe/report.en.html` and `report.zh-CN.html` hold its headless verification record (console errors, CSP violations, interaction proofs, levels, audio state, frame rate).

## When to use

- Someone wants a playful product-style scene of one drink or object in a glass: physical glass on a white backdrop, a liquid that reacts to every gesture, sound, and no text beyond a hint.
- You need one self-contained HTML file that runs inside a strict sandbox (no network, no storage).

When not to use: scenes that need model or texture files, a full fluid simulation, or server-side rendering. This Skill calls no model; an agent does the work locally.

## Inputs (slots)

Required: `drink`, `glass`. Optional, with defaults: `garnish`, `ice`, `interaction`, `background`, `extraPrompt`. Every value is a string of at most 2000 characters. `references/prompt-slots.md` explains each slot; `assets/prompts/examples/pink-coupe.json` is the slots file of the example.

## Workflow

```
node <skill>/scripts/fill-prompt.mjs --slots slots.json --lang en --out prompt.md
node <skill>/scripts/new-project.mjs --out my-drink
cd my-drink && npm ci
npm run dev                      # live preview; stop it with Ctrl+C
npm run build:single             # → dist-single/index.html
npm run shoot -- --file dist-single/index.html   # needs local Chrome
```

1. Fill the slots and render the meta prompt (`fill-prompt`). Give the prompt and any reference photo to the coding agent.
2. Start from the reference app (`new-project`). It already has the lathed glass, the liquid volume with its surface height function, the slosh springs, the ice buoyancy, the garnish drop, the splash droplets, the pour stream, the studio environment, synthesized sound, the `window.__scene` capture API and the single-file build. Change the profile in `src/geometry/profile.ts`, the colours in `src/components/Liquid.tsx` and the garnish for the new drink.
3. Between `dev` and `build:single`, iterate: read `references/lessons.md` before changing the materials, and run `shoot` after each round to compare the beat screenshots with the reference.
4. Deliver `dist-single/index.html` once `shoot` passes. `references/verification.md` describes every check; `references/hosting.md` the sandbox it must pass.

## Language

`fill-prompt --lang en|zh-CN` picks the prompt language (`zh` is accepted; anything else falls back to English). The scene UI follows `?lang=en|zh-CN` (default English). The CLIs print English by default; `new-project` and `fill-prompt` take `--lang`, and the app scripts (`notices`, `inline-build`, `shoot`, `record`) take `--lang` or the `SCENE_LANG` environment variable.

## Keys

None. Nothing in this Skill calls a model or a network service. `npm ci` downloads the pinned packages from the public npm registry.

## Failure boundaries

- Real-time WebGL: the liquid is a height field driven by springs and waves, not a fluid simulation; it cannot pour over the rim or break into a spray.
- The ice is opaque with a glassy finish (transmissive objects do not refract each other in three.js), so it does not bend the drink behind it.
- Phones run the low tier (coarser surface grid, half-resolution refraction, no multisampling) and are verified under headless touch emulation only, not on devices.
- `shoot` and `record` need Node 22.4 or newer and a local Chrome; `record` also needs ffmpeg. Headless capture has no audio, so listening is a manual check.

## FAQ

- **Does it need an API key?** No.
- **Why one HTML file?** It can be opened from disk, hosted anywhere and embedded in a sandboxed iframe without a server.
- **Can I use another glass?** Yes: edit the outer, inner and stem points in `src/geometry/profile.ts`. The volume table, the waterline and the ice floor all follow the inner profile.
- **Why is the backdrop brighter than white in the code?** The neutral tone mapper never quite reaches white; the backdrop sits above 1 and a last pass scales it to exactly white, so the bloom threshold stays above it.
- **Why is there no drei `Environment`?** It ships a CDN preset table; the sandbox blocks network access. `src/components/ProceduralEnv.tsx` builds the studio light in code.
- **Where are the third-party licences?** `npm run build:single` embeds `THIRD_PARTY_NOTICES.txt` as an HTML comment in the file's `<head>`; never strip it.
