Go from zero to animated in about 5 minutes.
Add the library to your Cargo.toml.
(Not yet on crates.io—use git dependency for now)
[dependencies]
mixed-signals = { git = "https://github.com/5ocworkshop/mixed-signals" }Import the prelude, create a signal, and sample it.
Core principle: All signals output bipolar [-1, 1]. Use .normalized() for TUI work.
use mixed_signals::prelude::*;
fn main() {
// A standard 1Hz sine wave (bipolar: -1.0 to 1.0)
let sine = Sine::default();
// Sample at t = 0.25 seconds (peak of sine)
let value = sine.sample(0.25);
println!("Bipolar value: {}", value); // Output: 1.0
// For TUI (opacity, progress bars): normalize to 0.0-1.0
let opacity = sine.normalized();
println!("Normalized: {}", opacity.sample(0.25)); // Output: 1.0
println!("Normalized: {}", opacity.sample(0.0)); // Output: 0.5 (center)
}Static waves are boring. Add "life" using noise.
Note: Noise requires a SignalContext to be deterministic (reproducible).
// 1. Create generators (both output bipolar [-1, 1])
let sine = Sine::new(1.0, 1.0, 0.0, 0.0); // The pulse
let noise = PerlinNoise::with_seed(42); // The organic jitter
// 2. Mix them and normalize for TUI
// Blend: 80% Sine, 20% Noise, then map to [0, 1]
let organic_pulse = sine.mix(noise, 0.2).normalized();
// 3. Create Context (Frame 100, Seed 42)
// Ensures frame 100 always looks the same, even on different machines.
let ctx = SignalContext::new(100, 42);
// 4. Sample
let val = organic_pulse.sample_with_context(0.5, &ctx);
// Returns a safe f32 between 0.0 and 1.0If you are building a TUI, you often want characters to animate independently but coherently.
let text = "LOADING...";
let noise = PerCharacterNoise::with_seed(99).normalized();
for (index, char) in text.chars().enumerate() {
// Context carries the character index!
let ctx = SignalContext::new(100, 42).with_char_index(index);
// Each character gets a unique, stable random value [0, 1]
let alpha = noise.sample_with_context(0.5, &ctx);
println!("Char '{}' alpha: {:.2}", char, alpha);
}Don't recompile to change animation speeds. Define signals in JSON/TOML and load them at runtime.
use mixed_signals::types::SignalSpec;
// Imagine this came from "config.json"
let json = r#"{
"type": "sine",
"frequency": 2.0,
"amplitude": 1.0,
"phase": 0.5
}"#;
let spec: SignalSpec = serde_json::from_str(json).unwrap();
let signal = spec.build().unwrap();
let v = signal.sample(0.1);Use deterministic physics solvers for natural motion.
use mixed_signals::physics::{DampedSpring, BouncingDrop};
// Bouncy modal drop-in (starts at y=0, drops 300px, settles at 500ms)
let drop = BouncingDrop::rubber_ball(0.0, 300.0, 500.0);
let y_position = drop.sample(t);
// Spring-loaded button snap-back
let spring = DampedSpring::default();
let displacement = spring.sample(t);- Explore: Run the demos:
cargo run --example visualizer --features visualization - Reference: Check
CHEATSHEET.mdfor the full API reference. - Demos: See
HOWTO-VISUALIZER.mdfor all interactive examples.