Skip to main content
Version: 0.1.6

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, CodeBlockSurface, CodeBlockTheme,
};

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 - Visible code text size in world units

Basic Usage

let code = CodeBlock::new(
"fn main() {\n println!(\"Hello, world!\");\n}",
"rust",
0.24
)
.with_theme(CodeBlockTheme::Dark)
.with_surface(CodeBlockSurface::Dark)
.with_title("main.rs")
.with_content_box_size(4.8, 1.6)
.with_content_offset(0.0, -0.04);

scene.add_tattva(code, Vec3::new(1.5, -0.4, 0.0));

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 controls the code text size. If you want a deterministic panel footprint, pair it with .with_content_box_size(width, height).

Layout Controls

  • Move the whole block by changing the position passed to scene.add_tattva(...)
  • Move only the code inside the panel with .with_content_offset(x, y)
  • Use .with_content_box_size(width, height) when you want explicit authored block sizing

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

Sizing behavior:

  • If you provide .with_content_box_size(...), that explicit authored size drives the block layout
  • Without an explicit content box, Murali falls back to a measured or estimated natural size
  • For production scenes, prefer explicit sizing when the exact panel footprint matters

Syntax highlighting:

  • Powered by Typst's built-in highlighter
  • This API currently exposes two built-in palettes: dark and light
  • The code panel surface is also chosen from built-in dark and light presets
  • Custom themes are intentionally deferred until the API matures

Large code blocks:

  • CodeBlock is currently rasterized to a GPU texture before rendering
  • Very large snippets can hit the device texture-size limit on some hardware
  • For now, prefer camera framing, shorter snippets, or smaller world_height if a block gets too large
  • A renderer-level fix for oversized blocks is planned for a future iteration

Comparison Table

FeatureLabelLatexTypstCodeBlock
SpeedFastestSlow (first)FastFast
DependenciesNonelatex, dvisvgmNoneNone
Math supportNoneExcellentExcellentNone
Syntax highlightingNoNoNoYes
Rich textNoLimitedYesNo
Best forSimple textMath equationsMath + textCode
Reveal animationYesYesNoYes
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, Latex, CodeBlock)

let code_id = scene.add_tattva(
CodeBlock::new("fn main() {}", "rust", 0.24),
Vec3::ZERO,
);

timeline
.animate(code_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