# One-Shot Prompt — Tender (Receipt Paper Physics)

**Simulation**: Paper / Receipt  
**Theme**: Default (Clean Minimal)  
**Generated**: 2026-04-21  
**Name**: Tender

---

## Prompt

Create a single-file Three.js physics simulation of a receipt sheet — a tall, narrow sheet of paper printed with a store receipt, which the user can grab, fold, crumple, and toss in 3D. The paper should hang from two top pins and respond to gravity, wind, mouse/touch interaction, and floor collision. The receipt text must deform with the surface as the paper moves.

### Particle Topology & Physics

- **Grid**: 40 columns × 60 rows (receipt aspect ratio ~1:1.5). On mobile (`navigator.maxTouchPoints > 0` or `window.innerWidth < 768`), reduce to 24×36.
- **Spacing**: 0.12 units between particles, giving a sheet roughly 4.7 × 7.1 world units.
- **Integration**: Verlet integration. Each particle stores `pos` and `oldPos` as `Float64Array(3)`, plus a `pinned` boolean and `mass` (1.0).
- **Update rule**: velocity = (pos − oldPos). oldPos = pos. pos += velocity × damping + acceleration × dt². Damping = 0.99.
- **Fixed timestep**: Physics runs at 120 Hz. Use an accumulator pattern with frame dt capped at 0.033 s.
- **Substeps**: 8 substeps per physics tick (960 Hz physics). dt_sub = physics_dt / 8.
- **Constraint iterations**: 6 constraint-solver passes per substep for stiffness.

### Constraint Layout

For every particle at grid index (i, j), 0 ≤ i < cols, 0 ≤ j < rows:

1. **Structural horizontal**: if i < cols − 1, link (i,j) to (i+1,j), rest = spacing.
2. **Structural vertical**: if j < rows − 1, link (i,j) to (i,j+1), rest = spacing.
3. **Shear up-right**: if i < cols − 1 && j < rows − 1, link (i,j) to (i+1,j+1), rest = spacing × √2.
4. **Shear up-left**: if i > 0 && j < rows − 1, link (i,j) to (i−1,j+1), rest = spacing × √2.
5. **Bend horizontal**: if i < cols − 2, link (i,j) to (i+2,j), rest = spacing × 2.
6. **Bend vertical**: if j < rows − 2, link (i,j) to (i,j+2), rest = spacing × 2.

### Forces

- **Gravity**: (0, −9.81, 0).
- **Wind**: Toggleable. Per-particle wind force based on superimposed sine waves:
  - wx = (sin(t×1.3 + x×0.5)×0.5 + sin(t×2.7 + z×0.3)×0.3) × windStrength
  - wy = sin(t×0.7 + y×0.4)×0.1 × windStrength
  - wz = (cos(t×1.1 + x×0.4)×0.4 + cos(t×3.1 + y×0.2)×0.2) × windStrength
  - Default windStrength = 3.0. Off by default; toggle with W key or UI button.

### Self-Collision

After constraint solving each substep, run one pass of spatial-hash self-collision:
- Cell size = spacing × 1.5.
- For each particle, check all other particles in the same and 26 neighboring cells.
- If two particles are closer than minDistance (0.08) and are not adjacent in the grid (grid distance ≥ 2 in any axis), push them apart equally by 0.4 × overlap along their separation vector. Skip pinned particles.

### Floor Collision

Floor at y = −4.0. If particle.y < floorY:
- Clamp pos[1] = floorY.
- Apply friction: horizontal velocities are reduced (vx *= 0.8, vz *= 0.8).
- Add bounce: oldPos[1] = pos[1] + (pos[1] − oldPos[1]) × 0.2.

### Interaction Model

