OriginRestoringForce
Constraint that applies spring-like force to restore particles to their original positions
Constructor
new OriginRestoringForce(params?: OriginRestoringForceParams)Parameters
| Parameter | Type | Description | Default | 
|---|---|---|---|
| params | OriginRestoringForceParams | Configuration parameters | {} | 
Properties
| Property | Type | Description | 
|---|---|---|
| name | string | Unique identifier for the constraint | 
| active | boolean | Whether the constraint is active | 
| glsl | string | GLSL shader code for the force | 
| strength | number | Spring force strength | 
Methods
build(): void
Builds the GLSL shader code for the origin restoring force.
setStrength(strength: number): void
Updates the strength of the restoring force.
Example Usage
import { OriginRestoringForce, PropertyManager } from 'particlize';
// Create origin restoring force with default strength
const restoringForce = new OriginRestoringForce({
  strength: { value: 0.05, hardcode: false }
});
// Create strong restoring force
const strongRestoring = new OriginRestoringForce({
  strength: { value: 0.2, hardcode: true }
});
// Add to property manager
const propertyManager = new PropertyManager({ /* ... */ });
propertyManager.addProperty('velocity', { size: 3 });
propertyManager.addProperty('originalPosition', { size: 3 });
propertyManager.addConstraint('velocity', restoringForce);
// Update force strength dynamically
restoringForce.setStrength(0.1);import { OriginRestoringForce } from 'particlize';
// Basic spring force
const spring = new OriginRestoringForce('spring', {
  strength: { value: 0.1, hardcode: false }
});
// Strong restoring force
const strongSpring = new OriginRestoringForce('strongSpring', {
  strength: { value: 1.0, hardcode: true }
});
// Default parameters
const defaultSpring = new OriginRestoringForce('default');Properties
Default Parameters
static readonly defaultParams: OriginRestoringForceParams = {
  strength: { value: 10, hardcode: false }
};Instance Properties
name:string- Constraint nameparams:OriginRestoringForceParams- Spring parametersglsl:string- Generated GLSL codeuniforms:Record<string, any>- Shader uniforms (for non-hardcoded params)
GLSL Implementation
The OriginRestoringForce generates the following GLSL code:
float d_forceName = 2.0 * sqrt(STRENGTH);
vec3 restoring_forceName = (origin - position) * STRENGTH;
vec3 damping_forceName = -velocity * d_forceName;
vec3 acceleration_forceName = restoring_forceName + damping_forceName;
force = acceleration_forceName * mass;Where:
STRENGTHis replaced with the strength parameter value or uniformforceNameis the constraint name for variable namespacingoriginis the particle's original position property- The damping coefficient is calculated as 
2.0 * sqrt(strength)for critical damping 
Physics Model
The constraint implements a damped harmonic oscillator:
F = -k(x - x₀) - c*vWhere:
- k is the spring constant (strength parameter)
 - x is the current position
 - x₀ is the origin position
 - c is the damping coefficient (calculated as 2√k for critical damping)
 - v is the velocity
 
Usage Examples
Basic Spring Behavior
import { OriginRestoringForce, PropertyManager } from 'particlize';
// Create spring force
const spring = new OriginRestoringForce('spring', {
  strength: { value: 0.1, hardcode: false }
});
// Apply to force property (which affects velocity)
propertyManager.constrain('force', spring);Cloth Simulation
// Setup for cloth-like behavior
propertyManager
  .add('origin', 3)      // Store original positions
  .add('position', 3)    // Current positions
  .add('velocity', 3, new Float32Array([0, 0, 0]))
  .add('force', 3, new Float32Array([0, 0, 0]))
  .add('mass', 1, new Float32Array([1.0]));
// Weak spring for cloth flexibility
const clothSpring = new OriginRestoringForce('clothSpring', {
  strength: { value: 0.05, hardcode: false }
});
propertyManager.constrain('force', clothSpring);Elastic Deformation
// Strong springs for elastic materials
const elasticForce = new OriginRestoringForce('elastic', {
  strength: { value: 2.0, hardcode: true }
});
propertyManager.constrain('force', elasticForce);Dynamic Spring Strength
// Use uniforms for runtime strength adjustment
const variableSpring = new OriginRestoringForce('variable', {
  strength: { value: 0.1, hardcode: false }
});
propertyManager.constrain('force', variableSpring);
// Update strength during animation
function updateSpringStrength(newStrength: number) {
  propertyManager.setUniforms('force', {
    u_variable_strength: newStrength
  });
}
// Example: Increase stiffness over time
updateSpringStrength(Math.sin(time) * 0.5 + 0.5);Multiple Spring Systems
// Primary restoring force
const primarySpring = new OriginRestoringForce('primary', {
  strength: { value: 0.2, hardcode: true }
});
// Secondary stabilizing force
const secondarySpring = new OriginRestoringForce('secondary', {
  strength: { value: 0.05, hardcode: false }
});
propertyManager
  .constrain('force', primarySpring)
  .constrain('force', secondarySpring);Interactive Spring System
class InteractiveSpringSystem {
  private spring: OriginRestoringForce;
  private propertyManager: PropertyManager;
  constructor(propertyManager: PropertyManager) {
    this.propertyManager = propertyManager;
    
    this.spring = new OriginRestoringForce('interactive', {
      strength: { value: 0.1, hardcode: false }
    });
    
    propertyManager.constrain('force', this.spring);
  }
  setStiffness(stiffness: number) {
    this.propertyManager.setUniforms('force', {
      u_interactive_strength: stiffness
    });
  }
  // Make springs stiffer when particles are disturbed
  updateStiffness(disturbanceLevel: number) {
    const baseStiffness = 0.1;
    const maxStiffness = 1.0;
    const stiffness = baseStiffness + disturbanceLevel * maxStiffness;
    this.setStiffness(stiffness);
  }
}Parameter Configuration
Strength Values
// Very weak spring (fluid-like)
new OriginRestoringForce('fluid', {
  strength: { value: 0.01, hardcode: true }
});
// Medium spring (cloth-like)
new OriginRestoringForce('cloth', {
  strength: { value: 0.1, hardcode: false }
});
// Strong spring (elastic solid)
new OriginRestoringForce('solid', {
  strength: { value: 1.0, hardcode: true }
});
// Very strong spring (rigid)
new OriginRestoringForce('rigid', {
  strength: { value: 10.0, hardcode: true }
});Hardcoded vs Uniform
// Static spring strength (better performance)
const staticSpring = new OriginRestoringForce('static', {
  strength: { value: 0.5, hardcode: true }
});
// Dynamic spring strength (updateable)
const dynamicSpring = new OriginRestoringForce('dynamic', {
  strength: { value: 0.5, hardcode: false }
});Integration with ParticleSystem
import { ParticleSystem, OriginRestoringForce, SamplerFrame } from 'particlize';
const particleSystem = new ParticleSystem({ canvas });
// Setup properties with origin tracking
particleSystem.manager
  .add('origin', 3)      // Original positions
  .add('position', 3)    // Current positions  
  .add('velocity', 3, new Float32Array([0, 0, 0]))
  .add('force', 3, new Float32Array([0, 0, 0]))
  .add('mass', 1, new Float32Array([1.0]));
// Add spring constraint
const spring = new OriginRestoringForce('mainSpring', {
  strength: { value: 0.2, hardcode: false }
});
particleSystem.manager.constrain('force', spring);
// Add particles (origin will be set to initial positions)
const frame = new SamplerFrame({ sampler, count: 5000 });
particleSystem.addParticles(frame);Damping Behavior
The constraint automatically calculates critical damping:
damping_coefficient = 2 * sqrt(spring_strength)This provides:
- No overshoot: Particles return to origin without oscillating
 - Fastest convergence: Quickest return to equilibrium
 - Stable behavior: No energy buildup or instability
 
