diip3sh / feel
git clone https://github.com/diip3sh/feel.git
Interactions built on physics. Describe a feel, get a component driven by mass, stiffness and damping, plus a test rig. No durations, no easing curves.
—
stars, 24h
+52
stars, 7 days · #869
+52
stars, 30 days · #5,697
52
★ total · +52% this week
1
forks · 0 open issues
Star history starts 2026-10-20; the chart appears after the second daily snapshot.
LanguageJavaScript
LicenseMIT
Created2026-10-13 (7d ago)
Last push7d ago
Watchers1
OwnerUser
GitHub topics
Links
- GitHub: github.com/diip3sh/feel
- Homepage: https://diip3sh.github.io/feel
- Releases · Issues · Hacker News mentions
README excerpt
# Feel
**Interactions built on physics. No hand-picked durations.** A skill for Claude Code, plus the runtime and test rig it uses.
**Live demo** · **Watch the video**
Say how it should feel, for example "a toggle that feels heavy", "a drawer with a magnetic snap" or "a card you throw into a corner". You get two things:
- **A production component** driven by mass, stiffness, damping and force fields, on a line, in a plane or around a dial.
- **A test rig** that plots position, velocity and the force field. It overlays the model's prediction on the measured motion, can hand each release to the browser as CSS keyframes baked from the same model, and exports the same spring to Framer Motion, react-spring, SwiftUI, Compose or CSS `linear()`.
## The rule
**Every motion comes from a physical model. No hand-picked durations.**
- A tap changes a target or gives an impulse.
- A drag pulls the body through a hand spring, so heavy things lag the finger.
- Release keeps the body's real velocity, and decisions come from where it would coast.
- If CSS forces a duration, it's computed from the model and labelled as such.
- `scripts/audit.mjs` fails on any `transition: 300ms`, `cubic-bezier`, `duration:` or timer that moves things.
## Try it
Open the **live demo**, or run it locally:
[code]
**Heavy toggle:** drag it slowly, flick it, or let go halfway. **Magnetic drawer:** flick the sheet and watch which magnet catches it. **Corner card:** throw it diagonally. **Knob:** turn it, flick it, and feel the detents. Switch any rig's "Runs on" to CSS keyframes and the browser plays each release instead.
## Install the skill
[code]
[code]
## Runtime
[code]
`Feel.words("very heavy, snappy")`, `Feel.metrics(params)`, `Feel.magnets({ stops, period })`, `Feel.Body2D` with `Feel.points({ stops })`, `Feel.drag(el, body, { axis: "x" | "y" | "xy" | "angle" })`, `Feel.handoff(body, styles)`, `Feel.exportCode(params, "framer")`, `Feel.rig(host, { body, stage, travel })`. React: `runtime/react/useFeel.ts`.
The rig's controls (`Feel.ui`) follow Fluid Functionalism, ported to plain DOM and moved by Feel springs.
## Audit
[code]
It scans for hand-picked durations, checks the solver against the exact solution on a line and in a plane, checks passivity and settling, checks that taps land on every stop, corner, detent and across a wheel's seam, checks that baked CSS keyframes match the JS loop, and checks that the rig's Measures and its readout give the same settle time.
MIT © diip3sh