Animation and derived values
Rune Engine treats animation as data attached to values, not as imperative per-frame mutation. A property stays as a RuneValue from recording through commit, then resolves analytically at frame time.
Mental model
static float or animated reference -> RuneValue transport
-> copied into commands / metadata / text / paths
-> snapshot copies animation descriptors
-> render-time resolve at absolute frame time
-> draw with resolved floatsResponsibilities and boundaries
RuneValueis the one transport type. It can hold a static float, an ordinary animation slot, or a derived token that resolves to a slot owned by an expression or path-sampling program.- Animation descriptors live first on the node, then get copied into the snapshot during commit.
- Derived values come from one ordered snapshot-local expression program. They are downstream outputs, not a second mutable animation system.
Stable current behavior
One transport for literals, animation, and derived outputs
RuneValue is a 32-bit NaN-boxed payload. Static floats travel by bit-cast, ordinary animated values store a slot index, and derived values store a token that resolves to the owning slot during replay.
Analytical curve families
Rune ships three descriptor families:
- spring — closed-form damped motion with precise settle time plus a shorter perceptual settle time for underdamped motion
- cubic bezier — duration-based easing with CSS-like control points clamped into
[0,1] - physics — analytical velocity/friction/gravity motion with terminal min/max bounds
All three resolve from the same absolute frame time, so manual seeking, preview, and export sample the same curve math.
Repeat belongs to bezier timing
AnimateRepeat() is a convenience wrapper over a linear cubic-bezier. Repeat modes apply only to duration-based bezier motion:
Looprestarts from the beginning each cyclePingPongreverses direction on alternating cyclesNonemakes the animation one-shot
Springs and physics do not repeat automatically because their natural completion comes from convergence or terminal bounds rather than fixed-duration cycles.
Retargeting stays on the native side
When current already references an animation slot, retargeting evaluates that old animation at the node's current frame time and reuses its current position and velocity:
- spring retargeting inherits velocity into the new spring
- bezier retargeting starts from the evaluated current value
- physics retargeting inherits the evaluated current velocity
Derived expression outputs created by CreateExpression() or SamplePath() are intentionally downstream-only in V1 and cannot be retargeted as the current input to a new animation.
Expressions and path sampling are one ordered program
CreateExpression() appends scalar bytecode into one snapshot-local DATA_EXPRESSION record. SamplePath() appends an EXPR_SAMPLE_PATH record into that same ordered program and allocates two derived outputs for sampled X and Y.
That matters because later expressions can read earlier sampled outputs without creating a second dependency scheduler.
SamplePath is source-aware and seekable
SamplePath() consumes managed-prepared measured path data: original path bytecode, source segments, metric knots, total length, and conservative source bounds. Replay clamps progress, converts it to arc distance, finds the matching source segment, and evaluates the original line/quad/cubic instruction. Because it is analytical and frame-time based, the same path follower can seek forwards, backwards, or to an arbitrary export frame.
Settle metadata drives completion and fast paths
AnimationMetadata exposes:
SettleTimefor precise engine completionPerceptualDurationfor narrative pacing on underdamped springsTargetValuefor settled or target-state reasoning
Story uses perceptual settle for wait: settle, while the renderer uses precise settle for frame skipping and target-value fast paths after a snapshot is fully settled.
Current limitations
- Derived outputs are not legal retarget sources in V1.
SamplePath()supports only static single-figure path payloads in V1.- Repeat modes apply only to cubic-bezier timing.
- Spring bounds stay deliberately conservative: because overshoot/extrema are not published analytically, springs and spring-fed expressions propagate as unbounded for clipping/allocation analysis.
How the pieces connect
The same RuneValue can appear in draw commands, layer metadata, brush descriptors, text spacing, or path trimming. Commit copies the referenced descriptors once. Replay resolves every ordinary slot at one absolute frame time, runs derived operations in declaration order, and then all subsystems draw from the same resolved value array.
That is why Story can seek and export deterministically: animation, followers, waits, motion blur look-back, and completion metadata all come from one shared time model.