Your First Scene
This guide walks you through creating a minimal Murali scene from scratch. By the end, you'll have a working animation with shapes, text, and motion.
What You'll Build​
A simple scene with:
- A title label
- Two shapes (a square and a circle)
- Smooth animated motion between positions
Prerequisites​
Make sure you have Murali installed. If not, see the Introduction for setup instructions.
Step 1: Create a New File​
Create a new file in your project's examples/ directory or in a new binary crate:
# If using the Murali repo
touch examples/my_first_scene.rs
# Or create a new binary project
cargo new --bin my_first_scene
cd my_first_scene
# Add murali to Cargo.toml dependencies
Step 2: Import What You Need​
Start with the essential imports:
use glam::{Vec3, Vec4};
use murali::App;
use murali::colors::*;
use murali::positions::*;
use murali::engine::scene::Scene;
use murali::engine::timeline::Timeline;
use murali::frontend::animation::Ease;
use murali::frontend::collection::primitives::{circle::Circle, square::Square};
use murali::frontend::collection::text::label::Label;
fn main() -> anyhow::Result<()> {
// We'll build the scene here
Ok(())
}
Step 3: Create a Scene​
The Scene is the container for all your objects:
fn main() -> anyhow::Result<()> {
let mut scene = Scene::new();
// More code will go here
Ok(())
}
Step 4: Add a Title​
Add a text label at the top of your scene:
fn main() -> anyhow::Result<()> {
let mut scene = Scene::new();
// Add a title
scene.add_tattva(
Label::new("My First Scene", 0.32)
.with_color(WHITE),
3.0 * UP,
);
Ok(())
}
What's happening here:
Label::new("My First Scene", 0.32)creates a text label with font size 0.32.with_color(...)sets the text color (RGBA values from 0.0 to 1.0)3.0 * UPpositions the label at coordinates (x=0, y=3, z=0)
Step 5: Add Shapes​
Now add two shapes that we'll animate:
fn main() -> anyhow::Result<()> {
let mut scene = Scene::new();
// Title
scene.add_tattva(
Label::new("My First Scene", 0.32)
.with_color(WHITE),
3.0 * UP,
);
// Add a red square on the left
let square_id = scene.add_tattva(
Square::new(1.2, RED_B),
4.0 * LEFT,
);
// Add a green circle on the right
let circle_id = scene.add_tattva(
Circle::new(0.65, 48, GREEN_D),
4.0 * RIGHT,
);
Ok(())
}
Important: Notice we're saving the IDs returned by add_tattva. We'll need these to animate the shapes.
Parameters explained:
Square::new(1.2, color)- size of 1.2 units, with a red colorCircle::new(0.65, 48, color)- radius of 0.65, 48 segments for smoothness, green color
Step 6: Set Up the Camera​
Configure the camera to frame your scene properly:
fn main() -> anyhow::Result<()> {
let mut scene = Scene::new();
// ... (previous code for title and shapes)
// Configure camera
scene.camera_mut().position = CAMERA_DEFAULT_POS;
scene.camera_mut().set_view_width(16.0);
Ok(())
}
The camera is positioned at z=10 (looking toward the origin) with a view width of 16 units.
Step 7: Create a Timeline and Add Animations​
Now for the fun part - making things move:
fn main() -> anyhow::Result<()> {
let mut scene = Scene::new();
// ... (previous code)
// Create a timeline
let mut timeline = Timeline::new();
// Animate the square
timeline
.animate(square_id)
.at(0.0) // Start at time 0
.for_duration(2.0) // Take 2 seconds
.ease(Ease::InOutQuad) // Smooth easing
.move_to(2.0 * RIGHT) // Move to new position
.spawn();
// Animate the circle
timeline
.animate(circle_id)
.at(0.5) // Start at time 0.5
.for_duration(2.0) // Take 2 seconds
.ease(Ease::OutQuad) // Different easing
.move_to(2.0 * LEFT) // Move to new position
.spawn();
Ok(())
}
What's happening:
- We create animations using the builder pattern
.animate(id)targets a specific tattva by its ID.at(time)sets when the animation starts.for_duration(seconds)sets how long it takes.ease(...)controls the motion curve (smooth vs linear).move_to(position)is the animation verb - what actually changes.spawn()adds the animation to the timeline
Step 8: Play the Timeline​
Tell the scene to use this timeline:
fn main() -> anyhow::Result<()> {
let mut scene = Scene::new();
// ... (all previous code)
// Play the timeline
scene.play(timeline);
Ok(())
}
Step 9: Run the App​
Finally, create and run the app:
fn main() -> anyhow::Result<()> {
let mut scene = Scene::new();
// ... (all previous code)
// Run the app
App::new()?.with_scene(scene).run_app()
}
Complete Code​
Here's the full example:
use glam::{Vec3, Vec4};
use murali::App;
use murali::engine::scene::Scene;
use murali::engine::timeline::Timeline;
use murali::frontend::animation::Ease;
use murali::frontend::collection::primitives::{circle::Circle, square::Square};
use murali::frontend::collection::text::label::Label;
fn main() -> anyhow::Result<()> {
let mut scene = Scene::new();
// Title
scene.add_tattva(
Label::new("My First Scene", 0.32)
.with_color(WHITE),
3.0 * UP,
);
// Shapes
let square_id = scene.add_tattva(
Square::new(1.2, RED_B),
4.0 * LEFT,
);
let circle_id = scene.add_tattva(
Circle::new(0.65, 48, GREEN_D),
4.0 * RIGHT,
);
// Camera
scene.camera_mut().position = CAMERA_DEFAULT_POS;
scene.camera_mut().set_view_width(16.0);
// Timeline
let mut timeline = Timeline::new();
timeline
.animate(square_id)
.at(0.0)
.for_duration(2.0)
.ease(Ease::InOutQuad)
.move_to(2.0 * RIGHT)
.spawn();
timeline
.animate(circle_id)
.at(0.5)
.for_duration(2.0)
.ease(Ease::OutQuad)
.move_to(2.0 * LEFT)
.spawn();
scene.play(timeline);
// Run
App::new()?.with_scene(scene).run_app()
}
Running Your Scene​
Preview Mode​
To see your animation in a window:
cargo run --example my_first_scene --release -- --preview
Without --preview, Murali exports by default rather than opening an interactive window.
For explicit export:
cargo run --example my_first_scene --release -- --export
Controls:
- [O] - Switch to orbit camera mode
- [P] - Switch to pan/zoom camera mode
- [Drag] - Move the camera
- [Scroll] - Zoom in/out
- [Esc] - Exit
Export Mode​
To export your animation as a video:
cargo run --example my_first_scene --release
Or explicitly:
cargo run --example my_first_scene --release -- --export
This will create an MP4 file in your output directory.
Understanding the Coordinate System​
Murali uses a right-handed coordinate system:
- X-axis: Left (negative) to right (positive)
- Y-axis: Down (negative) to up (positive)
- Z-axis: Into screen (negative) to out of screen (positive)
The origin (0, 0, 0) is at the center of the frame.
Key Concepts​
Scene​
The container for all objects, timelines, and state. It's the source of truth for your animation.
Tattva​
Any visual object in Murali (shapes, text, graphs, etc.). Each tattva has:
- A unique ID
- A position in 3D space
- Properties like color, scale, rotation, opacity
Timeline​
Schedules when animations happen and how long they take. You can have multiple timelines, but most scenes use just one.
Animation Verbs​
Methods that change tattva properties over time:
.move_to(position)- Change position.scale_to(scale)- Change size.rotate_to(quat)- Change rotation.fade_to(opacity)- Change opacity.appear()- Fade in from invisible- And many more...
What's Next?​
Now that you have a working scene, here's your recommended learning path:
Core Concepts (Start Here):
- Mental Model - Understand how Scene, Tattva, and Timeline work together
- Which API Should I Use? - Decision guide for common tasks
- Common First Mistakes - Avoid common pitfalls
Expand Your Skills:
- Animations - Complete reference for all animation verbs
- Tattvas - Discover all shapes, text, graphs, and components
- Scene and App - Advanced scene management
- Camera - Control camera movement and framing
By Use Case:
- Teaching math → Text, Math, Graphs
- Data visualization → Graphs, Tables
- AI/ML explanations → AI Tattvas, Storytelling
Common First Mistakes​
Shapes Not Visible​
- Check that your camera is positioned correctly (usually at positive Z)
- Verify your shapes are within the camera's view width
- Make sure colors have alpha = 1.0 (fully opaque)
Animations Not Playing​
- Did you call
scene.play(timeline)before running the app? - Check that animation start times (
.at(...)) are reasonable - Verify you're using the correct tattva IDs
Colors Look Wrong​
- Colors use RGBA format with values from 0.0 to 1.0
- Example:
Vec4::new(1.0, 0.0, 0.0, 1.0)is bright red - The fourth value is alpha (transparency)
Troubleshooting​
"Cannot find tattva with ID"
- You're trying to animate a tattva that doesn't exist
- Make sure you saved the ID from
add_tattvaand used it correctly
Window opens but nothing appears
- Your objects might be outside the camera view
- Try adjusting camera position or view width
- Check that your positions make sense (not too far from origin)
Animation happens instantly
- Check your
.for_duration(...)value - it should be > 0 - Verify your timeline is actually being played with
scene.play(timeline)