# CriticallyDampedSpring — Smooth Target Chasing
**Chase a moving target without overshoot.** Has velocity tracking so it
anticipates rather than lags. Unity's `SmoothDamp` — one of the most-used
utility functions in game development.
| Update cost | ~12 cycles |
| Memory | ~16 bytes |
| Types | `SpringF64` |
| Output | Current smoothed value |
| Error handling | Returns `Result<_, DataError>` on NaN/Inf input |
## What It Does
```
Target jumps from 0 to 100:
EMA (alpha=0.1):
100 ┤ ─────────
80 ┤ ──
60 ┤ ──
40 ┤ ── lags behind,
20 ┤ ── approaches asymptotically
0 ┤─────────
└──────────────────────────────────────── t
Spring (smooth_time=10):
100 ┤ ───────────────────
80 ┤ ─
60 ┤ ─ faster convergence,
40 ┤ ─ no overshoot,
20 ┤ ─ has velocity (anticipates)
0 ┤──────
└──────────────────────────────────────── t
Underdamped spring (NOT this — for comparison):
120 ┤ ─
100 ┤ ─ ─────── ────────
80 ┤ ─ ─ overshoots!
60 ┤ ─
0 ┤──────
└──────────────────────────────────────── t
```
The critically damped spring converges as fast as possible WITHOUT
overshooting. It's the optimal response — any faster would oscillate,
any slower would lag unnecessarily.
## When to Use It
**Use Spring when:**
- You need to smoothly chase a moving target
- Overshoot is unacceptable (display values, adaptive thresholds)
- The target changes frequently and you want smooth transitions
- You need variable-dt stability (game loops, irregular updates)
**Don't use Spring when:**
- You just need simple signal smoothing → [EMA](ema.md)
- You need statistics (variance, etc.) → [Welford](welford.md)
- The target is the signal itself, not a separate goal
## Configuration
```rust
let mut spring = SpringF64::new(0.5); // smooth_time in seconds
// Each update: provide target and time delta
let smoothed = spring.update(target_value, dt_seconds).unwrap();
```
### Parameters
| `smooth_time` | Time to converge (seconds) | Lower = faster, snappier |
**smooth_time = 0.1** — snappy, reaches target in ~0.3s
**smooth_time = 0.5** — smooth, reaches target in ~1.5s
**smooth_time = 2.0** — very smooth, reaches target in ~6s
### Implementation Note
Uses a Padé approximant instead of `exp(-x)` for numerical stability
with variable dt. Accurate within 1% for `omega × dt < 2`. For very
large dt values, the spring still converges — just not at the
mathematically exact rate.
## Examples
### Dashboard — Smooth Gauge Display
```rust
let mut gauge = SpringF64::new(0.3);
// On each data update (potentially irregular):
let display_value = gauge.update(actual_value, dt).unwrap();
render_gauge(display_value);
```
### Trading — Adaptive Threshold
```rust
let mut threshold = SpringF64::new(1.0);
// Smoothly track a moving threshold target
let target = compute_new_threshold(market_conditions);
let smooth_threshold = threshold.update(target, dt).unwrap();
```
### Gaming — Camera Follow
```rust
let mut cam_x = SpringF64::new(0.2);
let mut cam_y = SpringF64::new(0.2);
let sx = cam_x.update(player_x, dt).unwrap();
let sy = cam_y.update(player_y, dt).unwrap();
```
## Performance
| `SpringF64::update` | 12 cycles | 12 cycles |
~10 floating-point ops. No transcendentals (Padé approximant avoids
`exp()`). Consistent timing — no data-dependent branches.