# Squash & Stretch Spring Motion — `spring-motion@1.1.0`

Bouncy, jelly-like motion: things crouch, jump, stretch and land with a squash and wobble, shown on three glossy balls.

The classic cartoon animation principles as ready-made motion: anything can hop, stretch as it flies, squash when it lands and wobble back into shape. There is also an exact spring (for overshoot-and-settle moves like a pop-in) and a squash helper that keeps the shape's volume believable. Use it to make logos, icons, characters or buttons feel playful.

- Kind: motion; status: published; digest sha256:6f156d4a1c7e533b02d66c24fff519c6734be925d4cfdccf920ee786f34c5cf6
- Role: Motion source: spring values and squash/stretch scales over time.
- Implementation: js; entry `src/spring-motion.js`
- Files: https://aipixelwizard.com/registry/spring-motion/1.1.0/<path> (`LICENSE`, `asset.json`, `demo.js`, `index.html`, `src/spring-motion.js`)
- Manifest JSON: https://aipixelwizard.com/api/v1/visuals/spring-motion/versions/1.1.0
- Runnable demo: https://aipixelwizard.com/registry/spring-motion/1.1.0/index.html (open over http; it imports sibling packages by relative path)
- Dependencies (exact): `apw-runtime@1.0.0` (demo) — Demo harness and colour helpers. src/spring-motion.js has no dependencies.

## Exports
- `spring(t, { from?, to?, velocity?, stiffness?, damping?, mass? }) => number` — Exact damped spring position at time t (seconds).
- `squashStretch(stretch) => [scaleX, scaleY]` — Volume-preserving scale pair.
- `hop(time, { period?, height?, squash?, airtime?, wobble?, offset? }) => { y, scaleX, scaleY, phase, inAir }` — Periodic hop cycle with anticipation, stretch, landing squash and wobble.
- `cyclePhase(progress, cycles?) => number` — Loop phase helper.

## Parameters
- `hops` (integer 1–8, default 4) — Hops per clip
- `jump` (number 0.2–1.6, default 1) — Jump height
- `squash` (number 0–0.6, default 0.35) — Squishiness
- `wobble` (number 3–30, default 9) — Wobble: Lower numbers wobble longer after landing.
- `stagger` (boolean, default true) — Hop out of step
- `colorA` (color, default "#ff4f8b") — Colour 1
- `colorB` (color, default "#ffc93c") — Colour 2
- `colorC` (color, default "#1ed6a3") — Colour 3
- `background` (color, default "#15121f") — Background

## Composition
- Inputs: Time in seconds (or progress) plus options.
- Outputs: Numbers: an offset (y) and scales (scaleX, scaleY). Apply them to any object.
- Coordinates: y is height above the ground in your units. Scale around the object's bottom centre so it squashes onto the floor.
- Lighting: None.
- Timing: Closed-form math: no state, frames can be computed in any order.
- Works with: kinetic-title, rolling-counter, bouncing-balls
- Apply scale with the pivot at the bottom of the object, not its centre.

## Output and loop
- 1080×1080, 30 fps, 4 s, procedural
- Seamless loop: yes — The hop is periodic and the clip holds a whole number of hops.
- Verified 2026-10-03 in Chrome 154.0.8037.97 headless on Linux 7.0.0-38-generic (x64), Node 22.14.0 (Canvas 2D); deterministic: yes; loop check: Frame 0 vs end state (progress 1): mean |Δ| 0.0000, max |Δ| 0 of 255.

## Use it
New project: Each frame compute const s = hop(frame / fps, { period: 1 }) and draw your object at y = groundY − s.y with scale (s.scaleX, s.scaleY) around its bottom centre.

Existing project: Copy src/spring-motion.js. Feed your existing time variable into hop() or spring() and apply the result to the existing object's transform.

Reuse ideas:
- Pop a number or label in with spring(t, { from: 0, to: 1 }) as its scale (see Minimal Animated Number Reveal).
- Make a logo or app icon hop in a product video.
- Use squashStretch on a Three.js mesh's scale for 3D jelly characters.

## Limitations
- The hop is stylised animation, not physics. Use Bouncing Balls for real collisions.

## License
MIT. Copyright (c) 2026 AI Pixel Wizard. Released under the MIT License. See LICENSE in this folder.

Provenance: Original code written for AI Pixel Wizard in October 2026. Demo rebuilt in October 2026 to the catalog quality bar (docs/QUALITY.md).

