by diip3sh
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.
# Add to your Claude Code skills
git clone https://github.com/diip3sh/feelSee how feel compares with popular alternatives.
feel is an open-source ai agents skill for AI coding assistants such as Claude Code, Codex CLI, and ChatGPT, built by diip3sh. 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. It has 51 GitHub stars.
feel's catalog security scan is still queued. You can run an instant dependency and prompt-injection check now with the "Scan for vulnerabilities" button above.
Clone the repository with "git clone https://github.com/diip3sh/feel" and add it to your Claude Code skills directory (see the Installation section above). feel ships a SKILL.md manifest, so compatible agents can discover and load it automatically.
feel is primarily written in JavaScript. It is open-source under diip3sh on GitHub, so you can review or fork the full source.
Yes. SkillsLLM lists many other AI Agents skills you can browse and compare side by side. Open the AI Agents category from the badge at the top of this page, or use the Related Skills and comparison links further down to weigh feel against similar tools.
No comments yet. Be the first to share your thoughts!
Available after the catalog security scan passes.
⚠️ Third-Party Software Notice
This skill is third-party open-source software developed and hosted independently on GitHub. SkillsLLM is an informational directory and does not control or maintain the underlying repository.
Any security checks, ratings, or warnings displayed by SkillsLLM are automated and limited in scope. They do not constitute a security certification or guarantee that the software is safe, error-free, or free from malicious code, vulnerabilities, compromised dependencies, or prompt-injection risks.
Review the source code, permissions, dependencies, and configuration before installing or running any third-party skill. Use is at your own risk. To the maximum extent permitted by applicable law, SkillsLLM is not liable for losses arising from third-party software.
The deep catalog scan for this skill is still queued. Run an instant dependency check now instead.
This skill makes interactions that move like things. A toggle's thumb is a body with mass on a spring. A drawer is a body coasting through a field of magnets. A drag is a force: the pointer pulls the body through a hand spring, so a heavy thing lags the finger and a light thing doesn't. Nothing in the result has a duration or an easing curve. How long a motion takes is a consequence of the model, and the rig can show you exactly why.
One complete page ships with the skill: index.html, with four components, each in its rig: a heavy toggle (a spring), a magnetic drawer (a field), a card thrown to a corner (a body in a plane) and a detented knob (a body in degrees). Open it before you start. Serve the folder (python3 -m http.server) and play with all four.
Every motion comes from a physical model: mass, stiffness, damping and forces. No hand-picked durations.
That means:
transition: … 300ms, no animation-duration, no cubic-bezier, no ease-out, no duration: options, no timers that move things. scripts/audit.mjs fails on any of them.x + v·m/c, or by simulating forward. It is never made from a threshold in milliseconds.Feel.handoff does this for you.It fails in two ways:
Size it, before anything else. Tell the user which size this is.
Words to physics. Write the feel as words and run Feel.words("…"). It returns mass, natural frequency and damping ratio, then stiffness and damping. references/vocabulary.md has every word and how words blend. If the user's word isn't there, map it to the nearest ones and say which you used. Then write one sentence per number:
Choose the model. references/models.md covers them:
Feel.Body2D) for anything thrown in two directions at once (a picture-in-picture card, a floating panel). Corners and snap points are Feel.points, measured in a straight line. Two 1D bodies can't do this, because which stop is nearest depends on both coordinates.Feel.Body with unit: "°") for knobs, dials and wheels. Detents are Feel.magnets in degrees; a wheel that turns forever adds period: 360.Build the component on a body.
styles(b) that returns [[el, props], …]: transform from position, anything else (fill, shadow, stretch) from position or speed. Paint it with Feel.paint(styles(b)) in onFrame.Feel.drag(el, body, { axis }): "x", "y", "xy" for a plane, "angle" for a turn.prefers-reduced-motion jumps to rest. The runtime does this for you.Feel.handoff(body, styles). Register any custom property it animates with @property.components/heavy-toggle.js, magnetic-drawer.js, corner-card.js or rotary-knob.js. React: runtime/react/useFeel.ts.Put it in the rig. Feel.rig(host, { body, stage, travel, stops, feel, actions, handoff }). Check every one of these:
Feel.metrics (the spring alone).handoff, switch "Runs on" to CSS keyframes and do it again: the same motion, and a grab mid-flight doesn't jump.Export if the target stack needs it. The rig's export tabs give the same spring for Framer Motion, react-spring and SwiftUI (identical equation), Compose (unit-mass stiffness), and CSS linear(), which is sampled from the exact solution and only replays a step from rest.
Feel.handoff: each release is baked from the model into keyframes, with the velocity it has.runtime/feel.js (15 kB gzipped, comments included).Verify. Run node scripts/audit.mjs <your files> --feel "<your words>". It must print passed. Then do it with your hands in the browser:
Record what you saw in one line each. references/verify.md has the checklist.
const body = new Feel.Body({ feel: "heavy", x: 0, target: 0, min: 0, max: 60, onFrame, onRelease });
body.to(60); // new target: the spring takes it there
body.kick(900); // an impulse, px/s
body.kickTo(170); // the push that comes to rest at 170 (solved by simulation in a field)
body.grab(px); body.move(px); body.release(); // what Feel.drag calls for you
body.projected(); // where it would coast: x + v·m/c
body.predict(v); // where it would come to rest from here with velocity v
body.path(x, v); // the whole predicted path, for the rig's ghost
body.measureStep(0, 60); // a tap from rest, walls and forces included → { overshoot, settle }: the rig's Measures
body.lastSettle(); // the last motion's settle (±2% of the move, from "let go"), read off the trace: the readout
const card = new Feel.Body2D({ feel: "airy", x: 0, y: 0, target: null, forces: [Feel.points({ stops })], min: { x: 0, y: 0 }, max: { x: W, y: H } });
card.to({ x, y }); card.kick(vx, vy); card.kickTo(stops[2]); // the same commands, on points
const knob = new Feel.Body({ feel: "heavy, clicky", unit: "°", target: null, forces: [Feel.magnets({ stops: degrees })], min: -135, max: 135 });
Feel.words("very heavy, snappy") // → { params: { mass, stiffness, damping }, modal, magnet, friction }
Feel.metrics(params) // → { freq, zeta, overshoot, settle, rise, peakVelocity }
Feel.magnets({ stops, reachFreq, clickFreq, clickRadius, period }); Feel.points({ stops, … })
Feel.friction({ level }); Feel.friction2d({ level }); Feel.gravity(g)
Feel.drag(el, body, { axis: "x" | "y" | "xy" | "angle", onTap })
Feel.paint(styles(body)) // write [[el, props], …] onto the elements
Feel.handoff(body, styles) // let the browser play every free motion as keyframes; .off() takes it back
Feel.exportCode(params, "framer" | "react-spring" | "swiftui" | "compose" | "css", body?)
Units are px (or degrees) and seconds. Mass 1 is a normal UI element, and the hand spring is tuned for it. All bodies share one requestAnimationFrame loop that stops when everything has settled. With a handoff, nothing runs per frame: each release is simulated once at 240 Hz, simplified to keyframes within 0.2 px of the path, and played by the browser. A grab or a command mid-flight reads the state at that instant from the same path.
Feel.ui holds the rig's own controls (fluid hover, tabs, the scrubber slider, copy), after Fluid Functionalism. They move on Feel springs too, so a page can use them without adding a duration.
Body2D spring is isotropic: one stiffness and one damping for both axes. Walls are per axis, so travel is a rectangle.Feel.handoff plays free motion only. A drag still runs in JavaScript, because the hand needs it. Custom properties it animates must be registered with @property, or the browser flips them instead of interpolating.linear() export replays a step from rest. For a flick or a field in CSS, use Feel.handoff.| File | Use it for |
|---|---|
runtime/feel.js |
Bodies on a line and in a plane, the exact spring solution, metrics, words, force fields, drag, the shared loop, the CSS handoff, export. Works in the browser and in Node. |
runtime/rig.js + runtime/feel.css |
The test rig: bench, position/velocity/force plots with the predicted ghost (path, speed and arrows in a plane), dials, word chips, readout, keys, JS or CSS, export tabs. Also Feel.ui, its controls. Light by default; theme: "dark" for dark pages. |
runtime/react/useFeel.ts |
React hook: a body that writes styles through a ref, without re-rendering. |
components/heavy-toggle.js |
Example: a spring with walls, a drag through the hand spring, a flick decided by projection. |
components/magnetic-drawer.js |
Example: a field with no target, taps as impulses solved by simulation. |
components/corner-card.js |
Example: a body in a plane, thrown to the nearest corner, with lift from speed. |
components/rotary-knob.js |
Example: a body in degrees, turned around its centre, with detents as magnets. |
scripts/audit.mjs |
No-durations scan, solver vs exact solution on a line and in a plane, passivity, settling, taps land (stops, corners, detents, across a wheel's seam), baked keyframes against the loop, and one settle time for the rig's Measures and readout. |
references/vocabulary.md |
Every feel word, its numbers, and how words blend. |
references/models.md |
Springs, fields, walls, the hand, projection, and how to write a new force. |
references/verify.md |
The hands-on checklist. |
Interactions built on physics. No hand-picked durations. A skill for Claude Code, plus the runtime and test rig it uses.
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:
linear().Every motion comes from a physical model. No hand-picked durations.
scripts/audit.mjs fails on any transition: 300ms, cubic-bezier, duration: or timer that moves things.Open the live demo, or run it locally:
python3 -m http.server 8750
open http://127.0.0.1:8750/
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.
npx skills add diip3sh/feel
# or
git clone https://github.com/diip3sh/feel ~/.claude/skills/feel
/feel a toggle that feels heavy, React
/feel bottom sheet with three magnetic stops, slightly bouncy
const body = new Feel.Body({ feel: "heavy", x: 0, target: 0, min: 0, max: 60, onFrame: (b) => thumb.style.transform = `translateX(${b.x}px)` });
Feel.drag(el, body, { axis: "x", onTap: () => body.to(body.target ? 0 : 60) });
body.onRelease = (b, projected) => b.to(projected > 30 ? 60 : 0);
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.
node scripts/audit.mjs # components + demo
node scripts/audit.mjs src/ --feel "heavy, snappy"
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