# Jelly — Three.js soft-body slime

A single-page, zero-build Three.js scene: a glossy translucent slime blob sitting on a
studio floor. It is simulated as a soft body, so it dents when you poke it, squishes when
you drag it, wobbles and settles with damping, and squashes on impact when you drop it.

## Run it

Any of these work:

- **Serve the folder** (recommended):
  ```bash
  python3 -m http.server 8000
  # then open http://localhost:8000
  ```
- **Open `index.html` directly** in a modern browser. The page first tries the vendored
  copy (`vendor/three.module.min.js`); if the browser blocks local module loads
  (e.g. `file://`), it automatically falls back to the pinned CDN build of
  `three@0.160.0` (internet connection required in that case).

three.js r160 is vendored in `vendor/` and pinned via CDN in the fallback, so there is no
build step and no package manager.

## Controls

| Action | Effect |
| --- | --- |
| **Click / tap** the blob | Poke it — makes a dent that jiggles back |
| **Drag** on the blob (mouse or touch) | Grab and squish it; release to let it wobble back |
| **Multi-touch** | Multiple fingers can squish different spots at once |
| **Drop** button (or `D` / `Space`) | Launches the blob up and drops it from height |
| **Reset** button (or `R`) | Returns the blob to a calm rest state |

An FPS readout sits in the bottom-right corner.

## What's inside

- `index.html` — the whole scene: CSS, UI, simulation and shaders, in one file.
- `vendor/three.module.min.js` — vendored three.js r160 (MIT).

Implementation notes:

- **Soft body** — an icosphere (2,562 welded vertices, 5,120 triangles) simulated with a
  fixed 120 Hz substep: per-vertex shape springs to a moving anchor, edge springs for
  propagating jiggle, velocity damping relative to the anchor, floor collision with
  restitution and friction, and a volume-preserving correction so squashes bulge instead
  of collapsing. Laplacian smoothing keeps deformations gooey rather than creased.
- **Interaction** — pointer rays hit the deformed mesh; a click applies a falloff
  impulse along the view ray (poke), and dragging applies a position-based grab so the
  surface follows your finger. Touch is handled through pointer events with
  `touch-action: none`.
- **Material** — custom shader with wrapped diffuse lighting, back-lit subsurface
  scattering on thin edges, fresnel environment reflection, a softbox top reflection and
  glossy clearcoat-style highlights. The blob is alpha-blended so it stays translucent.
- **Shadow** — a soft two-layer contact shadow that tracks the blob's footprint,
  spreads and fades with height, and darkens as it settles onto the floor.
- **Performance** — no shadow maps, no transmission pass, no post-processing; physics
  costs roughly 0.3 ms/frame on a desktop CPU. Device pixel ratio is capped and is
  automatically reduced if the frame rate drops below ~50 fps. Targets 60 fps on
  mid-range phones, including portrait layouts (the camera fit adapts to aspect ratio).
