---
name: fur-plushie
description: Build an interactive 3D plush toy with layered shell fur and fine strands that you blow around with the pointer, with a slot-filled meta prompt, a runnable sample, headless checks and one HTML file.
version: 0.1.0
---

# Fur plushie

## Example

Open `results/plush-pumpkin/index.html` in a desktop browser. A squat orange plush pumpkin with ten round lobes and a hooked green stem sits on a warm peach studio backdrop. Its long fur is combed from the stem down to a whorl underneath and parts along the stitched creases between the lobes. Press and drag on it: a ring-shaped nozzle blows the fur away from the pointer and flattens it, showing the darker roots; let go and it springs back with a small wobble. The toolbar's Blow / Orbit switch decides what a drag does, so on a phone you tap Orbit to turn the pumpkin all the way round, including underneath. Gust puffs air over the whole visible side; the wheel or a pinch zooms. Keys: B blow, O orbit, G gust, + and - zoom, arrows orbit, R reset the view. 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/plush-pumpkin/`.
- `results/plush-pumpkin/report.en.html` and `report.zh-CN.html` hold its headless verification record (console errors, CSP violations, interaction proofs, the phone embed layout and pixel ratio, the fur shimmer bound, reduced motion, audio state, frame rate).

## When to use

- Someone wants a soft, touchable object in the browser: a plush toy, a furry mascot or a fuzzy product, with fur that reacts to the pointer and settles again.
- You need one self-contained HTML file that runs inside a strict sandbox (no network, no storage) and stays clean in a small square embed on phones.

When not to use: realistic hair grooming of characters, long hair that collides with itself, scenes that need model files or photo textures, or server-side rendering. This Skill calls no model; an agent does the work locally.

## Inputs (slots)

Required: `subject`, `fur`. Optional, with defaults: `palette`, `background`, `interaction`, `sounds`, `ui`, `extraPrompt`. Every value is a string of at most 2000 characters. `references/prompt-slots.md` explains each slot; `assets/prompts/examples/plush-pumpkin.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-plush
cd my-plush && 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 images to the coding agent.
2. Start from the reference app (`new-project`). It already has the parametric lobed body, the shell fur and the instanced strands with their anti-aliasing, the air field with springback, the stem, the orbit camera with framing above the toolbar, the Blow / Orbit HUD, synthesized sound, the `window.__scene` capture API and the single-file build. Change `src/plush/body.ts` for another shape and the colours in `src/plush/shells.ts` for other fur.
3. Between `dev` and `build:single`, iterate: read `references/lessons.md` before changing the fur shaders or the HUD, and run `shoot` after each round to compare its screenshots (blow before, during and after, orbit, zoom, gust, the 345×345 embed at three pixel ratios) 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

- The fur is a visual layer, not a simulation of individual hairs: strands do not collide with each other or the body, and the air field is a grid of springs over the surface.
- One plush object only; there is no floor, no shadow and no second object to blow onto.
- The low tier (phones) draws 16 fur layers and 12,000 strands instead of 32 and 48,000; phones are verified under headless touch emulation only, not on devices.
- With `prefers-reduced-motion: reduce` there is no breeze, no orbit coast and the fur settles without a wobble; capture mode ignores the setting.
- `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.
- **Is the fur made of image textures?** No. The strands of every fur layer are cut out of hashed cells in a shader, and the guard hairs are instanced ribbons; nothing is loaded.
- **Can I make another plush toy?** Yes: replace the parametric surface in `src/plush/body.ts` (keep the uv layout so the fur and the air field follow it), the stem in `src/plush/stem.ts` and the colours in `src/plush/shells.ts`; keep the subject group named `plush` (the layout check reads its screen box) and update the framing sizes in `fit()` in `src/components/Stage.tsx`.
- **Why does the toolbar have a Blow / Orbit switch?** On touch screens one finger cannot both blow and turn; the switch makes a drag do exactly one thing, and `shoot` proves the two never mix.
- **Why is there no drei `Environment`?** It ships a CDN preset table; the sandbox blocks network access. The studio light is a few panels in a private scene, prefiltered 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.
