Skip to content

feat(motion): native spring integrator and GELATIN preset - #34

Open
professorpalmer wants to merge 3 commits into
remorses:mainfrom
professorpalmer:feat/motion-springs
Open

professorpalmer wants to merge 3 commits into
remorses:mainfrom
professorpalmer:feat/motion-springs

Conversation

@professorpalmer

Copy link
Copy Markdown

motion.div transition={{ type: 'spring', stiffness, damping, mass }}.

GELATIN { stiffness: 28, damping: 8, mass: 1.25 } is underdamped gel. Overdamped ~13/14/1 for micro UI.

JS stepSpring plus a native motion.rs integrator. The frame loop pumps onFrame. Proven in Automaton on published 0.6.0 channels (width / height / opacity / left / top / right / bottom / borderRadius). No scale/rotate. No metal CI on this machine.

@professorpalmer

Copy link
Copy Markdown
Author

Rebased feat/motion-springs onto current main (resolved renderer.ts: kept main's WindowKeyEventHandlers + tick try/catch, kept pumpFrames for spring lease parking). Tip is now d38c15f. Motion-spring vitest 8/8 locally. Ready when you want to review/merge + publish (Automaton is temporarily vendoring a 0.6.1 park build until an npm release exists).

professorpalmer added a commit to professorpalmer/Automaton that referenced this pull request Sep 13, 2026
Hold file:vendor/gpuix-react-0.6.1.tgz — npm 0.7.0 lacks motion-spring
lease-park; remorses/gpuix#34 is still open. Document drop criteria;
never ask NPM_TOKEN for @gpuix. Stay on 0.3.0.

Co-authored-by: Cary Palmer <professorpalmer@users.noreply.github.com>
professorpalmer and others added 3 commits September 29, 2026 16:32
Unsubscribe onFrame when every spring channel settles, skip setCurrent
for unchanged integer px/opacity, and hard-settle leftover GELATIN crawl
so GPUI is not rebuilt every frame. Tween motion.div is unchanged.

Co-authored-by: Cary Palmer <professorpalmer@users.noreply.github.com>
The overshoot test stepped 120 x 8ms, but stiffness 40 / damping 6 only
snaps to rest around frame 342, so it asserted a settle that had not
happened. Step 500 frames. Add a case for an explicit type: "tween"
transition, which the untagged enum would otherwise read as a spring.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@professorpalmer

Copy link
Copy Markdown
Author

Rebased onto main at 4ecca30 again. Head is now 774c854.

Main moved motion a fair bit since the last rebase (AnimatePresence, generation / isExit, just_settled / motionComplete, host types in @gpuix/native/host, frame loop in @gpuix/native/runtime), so here's how I resolved the conflicts:

  • motion.rs: kept main's visible_style / sample split, with_fallback targets, and the needs_settle / just_settled / generation logic. The spring integrator now plugs into that. frame() steps the springs, sample() returns the last integrated style for springs, and tweens work the same as before. Also, an explicit type: "tween" now parses as a tween. Before, the untagged enum read it as a spring and rejected it.
  • Spring/tween transition types moved to packages/native/js/host.ts, since main's types/host.ts re-exports from there. The React exports are unchanged.
  • renderer.ts: kept main's startNativeFrameLoop. pumpFrames runs inside the ticked renderer wrapper, just before each tick(), in its own try/catch. Spring lease parking is unchanged.
  • motion.div: kept main's presence, exit and generation handling. For springs, exit becomes the lease target, and safeToRemove fires when the lease parks at rest. onMotionComplete doesn't fire for spring divs yet, because they're JS-driven and never emit the native event. The tween motion key now hashes the whole transition.
  • Added one commit that fixes the Rust spring test. At stiffness 40 / damping 6 it only reaches rest around frame 342, and the test stepped 120 frames, so it now steps 500.

Test results (Windows, no Rust toolchain on this machine):

  • vitest run src/__tests__/motion-spring.test.ts: 8/8 pass
  • tsc --noEmit passes for @gpuix/react and @gpuix/solid (after tsc on @gpuix/native JS)
  • I couldn't run cargo test for motion.rs here. The native-binding vitest suites need a built .node, so I couldn't run them either. Please let CI check those.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant