spanda
Sanskrit: स्पन्द — vibration, pulse, the throb of motion.
A general-purpose animation library for Rust. Zero mandatory dependencies,
no_std-ready, and designed to work anywhere: terminal UIs, web (WASM),
game engines (Bevy), or native desktop apps.
Features
- Tweening — animate any value from A to B with 38+ easing curves
- Keyframe tracks — multi-stop animations with per-segment easing
- Timeline & Sequence — compose animations concurrently or sequentially
- Relative positioning — GSAP-style
At::Start,At::End,At::Label,At::Offset - Stagger — offset N animations with a single call
- Physics springs — damped harmonic oscillator with 4 presets +
SpringN<T>for 2D/3D/4D - Looping —
Loop::Once,Times(n),Forever,PingPongon tweens and keyframes - Time scale — speed up / slow down tweens and timelines at runtime
- Callbacks —
on_start,on_update,on_completeon tweens (stdfeature) - Value modifiers —
snap_to(grid),round_to(decimals), custom modifiers - Scroll-linked animation —
ScrollDriver/ScrollClockfor position-driven animations - Motion paths — quadratic/cubic Bezier, CatmullRom splines, SVG path parsing, arc-length parameterization
- Full motion path system —
PolyPath,CompoundPath,SvgPathParser, auto-rotate, start/end offsets - CSS easing —
CubicBezier(x1,y1,x2,y2)andSteps(n)on theEasingenum - Colour animation — 9 palette types +
InLab/InOklch/InLinearcolour-space-aware wrappers (palettefeature) - DrawSVG —
draw_on/draw_on_reversestroke-dashoffset helpers - Shape morphing —
MorphPathpoint-by-point morph with auto-resampling - Inertia physics —
Inertia/InertiaN<T>friction deceleration with presets - Advanced easings —
RoughEase,SlowMo,ExpoScale,Wiggle,CustomBounce - Drag tracking —
DragStatewith velocity EMA, bounds, axis lock, grid snap →InertiaNon release - WASM-DOM plugins — FLIP animations, SplitText, ScrollSmoother, Draggable, Observer (
wasm-domfeature) - Layout animation — automatic FLIP-style transitions with
LayoutAnimator, shared element transitions - Gesture recognition —
GestureRecognizerfor tap, swipe, long press, pinch, rotation - GPU compute shaders —
GpuAnimationBatchfor batch-evaluating 10,000+ tweens on the GPU (gpufeature) - Animation driver — manage multiple animations with auto-cleanup
- Clock abstraction — wall clock, manual clock, scroll clock, and mock clock for testing
Getting Started
[]
= "0.9.2"
Quick Example
use ;
use Update;
let mut tween = new
.duration
.easing
.build;
// Simulate 10 frames:
for _ in 0..10
assert!;
assert!;
Looping Tween
use ;
use Update;
let mut tween = new
.duration
.looping
.build;
// Runs forever, bouncing between 0 and 100
for _ in 0..600
Spring Animation
use ;
use Update;
let mut spring = new;
spring.set_target;
for _ in 0..300
assert!;
Multi-Dimensional Spring (SpringN)
use ;
use Update;
let mut spring = new;
spring.set_target;
for _ in 0..1000
let pos = spring.position; // [f32; 2]
assert!;
Timeline with Relative Positioning
use ;
use Tween;
use Easing;
let mut tl = new
.add;
// Place "slide" right after "fade" ends
tl.add_at;
// Place "glow" at the same time as "fade"
tl.add_at;
tl.play;
Staggered Animations
use stagger;
use Tween;
use Update;
let tweens: = .map.collect;
let mut timeline = stagger;
timeline.play;
// Animations start at t=0.0, 0.1, 0.2, 0.3, 0.4
Scroll-Linked Animation
use ScrollDriver;
use Tween;
let mut driver = new;
driver.add;
// Drive animation from scroll position instead of time
driver.set_position; // 50% scroll
Motion Path Animation
use ;
use Easing;
use Update;
let path = new
.cubic
.line;
let mut tween = new
.duration
.easing;
tween.update;
let pos = tween.value; // position along the curve
Smooth Path Through Points (PolyPath)
use PolyPath;
let path = from_points;
let pos = path.position; // arc-length parameterized
let angle = path.rotation_deg; // auto-rotate angle
SVG Path Parsing
use SvgPathParser;
use CompoundPath;
let commands = parse;
let path = new
.start_offset
.end_offset;
let pos = path.position;
CSS Cubic-Bezier Easing
use ;
use Update;
let mut tween = new
.duration
.easing // CSS ease
.build;
tween.update;
Colour Animation (palette feature)
use Srgba;
use ;
use InLab;
use Update;
// Interpolate in CIE L*a*b* for perceptually smooth gradients
let mut tween = new
.duration
.easing
.build;
tween.update;
let colour = tween.value.0; // Srgba
Sequence Composition
use Sequence;
use Tween;
use Update;
let mut timeline = new
.then
.gap
.then
.build;
timeline.play;
timeline.update;
Layout Animation (FLIP)
use ;
use Easing;
let mut layout = new;
layout.track;
layout.track;
// After layout mutation (e.g. items reordered)
let transitions = layout.compute_transitions;
// In animation loop:
layout.update;
if let Some = layout.css_transform
Gesture Recognition
use ;
use PointerData;
let mut recognizer = new;
// Feed pointer events
recognizer.on_pointer_down;
recognizer.update;
if let Some = recognizer.on_pointer_up
GPU Batch Animation
use GpuAnimationBatch;
use ;
let mut batch = new_auto; // GPU with CPU fallback
for i in 0..10_000
batch.tick;
let positions: & = batch.read_back;
Feature Flags
| Flag | What it adds |
|---|---|
std |
(default) wall-clock driver, thread-safe internals |
serde |
Serialize/Deserialize on all public types |
bevy |
SpandaPlugin for Bevy 0.18 |
wasm |
requestAnimationFrame driver |
wasm-dom |
DOM plugins: FLIP, SplitText, ScrollSmoother, Draggable, Observer |
palette |
Colour interpolation via the palette crate |
tokio |
async / .await on timeline completion |
gpu |
GPU compute shader batch animation via wgpu |
Bevy Integration
use *;
use ;
WASM Integration
use RafDriver;
let mut driver = new;
driver.pause; // pause animations
driver.resume; // resume
driver.set_time_scale; // 2x speed
// Call driver.tick(timestamp_ms) from your rAF callback.
Benchmarks
Tests
Project Structure
src/
├── lib.rs — crate root, re-exports
├── traits.rs — Interpolate, Animatable, Update
├── easing.rs — 38 easing functions + CubicBezier + Steps + 5 advanced
├── tween.rs — Tween<T>, TweenBuilder, TweenState
├── keyframe.rs — KeyframeTrack, Keyframe, Loop
├── timeline.rs — Timeline, Sequence, At, stagger
├── spring.rs — Spring, SpringConfig, SpringN, SpringAnimatable
├── clock.rs — Clock, WallClock, ManualClock, MockClock
├── driver.rs — AnimationDriver, AnimationId
├── scroll.rs — ScrollClock, ScrollDriver
├── path.rs — BezierPath, MotionPath, MotionPathTween
├── bezier.rs — CatmullRomSpline, PathEvaluate2D
├── motion_path.rs — PolyPath, CompoundPath, PathCommand
├── svg_path.rs — SvgPathParser (SVG d-attribute parser)
├── colour.rs — colour interpolation (feature = "palette")
├── svg_draw.rs — DrawSVG stroke-dashoffset helpers
├── morph.rs — MorphPath shape morphing + resample
├── inertia.rs — Inertia, InertiaN friction deceleration
├── drag.rs — DragState, DragConstraints, PointerData
├── layout.rs — LayoutAnimator, Rect, SharedElementTransition
├── gesture.rs — GestureRecognizer, Gesture, GestureConfig
├── gpu.rs — GpuAnimationBatch (feature = "gpu")
├── gpu_tween.wgsl — WGSL compute shader
└── integrations/
├── mod.rs
├── bevy.rs — SpandaPlugin (feature = "bevy")
├── wasm.rs — RafDriver (feature = "wasm")
├── split_text.rs — SplitText character/word splitting
├── flip.rs — FlipState, FlipAnimation (feature = "wasm-dom")
├── scroll_smoother.rs — ScrollSmoother (feature = "wasm-dom")
├── draggable.rs — Draggable DOM binding (feature = "wasm-dom")
└── observer.rs — Observer unified input (feature = "wasm-dom")
License
Licensed under either of
at your option.