MCPcopy Index your code
hub / github.com/bugthesystem/natura

github.com/bugthesystem/natura @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
108 symbols 206 edges 8 files 14 documented · 13%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Natura

An opinionated, simple and efficient spring animation library for smooth, natural motion in Rust

Usage

Natura is framework-agnostic and works well in 2D and 3D contexts. Simply call Spring::new with your settings to initialize and update on each frame to animate.

For details, see the examples

Examples

Example with 2D engine coffee

cargo run -p coffee-2d

Example with Bevy Engine

cargo run -p bevy-simple

Enable Plugin and Spawn Multiple Sprites:

The Bevy plugin now uses Bevy's Time resource for frame-rate independent animation. No need to specify a delta time - the plugin handles it automatically!

use bevy::prelude::*;
use bevy_natura::{NaturaAnimationPlugin, NaturaSpringBundle, NaturaTarget};
use natura::{AngularFrequency, DampingRatio};

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(NaturaAnimationPlugin)
        .add_systems(Startup, setup)
        .run();
}

fn setup(mut commands: Commands) {
    commands.spawn(Camera2d::default());

    // Spawn first animated entity
    commands.spawn((
        Text2d::new("First sprite"),
        NaturaSpringBundle::new(
            AngularFrequency(6.0),
            DampingRatio(0.7),
        ),
        NaturaTarget::new_2d(100.0, 50.0),
    ));

    // Spawn second animated entity with bouncy spring (low damping)
    commands.spawn((
        Text2d::new("Second sprite - bouncy!"),
        NaturaSpringBundle::new(
            AngularFrequency(8.0),
            DampingRatio(0.3), // Under-damped: bouncy oscillation
        ),
        NaturaTarget::new_2d(-100.0, -50.0),
    ));
}

Advanced Features:

use bevy_natura::{
    AnimationGroup, AnimationPaused, AnimationStarted, AnimationCompleted,
    EasingCurve, GlobalAnimationPaused, PausedGroups,
};

// Use easing curves for different animation feels
commands.spawn((
    NaturaSpringBundle::with_easing(
        AngularFrequency(6.0),
        DampingRatio(0.7),
        EasingCurve::EaseOut,
    ),
    NaturaTarget::new_2d(100.0, 50.0),
));

// Group animations for batch control
commands.spawn((
    NaturaSpringBundle::new(AngularFrequency(6.0), DampingRatio(0.7)),
    NaturaTarget::new_2d(100.0, 50.0),
    AnimationGroup::new(1), // Group ID 1
));

// Pause individual entities
commands.entity(entity).insert(AnimationPaused);

// Pause entire groups
let mut paused_groups = world.resource_mut::<PausedGroups>();
paused_groups.pause(1); // Pause group 1

// Listen for animation events
fn on_animation_complete(mut events: EventReader<AnimationCompleted>) {
    for event in events.read() {
        println!("Entity {:?} finished at {:?}", event.entity, event.final_position);
    }
}

Please see full usage here

Simple example

cargo run -p simple

Code:


// Where we want to animate it.
const TARGET_X: f64 = 50.0;
const TARGET_Y: f64 = 100.0;

fn main() {
    let mut sprite = Sprite::default();

    // initialize a spring with frame-rate, angular frequency, and damping values.
    let mut spring = Spring::new(DeltaTime(natura::fps(60)), AngularFrequency(6.0), 0.5);

    loop {
        let (sprite_x, sprite_x_velocity) = spring.update(sprite.x, sprite.x_velocity, TARGET_X);
        sprite.x = sprite_x;
        sprite.x_velocity = sprite_x_velocity;

        let (sprite_y, sprite_y_velocity) = spring.update(sprite.y, sprite.y_velocity, TARGET_Y);
        sprite.y = sprite_y;
        sprite.y_velocity = sprite_y_velocity;

        sleep(Duration::from_millis(10000));

        // use new position here on every frame
        println!(
            "Sprite x:{}, y:{}, x_vel:{}, y_vel:{}",
            sprite.x, sprite.y, sprite.x_velocity, sprite.y_velocity
        )
    }
}

Settings

Spring::new takes three values:

  • Time Delta: the time step to operate on. Game engines typically provide a way to determine the time delta, however if that's not available you can simply set the framerate with the included fps(u64) utility function. Make the framerate you set here matches your actual framerate.
  • Angular Velocity: this translates roughly to the speed. Higher values are faster.
  • Damping Ratio: the springiness of the animation, generally between 0 and 1, though it can go higher. Lower values are springier. For details, see below.

Damping Ratios

The damping ratio affects the motion in one of three different ways depending on how it's set.

Under-Damping

A spring is under-damped when its damping ratio is less than 1. An under-damped spring reaches equilibrium the fastest, but overshoots and will continue to oscillate as its amplitude decays over time.

Critical Damping

A spring is critically-damped the damping ratio is exactly 1. A critically damped spring will reach equilibrium as fast as possible without oscillating.

Over-Damping

A spring is over-damped the damping ratio is greater than 1. An over-damped spring will never oscillate, but reaches equilibrium at a slower rate than a critically damped spring.

Acknowledgements

This library is a fairly straightforward port of Ryan Juckett’s excellent damped simple harmonic oscillator originally writen in C++ in 2008 and published in 2012. Ryan’s writeup on the subject is fantastic.

License

UNLICENSE

This crate is developed to be part of Λ.R.Ξ.N.Λ 2D game engine.

Core symbols most depended-on inside this repo

update
called by 8
bevy-natura/src/lib.rs
update
called by 4
natura/src/spring.rs
pause
called by 4
bevy-natura/src/lib.rs
fps
called by 3
natura/src/spring.rs
clone
called by 3
bevy-natura/src/lib.rs
load
called by 3
examples/coffee-2d/src/main.rs
resume
called by 2
bevy-natura/src/lib.rs
is_at_rest
called by 2
bevy-natura/src/lib.rs

Shape

Function 56
Method 29
Class 21
Enum 2

Languages

Rust100%

Modules by API surface

bevy-natura/src/lib.rs80 symbols
natura/src/spring.rs11 symbols
natura/src/projectile.rs7 symbols
examples/coffee-2d/src/main.rs5 symbols
natura/src/sprite.rs2 symbols
examples/bevy-simple/src/main.rs2 symbols
examples/simple/src/main.rs1 symbols

For agents

$ claude mcp add natura \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact