Modules
StringImpulse
Applies cursor-reactive tilt and spring-like movement to interactive elements.
StringImpulse
StringImpulse turns cursor velocity into a spring simulation. It writes position and rotation values to CSS variables, while you stay in control of the final transform in your own styles.
Public API
Attributes
Position spring
| Attribute | Type | Default | Real runtime effect |
|---|---|---|---|
string-position-strength | number | 3 | Scales how much cursor velocity is injected into translation. |
string-position-tension | number | 0.05 | Pull-back force toward the resting position. |
string-position-friction | number | 0.15 | Per-frame damping for translation velocity. |
string-position-max-velocity | number | 10 | Translation velocity clamp. |
string-position-update-threshold | number | 0.1 | Minimal visible delta before --push-x and --push-y are rewritten. |
string-max-offset | number | 220 | Maximum translation offset in either direction. |
Rotation spring
| Attribute | Type | Default | Real runtime effect |
|---|---|---|---|
string-rotation-strength | number | 0.75 | Scales torque generated from cursor movement around the object. |
string-rotation-tension | number | 0.06 | Pull-back force toward 0deg. |
string-rotation-friction | number | 0.18 | Per-frame damping for angular velocity. |
string-rotation-max-angular-velocity | number | 6 | Angular velocity clamp. |
string-rotation-max-angle | number | 18 | Maximum visible rotation in degrees. |
string-rotation-update-threshold | number | 0.15 | Minimal visible delta before --push-rotation is rewritten. |
string-rotation-origin | string | center center | Changes the torque pivot used by the simulation. |
Shared control
| Attribute | Type | Default | Real runtime effect |
|---|---|---|---|
string-sleep-epsilon | number | 0.01 | Snaps the simulation back to rest once motion becomes tiny enough. |
string-continuous-push | boolean | true | When false, each hover pass injects only one push until the pointer leaves and re-enters. |
CSS Variables and DOM Output
The module writes these CSS variables on the source element and all mirrors:
--push-x--push-y--push-rotation
These are plain numbers. Add units yourself in CSS:
- translation usually consumes
* 1px - rotation usually consumes
* 1deg
Events
| Channel | Payload | Fired when |
|---|---|---|
object:impulse:<id>:move | { x, y } | Translation output changes past the configured threshold |
object:impulse:<id>:rotate | { rotation } | Rotation output changes past the configured threshold |
object:impulse:<id>:side | { value } | Pointer moves across the element; 0 is left edge and 1 is right edge |
Mirror Behavior
Mirrors inherit the source element's impulse CSS variables. Events are emitted only by the source object's string-id.
Quick Example
HTML
<div class="news-grid">
<article
class="news-card"
string="impulse"
string-id="breaking-news"
string-position-strength="2.0"
string-rotation-strength="1.5"
string-max-offset="80"
>
<div class="news-content">
<span class="news-date">March 21, 2026</span>
<h2 class="news-title">Design Engineering at Scale</h2>
<p>How modular effects pipelines transform the modern web.</p>
</div>
</article>
</div>
CSS
.news-grid {
padding: 4rem;
display: flex;
justify-content: center;
}
.news-card {
width: 100%;
max-width: 400px;
border: 1px solid black;
padding: 2.5rem;
background: white;
color: black;
cursor: default;
transform:
translate3d(
calc(var(--push-x, 0) * 1px),
calc(var(--push-y, 0) * 1px),
0
)
rotate(calc(var(--push-rotation, 0) * 1deg));
transform-origin: center center;
will-change: transform;
}
.news-date {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #666;
display: block;
margin-bottom: 1rem;
}
.news-title {
font-size: 1.5rem;
font-weight: 700;
margin-bottom: 0.5rem;
line-height: 1.1;
}
.news-card p {
font-size: 1rem;
color: #333;
}
Registration
TypeScript
import StringTune, { StringImpulse } from '@fiddle-digital/string-tune';
const stringTune = StringTune.getInstance();
stringTune.use(StringImpulse);
stringTune.start(60);
StringCursor is not required for StringImpulse. The module reads shared pointer velocity from StringTune itself.
Detailed Behavior
StringImpulsenever writes atransformstyle directly. The transform stays fully user-owned.- If you change
string-rotation-origin, keep your CSStransform-originin sync with the same point. Otherwise the visible rotation pivot and the simulated torque pivot will not match. - The module only injects pushes while the pointer is physically inside the element bounds.