npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

unease

v0.0.1

Published

Infer easing/spring curves from animation video via algorithmic tracking + MCP

Downloads

166

Readme

unease

Infer an easing curve or spring config from an animation video (.mp4).

unease is a Rust CLI and MCP server. A model identifies the item once; unease then tracks it across every frame algorithmically and fits the curve — deterministic, precise, reproducible. The model does perception (naming/pointing at the item); the algorithm does inference (tracking + fitting).

open_video → [model marks item with a bbox] → track_item → emit
                                                   │
                              color-mask · connected-blob · fit

Why this shape

The naive approach — hand every frame to a vision model and have it read the item's size by eye — is non-deterministic, imprecise (can't read sub-pixel scale), slow, and breaks when scale, translation, and camera-zoom combine. So perception is minimized to a single seed; everything measurable is a plain pixel algorithm:

  • seed — model marks the item with one bbox; unease samples its hue
  • track — per frame: hue-mask (survives the brightness/glow ramp UI elements undergo), label connected components, follow the item's blob by centroid continuity (so a same-colored label elsewhere can't steal the track)
  • signal — the blob's geometry over time, normalized (metric−start)/(settle−start) so start=0, settle=1, overshoot>1
  • fit — cubic-bezier and damped-spring models, dependency-free Nelder–Mead
  • classify — spring only on real overshoot and a clearly better fit
  • emit — CSS, framer-motion, reanimated, SwiftUI
  • verify — optional --demo: a blue pill the item's size replays the inferred curve on white; if it matches the source, the fit is good

A manual sample_frames/observe loop remains as a fallback for items color-masking can't isolate.

Install

Needs ffmpeg on PATH.

cargo build --release

CLI

unease probe anim.mp4                 # fps, duration, dims (+ low-fps warning)
unease frames anim.mp4 --count 12     # dump evenly spaced frames
unease track anim.mp4 --bbox x,y,w,h  # algorithmic track + fit (seed color from a bbox)
unease track anim.mp4 --color R,G,B   # …or seed the color directly
unease fit observations.json          # fit from a samples file (pure math, no video)
unease mcp                            # run the MCP server on stdio

track — the main path

# seed the item with a bbox on frame 0, track its scale, render a verification demo
unease track anim.mp4 --bbox 590,530,55,55 --property height --demo
  • --bbox x,y,w,h (on --ref-frame N) or --color R,G,B — seed the target
  • --property height|width|area|cx|cy — what to track; omit for auto (largest change)
  • --hue-tol, --min-value, --min-sat — mask tuning (defaults handle glow)
  • --demo [--demo-out path] — write <video>.demo.mp4: a blue pill the item's size replaying the inferred curve on white
  • --dump-metrics — per-frame bbox metrics to stderr

observations.json:

{ "duration": 0.5, "samples": [ {"t":0.0,"v":0.0}, {"t":0.5,"v":1.18}, {"t":1.0,"v":1.0} ] }
  • t — normalized time [0,1] over the animation window
  • v — normalized value of the tracked property (0 start, 1 settle, >1 overshoot)

MCP tools

| tool | purpose | |------|---------| | open_video(path) | metadata + a session handle; warns on low fps | | sample_frames(handle, count?\|timestamps?) | returns frames as images (to see the item before marking it) | | track_item(handle, bbox, property?, demo?) | mark item once → algorithmic track + fit in one call | | observe(handle, samples, window_seconds?) | fallback: model submits tracked {t,v} per frame | | fit(handle) | fallback: fit observed samples + classify | | emit(handle) | render the fitted curve into framework snippets |

Primary flow: open_videosample_frames (see the item) → track_item(bbox) → done.

Register with Claude Code

claude mcp add unease -- /path/to/unease mcp

Then: "infer the easing of the blue button in demo.mp4" — the model walks the loop.

Output example (underdamped spring)

{
  "kind": "spring",
  "confidence": 0.77,
  "framer_motion": "transition={{ type: 'spring', stiffness: 681.3, damping: 24.52, mass: 1.00 }}",
  "swiftui": ".spring(response: 0.241, dampingFraction: 0.470)",
  "notes": ["Overshoot peak 18.0% — underdamped spring."]
}

Limits

  • Frame rate — 30 fps undersamples fast springs; unease warns. 60 fps preferred.
  • Single property — fits one value channel (position or scale or opacity) at a time.
  • Window — spring stiffness/damping scale with the real animation duration; report it via window_seconds for physical params.

License

MIT