Custom Damping
For custom damping ratios, create a modified constraint:
class CustomDampedSpring extends Constraint {
  constructor(name: string, strength: number, dampingRatio: number = 1.0) {
    const dampingCoeff = dampingRatio * 2.0 * Math.sqrt(strength);
    
    super(name, `
      vec3 restoring = (origin - position) * ${strength.toFixed(6)};
      vec3 damping = -velocity * ${dampingCoeff.toFixed(6)};
      force += (restoring + damping) * mass;
    `);
  }
}
// Under-damped (oscillating)
const underDamped = new CustomDampedSpring('underDamped', 1.0, 0.5);
// Over-damped (slow return)
const overDamped = new CustomDampedSpring('overDamped', 1.0, 2.0);Performance Considerations
- Hardcoded Strength: Better performance for static springs
 - Origin Property: Requires additional memory for origin positions
 - Calculation Cost: Square root operation for damping coefficient
 - Multiple Springs: Each spring adds computational overhead
 
Common Use Cases
- Cloth Simulation: Flexible fabric behavior
 - Soft Body Physics: Deformable objects
 - Hair/Fur Simulation: Strand dynamics
 - Particle Clouds: Maintaining shape while allowing movement
 - Elastic Structures: Bouncy, rubber-like materials
 - Stabilization: Preventing particles from drifting too far
 - Recovery Systems: Returning to formation after disturbance
 
Best Practices
- Origin Setup: Ensure origin property is properly initialized
 - Strength Tuning: Start with small values (0.01-0.1) and adjust
 - Mass Consideration: Heavier particles need stronger springs
 - Performance: Use hardcoded strength for static scenarios
 - Stability: Avoid extremely high strength values
 - Integration: Combine with velocity and position update constraints
 
Troubleshooting
Common Issues
// Issue: No restoring force
// Solution: Ensure origin property exists and is set
propertyManager.add('origin', 3); // Must be added before constraint
// Issue: Too oscillatory
// Solution: The built-in damping should prevent this, check time step
// Issue: Too slow return
// Solution: Increase strength value
spring.uniforms.u_springName_strength = 0.5; // Higher value
// Issue: Particles stick to origin
// Solution: Reduce strength or add other forcesDebugging
// Log spring parameters
console.log('Spring strength:', spring.params.strength.value);
console.log('Is hardcoded:', spring.params.strength.hardcode);
// Check if origin property exists
const originProperty = propertyManager.properties.get('origin');
if (!originProperty) {
  console.error('Origin property not found - spring will not work');
}Advanced Usage
Anisotropic Springs
// Different strength per axis using custom constraint
class AnisotropicSpring extends Constraint {
  constructor(name: string, strengthX: number, strengthY: number, strengthZ: number) {
    super(name, `
      vec3 displacement = origin - position;
      vec3 springForce = displacement * vec3(${strengthX}, ${strengthY}, ${strengthZ});
      
      vec3 dampingCoeff = 2.0 * sqrt(vec3(${strengthX}, ${strengthY}, ${strengthZ}));
      vec3 damping = -velocity * dampingCoeff;
      
      force += (springForce + damping) * mass;
    `);
  }
}
const anisotropicSpring = new AnisotropicSpring('anisotropic', 0.1, 0.5, 0.1);Non-linear Springs
class NonLinearSpring extends Constraint {
  constructor(name: string, strength: number, nonlinearity: number = 1.0) {
    super(name, `
      vec3 displacement = origin - position;
      float distance = length(displacement);
      vec3 direction = normalize(displacement);
      
      // Non-linear force: F = k * d^n
      float forceMagnitude = ${strength} * pow(distance, ${nonlinearity});
      vec3 springForce = direction * forceMagnitude;
      
      force += springForce * mass;
    `);
  }
}
// Quadratic spring (stiffer at distance)
const quadraticSpring = new NonLinearSpring('quadratic', 0.1, 2.0);The OriginRestoringForce provides a robust foundation for creating spring-like behaviors in particle systems, offering both stability and natural motion characteristics.