`spring` returns `NaN` and breaks physics simulation when `config` has `undefined` properties
Describe the bug
Describe the bug
When calling spring() with a config object containing undefined properties (common when passing optional React props: { damping: props.damping, stiffness: props.stiffness }), springCalculation merges the configuration with object spread:
In packages/core/src/spring/spring-utils.ts:
config: {
...defaultSpringConfig,
...config,
}If stiffness or mass is undefined, it overrides defaultSpringConfig's value with undefined. In advance(), Math.sqrt(k / m) calculates Math.sqrt(undefined / 1) = NaN, causing spring() to return NaN for every frame.
If damping is undefined, zeta evaluates to NaN. Because NaN < 1 is false, it bypasses underDampedPosition and silently forces the spring into criticallyDampedPosition, breaking underdamped spring physics without error.
Steps To Reproduce
Steps To Reproduce
- Call
springwith an optional property explicitly set toundefined:
import { spring } from 'remotion';
const value = spring({
frame: 10,
fps: 30,
config: {
damping: 10,
stiffness: undefined,
},
});
### Expected behavior
### Expected behavior
`springCalculation` should fall back to `defaultSpringConfig` values using nullish coalescing (`config.stiffness ?? defaultSpringConfig.stiffness`), gracefully handling partial and optional configuration objects.
### Suggested Fix
```diff
--- a/packages/core/src/spring/spring-utils.ts
+++ b/packages/core/src/spring/spring-utils.ts
@@ -14,7 +14,7 @@ export type SpringConfig = {
overshootClamping: boolean;
};
-const defaultSpringConfig: SpringConfig = {
+export const defaultSpringConfig: SpringConfig = {
damping: 10,
mass: 1,
stiffness: 100,
@@ -118,12 +118,18 @@ export function springCalculation({
const from = 0;
const to = 1;
+ const resolvedConfig: SpringConfig = {
+ damping: config.damping ?? defaultSpringConfig.damping,
+ mass: config.mass ?? defaultSpringConfig.mass,
+ stiffness: config.stiffness ?? defaultSpringConfig.stiffness,
+ overshootClamping:
+ config.overshootClamping ?? defaultSpringConfig.overshootClamping,
+ };
const cacheKey = [
frame,
fps,
- config.damping,
- config.mass,
- config.overshootClamping,
- config.stiffness,
+ resolvedConfig.damping,
+ resolvedConfig.mass,
+ resolvedConfig.overshootClamping,
+ resolvedConfig.stiffness,
].join('-');
if (calculationCache[cacheKey]) {
return calculationCache[cacheKey];
@@ -144,10 +150,7 @@ export function springCalculation({
animation = advance({
animation,
now: time,
- config: {
- ...defaultSpringConfig,
- ...config,
- },
+ config: resolvedConfig,
});
}
@@ -156,10 +159,7 @@ export function springCalculation({
animation = advance({
animation,
now: (frameClamped / fps) * 1000,
- config: {
- ...defaultSpringConfig,
- ...config,
- },
+ config: resolvedConfig,
});
}Packages
Packages
@remotion/core
Additional Context
No response
Source: refinedev/refine