- **Grab** (left mouse / touch): On pointer down, use Three.js Raycaster to find the nearest particle within 0.5 units of the ray. Create a grab plane perpendicular to the camera through that particle. On pointer move, raycast the same plane and snap the particle’s pos and oldPos to the intersection. On pointer up, release.
- **Orbit**: Right-click-drag (or two-finger drag on touch) orbits the camera. Implemented manually via spherical coordinates (theta, phi, distance) around target (0, −1, 0). Scroll wheel zooms. Pinch-to-zoom on touch.
- **Reset** (R key / button): Animate all particles back to their rest positions over 0.5 seconds with ease-out interpolation.
- **Pause** (Spacebar): Toggle physics pause. Show a centered “PAUSED — press Space” overlay.
- **Instructions**: “Click and drag to grab the paper” appears top-left and fades after 3 seconds.

### Visual Treatment

- **Renderer**: WebGLRenderer with antialias, pixelRatio capped at 2, PCFSoftShadowMap, background #e8e8e8.
- **Lighting**:
  - AmbientLight #ffffff, intensity 0.5.
  - DirectionalLight #fff8ee, intensity 1.2, position (6, 12, 8), cast shadows, 2048×2048 shadow map.
  - HemisphereLight #e0e8f0 / #b0b8c0, intensity 0.3.
- **Ground**: PlaneGeometry(100, 100) at y = −4.02, rotated X −π/2, MeshStandardMaterial #d0d4d8, roughness 0.9, metalness 0.02, receiveShadow.
- **Paper mesh**: PlaneGeometry((cols−1)×spacing, (rows−1)×spacing, cols−1, rows−1), rotated X −π/2. BufferGeometry position attribute updated from particles per frame. MeshStandardMaterial with double-sided rendering, roughness 0.82, metalness 0.0, color #f5f0e8.
- **Receipt texture**: Canvas 512 × 768. Render store name, address, date/time, numbered items with quantities and prices, separator lines, subtotal, tax, total line, payment method, and a thank-you message. Use monospace font. Mapped as CanvasTexture to the material’s `map` property. Anisotropy = renderer max.
- **Normals**: Recomputed each frame via `geometry.computeVertexNormals()` after updating positions.
- **Favicon**: Inline SVG paper icon.
- **Title**: Tender — Receipt Paper Physics.
- **SEO/Social**: meta description, Open Graph, Twitter Card tags.

### UI / HUD Overlay

DOM overlay, CSS positioned:
- Top-left: title “Tender” in small caps, instruction line (fades after 3 s).
- Bottom-left: pill buttons “Wind” and “Reset” with translucent background and backdrop-filter blur.
- Top-right: FPS counter (toggle with I key) showing FPS, particle count, constraint count.
- Center: large pause text when paused.
- Responsive: adapts to mobile with reduced particle count and touch-friendly hit areas.

### Reduced Motion

If `prefers-reduced-motion: reduce`:
- Disable wind forcing.
- Cap grid to 24×36 max.
- Keep physics running but disable ambient animation; maintain interaction.

### Code Structure

One `index.html` file:
1. `<head>`: meta tags, title, inline favicon, CSS for UI overlay.
2. `<body>`: canvas container, loading screen, HUD elements, controls, pause overlay.
3. Module script: import Three.js from unpkg (`https://unpkg.com/three@0.160.0/build/three.module.js`).
4. Inside script: Particle class, Constraint class, physics loop, rendering loop, event listeners, UI bindings.
5. No external files except the Three.js module.

---

## Notes

- Verlet integration chosen for stability with dense constraint networks — it handles large deformation without stiffness explosion.
- 8 substeps × 6 iterations balance accuracy and performance on mid-range hardware.
- Self-collision uses a spatial hash (not exact mesh collision) to keep computation feasible in a single file.
- Orbit camera is hand-rolled to avoid importing OrbitControls, keeping the artifact self-contained.
- Receipt text is procedurally drawn on canvas so it bends naturally with UV deformation — no external images needed.
- Hosting: works on CodePen, Cloudflare Pages, Vercel, or `python3 -m http.server` locally.
