Animation: Damper, Spring, and Easing
Raw data is jagged. A mouse cursor jumps pixel by pixel. A MIDI knob sends discrete steps. A counter clicks from 3 to 4 instantly. None of that looks good when connected directly to a visual parameter.
You need smoothing. And not just one kind.
Damper
The “make it smooth” node. Plug it after any input and it interpolates toward the target value using exponential smoothing. The speed parameter controls how fast it catches up. Low speed means slow, flowing movement. High speed means snappy, responsive tracking.
I use this on almost everything. Mouse position into a damper into a circle’s position gives you that satisfying laggy follow. A slider value through a damper means parameter changes ease in instead of jumping. Audio levels through a damper give you smooth VU meters instead of flickering numbers.
It’s the node I reach for first, every time.
Spring
Physics-based smoothing. Instead of just easing toward a target, the spring overshoots, bounces back, and settles. Three parameters: stiffness (how hard it pulls toward the target), damping (how quickly the bouncing dies down), and mass (how heavy the thing being moved feels).
High stiffness and low damping gives you a bouncy rubber band. Low stiffness and high damping gives you something that feels like it’s moving through honey. Tuning these three values is half the fun.
Springs feel organic in a way that linear smoothing never does. When a circle bounces past its target and settles back, your eye believes it has physical weight. That’s what makes interactive installations feel tangible.
SmoothDamp
Unity-style critically damped smoothing. The key property: it never overshoots. It arrives at the target smoothly and stops exactly where it should. You give it a smooth time (how long it takes to reach the target) and it handles the rest.
This is for when you want smoothness without the bounciness of a spring. Camera movements, UI transitions, anything where overshoot would feel wrong.
Ease
Eight easing curves with in, out, and in-out variants for each: linear, quad, cubic, quart, sine, expo, circ, and bounce. Feed it a 0 to 1 progress value and it reshapes the curve.
Linear is a straight line. Quad starts slow and accelerates. Cubic is more dramatic. Expo is extreme. Bounce literally bounces at the end. Each one changes the feel of a transition completely.
I extracted all the easing math into a shared lux_core::math module so every node that needs easing uses the same implementation. The Ease node, the Ramp node, the ADSR, the Timeline, they all share the same curves.
ADSR
Attack, decay, sustain, release. The classic envelope shape from synthesizers, but for any value.
Feed it a gate signal (1.0 when on, 0.0 when off). When the gate opens, the value rises over the attack time, drops to the sustain level over the decay time, holds at sustain while the gate is open, then falls to zero over the release time when the gate closes.
Essential for anything trigger-driven. A pulse from a metro triggers an ADSR, and now you have a shaped burst instead of a flat bang. Connect that to opacity and you get a flash that fades. Connect it to scale and you get a pop that settles.
Ramp
The simplest of the bunch. Interpolate from A to B over a duration. One-shot or looping. Choose an easing curve for the shape.
It’s basically “get from here to there, smoothly.” Trigger it, it goes. Done.
Every one of these nodes has save_state and restore_state implemented, so undo works properly even with stateful smoothing. The spring remembers its velocity. The damper remembers its current value. Ctrl+Z puts them all back where they were.