unease 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +122 -0
  2. package/package.json +27 -0
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # unease
2
+
3
+ Infer an easing curve or spring config from an animation video (`.mp4`).
4
+
5
+ `unease` is a Rust CLI **and MCP server**. A model identifies the item *once*; `unease`
6
+ then tracks it across every frame algorithmically and fits the curve — deterministic,
7
+ precise, reproducible. The model does perception (naming/pointing at the item); the
8
+ algorithm does inference (tracking + fitting).
9
+
10
+ ```
11
+ open_video → [model marks item with a bbox] → track_item → emit
12
+
13
+ color-mask · connected-blob · fit
14
+ ```
15
+
16
+ ## Why this shape
17
+
18
+ The naive approach — hand every frame to a vision model and have it read the item's size
19
+ by eye — is non-deterministic, imprecise (can't read sub-pixel scale), slow, and breaks
20
+ when scale, translation, and camera-zoom combine. So perception is minimized to a single
21
+ seed; everything measurable is a plain pixel algorithm:
22
+
23
+ - **seed** — model marks the item with one bbox; unease samples its hue
24
+ - **track** — per frame: hue-mask (survives the brightness/glow ramp UI elements undergo),
25
+ label connected components, follow the item's blob by centroid continuity (so a
26
+ same-colored label elsewhere can't steal the track)
27
+ - **signal** — the blob's geometry over time, normalized `(metric−start)/(settle−start)`
28
+ so start=0, settle=1, overshoot>1
29
+ - **fit** — cubic-bezier and damped-spring models, dependency-free Nelder–Mead
30
+ - **classify** — spring only on real overshoot *and* a clearly better fit
31
+ - **emit** — CSS, framer-motion, reanimated, SwiftUI
32
+ - **verify** — optional `--demo`: a blue pill the item's size replays the *inferred* curve
33
+ on white; if it matches the source, the fit is good
34
+
35
+ A manual `sample_frames`/`observe` loop remains as a fallback for items color-masking
36
+ can't isolate.
37
+
38
+ ## Install
39
+
40
+ Needs `ffmpeg` on `PATH`.
41
+
42
+ ```sh
43
+ cargo build --release
44
+ ```
45
+
46
+ ## CLI
47
+
48
+ ```sh
49
+ unease probe anim.mp4 # fps, duration, dims (+ low-fps warning)
50
+ unease frames anim.mp4 --count 12 # dump evenly spaced frames
51
+ unease track anim.mp4 --bbox x,y,w,h # algorithmic track + fit (seed color from a bbox)
52
+ unease track anim.mp4 --color R,G,B # …or seed the color directly
53
+ unease fit observations.json # fit from a samples file (pure math, no video)
54
+ unease mcp # run the MCP server on stdio
55
+ ```
56
+
57
+ ### `track` — the main path
58
+
59
+ ```sh
60
+ # seed the item with a bbox on frame 0, track its scale, render a verification demo
61
+ unease track anim.mp4 --bbox 590,530,55,55 --property height --demo
62
+ ```
63
+
64
+ - `--bbox x,y,w,h` (on `--ref-frame N`) or `--color R,G,B` — seed the target
65
+ - `--property height|width|area|cx|cy` — what to track; omit for **auto** (largest change)
66
+ - `--hue-tol`, `--min-value`, `--min-sat` — mask tuning (defaults handle glow)
67
+ - `--demo [--demo-out path]` — write `<video>.demo.mp4`: a blue pill the item's size
68
+ replaying the inferred curve on white
69
+ - `--dump-metrics` — per-frame bbox metrics to stderr
70
+
71
+ `observations.json`:
72
+
73
+ ```json
74
+ { "duration": 0.5, "samples": [ {"t":0.0,"v":0.0}, {"t":0.5,"v":1.18}, {"t":1.0,"v":1.0} ] }
75
+ ```
76
+
77
+ - `t` — normalized time `[0,1]` over the animation window
78
+ - `v` — normalized value of the tracked property (`0` start, `1` settle, `>1` overshoot)
79
+
80
+ ## MCP tools
81
+
82
+ | tool | purpose |
83
+ |------|---------|
84
+ | `open_video(path)` | metadata + a session handle; warns on low fps |
85
+ | `sample_frames(handle, count?\|timestamps?)` | returns frames as images (to *see* the item before marking it) |
86
+ | **`track_item(handle, bbox, property?, demo?)`** | **mark item once → algorithmic track + fit in one call** |
87
+ | `observe(handle, samples, window_seconds?)` | fallback: model submits tracked `{t,v}` per frame |
88
+ | `fit(handle)` | fallback: fit observed samples + classify |
89
+ | `emit(handle)` | render the fitted curve into framework snippets |
90
+
91
+ Primary flow: `open_video` → `sample_frames` (see the item) → `track_item(bbox)` → done.
92
+
93
+ ### Register with Claude Code
94
+
95
+ ```sh
96
+ claude mcp add unease -- /path/to/unease mcp
97
+ ```
98
+
99
+ Then: *"infer the easing of the blue button in demo.mp4"* — the model walks the loop.
100
+
101
+ ## Output example (underdamped spring)
102
+
103
+ ```json
104
+ {
105
+ "kind": "spring",
106
+ "confidence": 0.77,
107
+ "framer_motion": "transition={{ type: 'spring', stiffness: 681.3, damping: 24.52, mass: 1.00 }}",
108
+ "swiftui": ".spring(response: 0.241, dampingFraction: 0.470)",
109
+ "notes": ["Overshoot peak 18.0% — underdamped spring."]
110
+ }
111
+ ```
112
+
113
+ ## Limits
114
+
115
+ - **Frame rate** — 30 fps undersamples fast springs; `unease` warns. 60 fps preferred.
116
+ - **Single property** — fits one value channel (position *or* scale *or* opacity) at a time.
117
+ - **Window** — spring stiffness/damping scale with the real animation duration; report it
118
+ via `window_seconds` for physical params.
119
+
120
+ ## License
121
+
122
+ MIT
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "unease",
3
+ "version": "0.0.1",
4
+ "description": "Infer easing/spring curves from animation video via algorithmic tracking + MCP",
5
+ "keywords": [
6
+ "easing",
7
+ "spring",
8
+ "animation",
9
+ "cubic-bezier",
10
+ "mcp",
11
+ "reverse-engineering",
12
+ "motion"
13
+ ],
14
+ "license": "MIT",
15
+ "author": "Enzo Manuel Mangano",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/enzomanuelmangano/unease.git"
19
+ },
20
+ "homepage": "https://github.com/enzomanuelmangano/unease#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/enzomanuelmangano/unease/issues"
23
+ },
24
+ "files": [
25
+ "README.md"
26
+ ]
27
+ }