Text
Text is essential for mathematical animation, teaching visuals, and explanatory content. Murali provides four text tattvas, each optimized for different use cases.
All text tattvas live under murali::frontend::collection::text.
If you are choosing between text systems for a real scene, start here and then read Math, Animations, and Examples.
Quick Decision Guide
| Need | Use | Why |
|---|---|---|
| Simple text, titles, labels | Label | Fast, lightweight, no dependencies |
| Mathematical equations | Latex or Typst | Industry standard (Latex) or modern (Typst) |
| Rich formatted text | Typst | Modern typesetting, no external tools |
| Syntax-highlighted code | CodeBlock | Built-in highlighting, monospace |
Which Text Tattva Should I Pick?
Use Label when the text is short, direct, and scene-native: titles, axis labels, captions, counters, callouts, and UI-like annotations.
Use Latex when you want standard academic math rendering and are comfortable depending on a local LaTeX toolchain.
Use Typst when you want richer formatting, modern typesetting, or math/text content without leaning on a full LaTeX install.
Use CodeBlock when the code itself is part of the visual explanation and syntax highlighting matters.
The most common beginner mistake is overusing Latex for everything. For titles, annotations, and short labels, Label is usually the better tool.
Label
Lightweight glyph-based text using fontdue. Best for short strings, numbers, titles, and UI text.
use murali::frontend::collection::text::label::Label;
Label::new(text: impl Into<String>, world_height: f32)
Parameters:
text- The text contentworld_height- Font size in world units (not pixels!)
Basic usage:
let title = Label::new("Hello, World!", 0.36)
.with_color(Vec4::new(0.9, 0.9, 0.9, 1.0));
scene.add_tattva(title, Vec3::new(0.0, 3.0, 0.0));
Styling
// Set color
Label::new("Colored Text", 0.32)
.with_color(Vec4::new(0.2, 0.6, 0.9, 1.0))
// Common colors
let white = Vec4::new(1.0, 1.0, 1.0, 1.0);
let black = Vec4::new(0.0, 0.0, 0.0, 1.0);
let red = Vec4::new(0.9, 0.3, 0.3, 1.0);
let blue = Vec4::new(0.2, 0.6, 0.9, 1.0);
Text Animations
Label supports two reveal modes for character-by-character animations:
Reveal Mode (Default)
Text reveals from center, characters shift into place:
let label = Label::new("Hello, World!", 0.32)
.with_char_reveal(0.0); // Start hidden
scene.add_tattva(label, Vec3::ZERO);
// Animate the reveal
timeline.call_during(0.0, 2.0, move |scene, t| {
if let Some(label) = scene.get_tattva_typed_mut::<Label>(label_id) {
label.char_reveal = t; // 0.0 to 1.0
}
});
Or use the built-in animation:
timeline
.animate(label_id)
.at(0.0)
.for_duration(2.0)
.ease(Ease::OutCubic)
.reveal_text()
.spawn();
Typewriter Mode
Text appears left-to-right, position stays fixed:
let label = Label::new("Hello, World!", 0.32)
.with_char_reveal(0.0);
scene.add_tattva(label, Vec3::ZERO);
// Enable typewriter mode
if let Some(label) = scene.get_tattva_typed_mut::<Label>(label_id) {
label.typewriter_mode = true;
}
// Animate
timeline
.animate(label_id)
.at(0.0)
.for_duration(2.0)
.ease(Ease::Linear) // Usually linear for typing
.typewrite_text()
.spawn();
Font Sizing
Typical sizes:
- 0.24-0.28 - Body text, annotations
- 0.32-0.36 - Titles, headings
- 0.40-0.50 - Large titles, emphasis
- 0.18-0.22 - Small text, subscripts
Rule of thumb: Start with 0.32 and adjust based on camera view width.
When to Use Label
✅ Use Label for:
- Titles and headings
- UI text and labels
- Numbers and simple annotations
- Axis labels
- Short text (< 100 characters)
- When you need fast rendering
❌ Don't use Label for:
- Mathematical equations (use Latex or Typst)
- Rich formatted text (use Typst)
- Code with syntax highlighting (use CodeBlock)
- Very long paragraphs (performance)
Performance
Label is the fastest text option:
- No external tools required
- Direct glyph rendering
- Minimal overhead
- Good for many labels in one scene
Best animation pairings:
appear()for titles and short labelsfade_to(...)for subtle emphasis/de-emphasisreveal_text()for teaching-style revealstypewrite_text()when a left-to-right typing feel matters
Latex
Renders mathematical equations using LaTeX. Industry standard for academic and scientific content.
use murali::frontend::collection::text::latex::Latex;
Latex::new(source: impl Into<String>, world_height: f32)
Parameters:
source- LaTeX source codeworld_height- Height of the rendered equation in world units
Basic Usage
// Simple equation
let eq = Latex::new(r"\frac{a}{b} + \sqrt{c}", 0.32)
.with_color(Vec4::new(0.9, 0.9, 0.9, 1.0));
scene.add_tattva(eq, Vec3::ZERO);
Common LaTeX Patterns
// Fractions
Latex::new(r"\frac{numerator}{denominator}", 0.32)
// Square root
Latex::new(r"\sqrt{x}", 0.32)
// Superscript and subscript
Latex::new(r"x^2 + y_i", 0.32)
// Greek letters
Latex::new(r"\alpha + \beta = \gamma", 0.32)
// Integrals
Latex::new(r"\int_0^\infty e^{-x} dx", 0.32)
// Summation
Latex::new(r"\sum_{i=1}^n i^2", 0.32)
// Matrices
Latex::new(r"\begin{bmatrix} a & b \\ c & d \end{bmatrix}", 0.32)
// Aligned equations
Latex::new(r"\begin{aligned} x &= 2 \\ y &= 3 \end{aligned}", 0.32)
Text Reveal Animation
Latex supports character reveal like Label:
let eq = Latex::new(r"E = mc^2", 0.36)
.with_char_reveal(0.0);
scene.add_tattva(eq, Vec3::ZERO);
timeline
.animate(eq_id)
.at(0.0)
.for_duration(2.0)
.reveal_text()
.spawn();
System Requirements
Required tools:
latex- LaTeX compilerdvisvgm- DVI to SVG converter
Installation:
# macOS
brew install --cask mactex
# Ubuntu/Debian
sudo apt-get install texlive-latex-base texlive-latex-extra dvisvgm
# Arch Linux
sudo pacman -S texlive-core texlive-bin
Caching
Latex results are cached to disk by source hash:
- First render: Slow (compiles LaTeX)
- Subsequent renders: Fast (loads from cache)
- Cache location: System temp directory
When to Use Latex
✅ Use Latex for:
- Mathematical equations
- Academic/scientific notation
- Complex formulas
- When you need LaTeX-specific packages
- Industry-standard math rendering
❌ Don't use Latex for:
- Simple text (use Label)
- When you can't install LaTeX
- Real-time editing (slow first render)
- Code (use CodeBlock)
Gotchas
Escaping backslashes:
// Use raw strings (r"...") to avoid double-escaping
Latex::new(r"\frac{a}{b}", 0.32) // ✅ Good
// Without raw string, you need double backslashes
Latex::new("\\frac{a}{b}", 0.32) // ✅ Also works but ugly
Compilation errors:
- Invalid LaTeX syntax will cause render failure
- Check terminal output for LaTeX error messages
- Test equations in a LaTeX editor first
Best animation pairings:
reveal_text()for equation revealsfade_to(...)for staging supporting formulas- vector-formula morph workflows when continuity matters more than static raster output
Typst
Modern typesetting system with built-in math support. No external tools required!
Typst is a strong middle ground between simple labels and full LaTeX. It works well for formatted teaching text, math-heavy notes, and scenes where you want cleaner authoring than raw LaTeX strings.
use murali::frontend::collection::text::typst::Typst;
Typst::new(source: impl Into<String>, world_height: f32)
Parameters:
source- Typst markupworld_height- Height in world units
Basic Usage
// Math equation
let eq = Typst::new(r"$\frac{a}{b} + \sqrt{c}$", 0.32)
.with_color(Vec4::new(0.9, 0.9, 0.9, 1.0));
scene.add_tattva(eq, Vec3::ZERO);
Typst Math Syntax
// Fractions
Typst::new(r"$a/b$", 0.32) // Inline
Typst::new(r"$frac(a, b)$", 0.32) // Function style
// Square root
Typst::new(r"$sqrt(x)$", 0.32)
// Superscript and subscript
Typst::new(r"$x^2 + y_i$", 0.32)
// Greek letters
Typst::new(r"$alpha + beta = gamma$", 0.32)
// Integrals
Typst::new(r"$integral_0^infinity e^(-x) dif x$", 0.32)
// Summation
Typst::new(r"$sum_(i=1)^n i^2$", 0.32)
// Matrices
Typst::new(r"$mat(a, b; c, d)$", 0.32)
Rich Text
Typst supports formatted text, not just math:
// Bold and italic
Typst::new("*Bold* and _italic_ text", 0.28)
// Mixed text and math
Typst::new("The equation $E = mc^2$ is famous", 0.28)
// Lists
Typst::new("- Item 1\n- Item 2\n- Item 3", 0.24)
Caching
Typst results are cached in an LRU cache:
- Cache size: 128 entries
- Key: source + height
- Much faster than Latex (no external process)
When to Use Typst
✅ Use Typst for:
- Mathematical equations (no LaTeX installation needed)
- Rich formatted text
- Modern, clean syntax
- When you want fast compilation
- Mixed text and math
❌ Don't use Typst for:
- Simple text (use Label - it's faster)
- When you need specific LaTeX packages
- Code with syntax highlighting (use CodeBlock)
Typst vs Latex
| Feature | Typst | Latex |
|---|---|---|
| Installation | None (built-in) | Requires LaTeX + dvisvgm |
| Speed | Fast | Slow (first render) |
| Syntax | Modern, clean | Traditional, verbose |
| Math support | Excellent | Excellent |
| Packages | Limited | Extensive |
| Learning curve | Easier | Steeper |
Recommendation: Use Typst unless you specifically need LaTeX packages or have existing LaTeX code.
CodeBlock
Syntax-highlighted code using Typst's #raw block.
use murali::frontend::collection::text::code_block::CodeBlock;
CodeBlock::new(
code: impl Into<String>,
language: impl Into<String>,
world_height: f32
)
Parameters:
code- Source codelanguage- Language identifier (e.g., "rust", "python", "javascript")world_height- Per-line height in world units
Basic Usage
let code = CodeBlock::new(
"fn main() {\n println!(\"Hello, world!\");\n}",
"rust",
0.24
).with_color(Vec4::new(0.9, 0.9, 0.9, 1.0));
scene.add_tattva(code, Vec3::ZERO);
Supported Languages
Common languages:
"rust"- Rust"python"- Python"javascript"/"js"- JavaScript"typescript"/"ts"- TypeScript"c"- C"cpp"/"c++"- C++"java"- Java"go"- Go"bash"/"sh"- Shell scripts"json"- JSON"yaml"- YAML"toml"- TOML"sql"- SQL
Multi-line Code
let code = r#"
fn fibonacci(n: u32) -> u32 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
"#;
let code_block = CodeBlock::new(code, "rust", 0.22);
scene.add_tattva(code_block, Vec3::ZERO);
Sizing
Typical per-line heights:
- 0.20-0.22 - Small code snippets
- 0.24-0.26 - Standard code blocks
- 0.28-0.30 - Large, emphasized code
Note: world_height is per-line height. Total height depends on number of lines.
When to Use CodeBlock
✅ Use CodeBlock for:
- Code snippets
- Algorithm demonstrations
- Programming tutorials
- Terminal output
- Configuration files
❌ Don't use CodeBlock for:
- Plain text (use Label)
- Math equations (use Latex or Typst)
- Prose (use Typst)
Gotchas
Bounds estimation:
- Bounds are estimated from line count and max line length
- May not be pixel-perfect for all fonts
- Usually close enough for layout purposes
Syntax highlighting:
- Powered by Typst's built-in highlighter
- Limited customization
- Colors are theme-dependent
Comparison Table
| Feature | Label | Latex | Typst | CodeBlock |
|---|---|---|---|---|
| Speed | Fastest | Slow (first) | Fast | Fast |
| Dependencies | None | latex, dvisvgm | None | None |
| Math support | None | Excellent | Excellent | None |
| Syntax highlighting | No | No | No | Yes |
| Rich text | No | Limited | Yes | No |
| Best for | Simple text | Math equations | Math + text | Code |
| Reveal animation | Yes | Yes | No | No |
| Caching | N/A | Disk | LRU (128) | LRU (128) |
Tooling And Performance Notes
Labelis the lightest-weight option and scales best when you have many text objects.Latexhas the heaviest setup cost because it depends on external tooling and a compile step.Typstis a good default when you need richer formatting but want a smoother authoring story than LaTeX.CodeBlockis specialized: excellent for code visuals, not a replacement for general-purpose text.
Animation Patterns
Fade In Text
// Works for all text types
timeline
.animate(text_id)
.at(0.0)
.for_duration(1.0)
.ease(Ease::OutCubic)
.appear()
.spawn();
Typewriter Effect (Label only)
let label = Label::new("Hello, World!", 0.32)
.with_char_reveal(0.0);
let label_id = scene.add_tattva(label, Vec3::ZERO);
// Enable typewriter mode
if let Some(label) = scene.get_tattva_typed_mut::<Label>(label_id) {
label.typewriter_mode = true;
}
timeline
.animate(label_id)
.at(0.0)
.for_duration(2.0)
.ease(Ease::Linear)
.typewrite_text()
.spawn();
Reveal Effect (Label, Latex)
timeline
.animate(text_id)
.at(0.0)
.for_duration(1.5)
.ease(Ease::OutCubic)
.reveal_text()
.spawn();
Sequential Text Appearance
let texts = vec![
"First line",
"Second line",
"Third line",
];
let mut current_time = 0.0;
for text in texts {
let id = scene.add_tattva(
Label::new(text, 0.28),
Vec3::new(0.0, current_y, 0.0),
);
timeline
.animate(id)
.at(current_time)
.for_duration(0.8)
.appear()
.spawn();
current_time += 1.0;
current_y -= 0.5;
}
Best Practices
Choosing Text Type
- Start with Label - Use for 90% of text needs
- Use Typst for math - If you don't have LaTeX installed
- Use Latex for math - If you have existing LaTeX code or need specific packages
- Use CodeBlock for code - Always, for syntax highlighting
Font Sizing
// Establish a size hierarchy
const TITLE_SIZE: f32 = 0.40;
const HEADING_SIZE: f32 = 0.32;
const BODY_SIZE: f32 = 0.24;
const SMALL_SIZE: f32 = 0.20;
Color Consistency
// Define a color palette
const TEXT_PRIMARY: Vec4 = Vec4::new(0.95, 0.95, 0.95, 1.0);
const TEXT_SECONDARY: Vec4 = Vec4::new(0.70, 0.75, 0.80, 1.0);
const TEXT_ACCENT: Vec4 = Vec4::new(0.20, 0.60, 0.90, 1.0);
Performance
- Many labels? Label is fastest
- Complex equations? Cache is your friend (both Latex and Typst)
- Real-time editing? Use Typst over Latex
- Hundreds of text objects? Consider Label over rendered text
Common Patterns
Title and Subtitle
let title = Label::new("Main Title", 0.40)
.with_color(Vec4::new(0.95, 0.95, 0.95, 1.0));
let title_id = scene.add_tattva(title, Vec3::ZERO);
scene.to_edge(title_id, Direction::Up, 0.35);
let subtitle = Label::new("Subtitle text", 0.24)
.with_color(Vec4::new(0.75, 0.80, 0.85, 1.0));
scene.add_tattva(subtitle, Vec3::new(0.0, 2.8, 0.0));
Related Docs
Equation with Label
// Equation
let eq_id = scene.add_tattva(
Typst::new(r"$E = mc^2$", 0.36),
Vec3::ZERO,
);
// Label below
let label_id = scene.add_tattva(
Label::new("Einstein's famous equation", 0.22),
Vec3::new(0.0, -1.0, 0.0),
);
Code with Title
let title_id = scene.add_tattva(
Label::new("Fibonacci Function", 0.32),
Vec3::new(0.0, 2.5, 0.0),
);
let code_id = scene.add_tattva(
CodeBlock::new(
"fn fib(n: u32) -> u32 {\n // ...\n}",
"rust",
0.24
),
Vec3::new(0.0, 0.0, 0.0),
);
Troubleshooting
Text not visible:
- Check color alpha (should be 1.0 for opaque)
- Verify camera position and view width
- Check if text is within frame bounds
Latex compilation fails:
- Verify latex and dvisvgm are installed
- Check terminal for LaTeX error messages
- Test equation in a LaTeX editor first
- Use raw strings:
r"\frac{a}{b}"
Text too small/large:
- Adjust
world_heightparameter - Remember: world units, not pixels
- Check camera view width
- Typical range: 0.20 to 0.50
Typewriter mode not working:
- Must set
typewriter_mode = trueon Label struct - Use
.typewrite_text()animation - Start with
char_reveal = 0.0
What's Next?
- Math - Equation layout and vector formulas
- Tables - Structured data display
- Primitives - Shapes to combine with text
- Animations - Text animation techniques