Skip to main content
Version: Next 🚧

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

NeedUseWhy
Simple text, titles, labelsLabelFast, lightweight, no dependencies
Mathematical equationsLatex or TypstIndustry standard (Latex) or modern (Typst)
Rich formatted textTypstModern typesetting, no external tools
Syntax-highlighted codeCodeBlockBuilt-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 content
  • world_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 labels
  • fade_to(...) for subtle emphasis/de-emphasis
  • reveal_text() for teaching-style reveals
  • typewrite_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 code
  • world_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 compiler
  • dvisvgm - 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 reveals
  • fade_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 markup
  • world_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

FeatureTypstLatex
InstallationNone (built-in)Requires LaTeX + dvisvgm
SpeedFastSlow (first render)
SyntaxModern, cleanTraditional, verbose
Math supportExcellentExcellent
PackagesLimitedExtensive
Learning curveEasierSteeper

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 code
  • language - 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

FeatureLabelLatexTypstCodeBlock
SpeedFastestSlow (first)FastFast
DependenciesNonelatex, dvisvgmNoneNone
Math supportNoneExcellentExcellentNone
Syntax highlightingNoNoNoYes
Rich textNoLimitedYesNo
Best forSimple textMath equationsMath + textCode
Reveal animationYesYesNoNo
CachingN/ADiskLRU (128)LRU (128)

Tooling And Performance Notes

  • Label is the lightest-weight option and scales best when you have many text objects.
  • Latex has the heaviest setup cost because it depends on external tooling and a compile step.
  • Typst is a good default when you need richer formatting but want a smoother authoring story than LaTeX.
  • CodeBlock is 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

  1. Start with Label - Use for 90% of text needs
  2. Use Typst for math - If you don't have LaTeX installed
  3. Use Latex for math - If you have existing LaTeX code or need specific packages
  4. 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));

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_height parameter
  • Remember: world units, not pixels
  • Check camera view width
  • Typical range: 0.20 to 0.50

Typewriter mode not working:

  • Must set typewriter_mode = true on 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