CLI Usage

The Lotio command-line interface provides a powerful way to render Lottie animations to image frames.

Installation

Homebrew (macOS)

brew tap matrunchyk/lotio

brew install lotio

From Source

# Build lotio (binary build with zero bundled dependencies)

./scripts/build_binary.sh

Basic Usage

lotio --data <input.json> [--output-format <format>] [--output <file>] [OPTIONS] [--fps <fps>] <output_dir>

Path Resolution

Important: Understanding how paths are resolved is crucial for correct file access.

Command-Line Arguments (--data, --layer-overrides)

Image Paths in imagePaths (layer-overrides.json)

Summary

Path Type Resolution Base
--data (relative) Current working directory (cwd)
--layer-overrides (relative) Current working directory (cwd)
--fonts (relative) Current working directory (cwd)
imagePaths in layer-overrides.json (relative) layer-overrides.json file's directory

Arguments

Options

Note: Frames are output as PNG by default. Use --output-format raw --output - to stream raw RGBA for maximum performance when piping to ffmpeg.

Text Padding

The --text-padding option controls how much of the target text box width is used for text sizing. A value of 0.97 means 97% of the target width is used, leaving 3% padding (1.5% per side). Lower values provide more padding, higher values allow text to use more of the available space.

Text Measurement Mode

The --text-measurement-mode option controls the accuracy vs performance trade-off for measuring text width:

Example: --text-measurement-mode pixel-perfect

Examples

Render to PNG

lotio --data animation.json --fps 30 frames/

This will create PNG files in the frames/ directory:

frames/
|
├── frame_0000.png
|
├── frame_0001.png
|
├── frame_0002.png
|
└── ...

Direct Video Encoding (No FFmpeg Binary Required)

# MOV format with QTRLE codec (fast, preserves alpha, widely compatible)

lotio --data animation.json --output-format mov --output video.mov --fps 30

# FFV1 format (lossless, good compression, preserves alpha)

lotio --data animation.json --output-format ffv1 --output video.mkv --fps 30

# Raw RGBA format (fastest, no encoding, largest files, preserves alpha)

lotio --data animation.json --output-format raw --output video.rgb --fps 30

These formats encode video directly using lotio's built-in encoding (libavcodec/libavformat). No FFmpeg binary is required. All formats preserve alpha channel for transparency.

Format comparison:
- mov (QTRLE): Fast encoding, medium file size (~20-50MB for 720x1280, 410 frames), widely compatible
- ffv1: Medium encoding speed, good compression (~500-800MB for same), lossless
- raw: Fastest (zero encoding), largest files (~1.5GB for same), perfect for stage 2 processing

Stream to ffmpeg

# PNG mode (compatible, default)

lotio --data animation.json --output-format png --output - --fps 30 | ffmpeg -f image2pipe -i - output.mp4

# Raw RGBA mode (faster, requires ffmpeg rawvideo input)

lotio --data animation.json --output-format raw --output - --fps 30 | ffmpeg -f rawvideo -pix_fmt rgba -s 720x1280 -r 30 -i - output.mp4

This streams frames directly to ffmpeg for video encoding without creating intermediate files. Raw RGBA mode is significantly faster (up to 15x) but requires specifying dimensions and frame rate to ffmpeg.

Performance comparison:
- PNG mode: Compatible, works with standard image2pipe format
- Raw RGBA mode: ~15x faster, requires rawvideo input format with dimensions

With Layer Overrides

lotio --data animation.json --layer-overrides layer-overrides.json --fps 30 frames/

The layer overrides file allows you to:
- Replace text layer values dynamically
- Auto-fit font sizes to text boxes
- Override image paths by asset ID
- Customize text and image appearance

With Custom Text Padding

lotio --data animation.json --text-padding 0.95 --layer-overrides layer-overrides.json --fps 30 frames/

Use 95% of text box width (5% padding) instead of the default 97% (3% padding).

With Pixel-Perfect Text Measurement

lotio --data animation.json --text-measurement-mode pixel-perfect --layer-overrides layer-overrides.json --fps 30 frames/

Use the most accurate text measurement mode for precise text sizing.

Example layer-overrides.json:

{

"textLayers": {

"Patient_Name": {

"minSize": 20,

"maxSize": 100,

"textBoxWidth": 500,

"value": "John Doe"

}

}

}

Debug Mode

lotio --data animation.json --debug --fps 30 frames/

Enables verbose logging for troubleshooting.

Layer Overrides

The layer overrides JSON file supports text and image customization. All fields are optional.

Text Layers

Define text layer properties:

{

"textLayers": {

"LayerName": {

"minSize": 10, // Optional: Minimum font size (no auto-fit if not specified)

"maxSize": 200, // Optional: Maximum font size (no auto-fit if not specified)

"fallbackText": "...", // Optional: Fallback text if text doesn't fit (defaults to empty)

"textBoxWidth": 500, // Optional: Text box width for auto-fit (defaults to frame size)

"value": "New Text", // Optional: Text value to set (defaults to original text from data.json)

"direction": 0 // Optional: Text direction (0 = LTR, 1 = RTL, omit to preserve existing)

}

}

}

Validation Rules:
- If both minSize and maxSize are specified: maxSize > minSize
- minSize > 0, maxSize > 0, textBoxWidth > 0 if specified
- If minSize and maxSize are not specified, no auto-fit is performed (original font size is used)
- value can be an empty string (uses original text from data.json)
- direction must be 0 (LTR) or 1 (RTL) if specified; if omitted, preserves existing direction from Lottie JSON

Examples:

{

"textLayers": {

"Text_1": {

"minSize": 50,

"maxSize": 222,

"fallbackText": "FALLBACK",

"textBoxWidth": 720,

"value": "OV3"

},

"Text_2": {

"value": "OV1" // Only text value override, no auto-fit

},

"Text_3": {

"minSize": 50,

"maxSize": 222

// No value override, uses original text from data.json

},

"Hebrew_Text": {

"value": "שלום עולם",

"direction": 1 // RTL for Hebrew text

},

"English_Text": {

"value": "Hello World",

"direction": 0 // LTR for English text

}

}

}

Text layer visibility requirements

Text layers overridden via textLayers must be visible in the Lottie composition for overridden text to appear. Lotio does not fix invalid or mis-exported comps at runtime.

Track matte when tp is omitted: Lotio builds Skottie with a track-matte patch. When the matte target index tp is omitted in the Lottie JSON, the matte source is the nearest prior layer with td !== 0 (Bodymovin behavior). Comps where the fill layer has tt !== 0, the matte layer has td !== 0, and tp is omitted (e.g. sample5) typically render correctly without editing data.json.

Requirements: Overridden text layers must not be:

  1. Under a null parent with opacity 0 — If a text layer’s parent (or ancestor) is a null layer (ty: 3) with constant opacity ks.o.k === 0, the text will not render. Fix in data.json by setting that null’s layers[].ks.o.k to 100, or fix in AE/Bodymovin.
  2. Using track matte on the text layer — If the text layer has tt !== 0 (track matte mode) and the matte source is wrong (e.g. tp points to the wrong layer), it may render invisible. When tp is omitted, lotio's patch uses the nearest prior layer with td !== 0; no data.json fix needed in that case. Otherwise fix in data.json by setting tt to 0 or correcting tp, or adjust in AE/Bodymovin.
  3. Masked by a track matte layer — If the layer above the text’s topmost parent has td !== 0 (track matte) and the fill layer’s tp (when present) points to the wrong layer, the text may be fully masked. When tp is omitted, lotio’s patch uses the nearest prior td !== 0 layer. Otherwise fix in data.json by setting td to 0 and "hd": true on that matte layer, or fix in AE/Bodymovin.

Runtime behavior: When applying overrides, lotio checks for these conditions and, if found, logs a [WARNING] to stderr with the layer name and concrete fix steps (e.g. “set tt to 0 on the layer in data.json, or adjust in AE/Bodymovin”). Fix the Lottie JSON or re-export from After Effects when the built-in track-matte patch does not apply.

Image Layers

Override image asset paths by asset ID:

{

"imageLayers": {

"image_0": {

"filePath": "images/", // Optional: Directory path (defaults to assets[].u)

"fileName": "img_0.png" // Optional: Filename (defaults to assets[].p)

}

}

}

Path Resolution:
- Absolute paths: Used as-is (e.g., /workspace/images/logo.png)
- Relative paths: Resolved relative to the layer-overrides.json file's directory (NOT the current working directory)
- Example: If layer-overrides.json is at /workspace/config/layer-overrides.json and filePath is "images/", it resolves relative to /workspace/config/
- URLs are NOT supported: HTTP (http://) and HTTPS (https://) URLs are not supported
- Empty filePath: If filePath is an empty string, fileName must contain the full path

Validation Rules:
- If fileName is specified but filePath is an empty string: validates full path from fileName
- If both filePath and fileName are specified (non-empty): validates combined path exists
- If both filePath and fileName are empty: error (invalid configuration)
- Non-existent asset IDs: warning only (ignored)

Examples:

{

"imageLayers": {

"image_0": {

// Full path: /tmp/folder/img_0.png

"filePath": "",

"fileName": "/tmp/folder/img_0.png"

},

"image_1": {

// Full path: /tmp/folder/my_image.png

"filePath": "/tmp/folder/",

"fileName": "my_image.png"

},

"image_2": {

// Full path: <layer-overrides-dir>/folder/my_image22.png

"filePath": "folder/",

"fileName": "my_image22.png"

}

}

}

Complete Example

{

"textLayers": {

"Title": {

"minSize": 20,

"maxSize": 100,

"textBoxWidth": 800,

"value": "Welcome to Lotio"

},

"Subtitle": {

"minSize": 12,

"maxSize": 50,

"textBoxWidth": 600,

"value": "High-performance Lottie renderer"

}

},

"imageLayers": {

"image_0": {

"filePath": "images/",

"fileName": "custom_logo.png"

},

"image_1": {

"filePath": "/path/to/",

"fileName": "background.png"

}

}

}

Output Format

Frames are output as PNG files by default. PNG provides:
- Lossless compression
- Widely supported format
- Perfect for video encoding pipelines

Streaming

Performance Tips

  1. Use raw RGBA for video: Use --output-format raw --output - when piping to ffmpeg for maximum speed (up to 15x faster than PNG)
  2. Stream for video: Use --output - when piping to ffmpeg to avoid disk I/O
  3. Adjust FPS: Lower FPS means fewer frames to render (faster)
  4. Multi-threading: Lotio automatically uses multiple CPU cores

Performance optimization example:

# Fastest: Raw RGBA mode

lotio --data animation.json --output-format raw --output - --fps 30 | \

ffmpeg -f rawvideo -pix_fmt rgba -s 720x1280 -r 30 -i - \

-c:v libx264 -pix_fmt yuv420p output.mp4

Troubleshooting

"Animation file not found"

"Output directory cannot be created"

"Text layer not found"

"Font loading failed"

Advanced Usage

Batch Processing

for file in animations/*.json; do

lotio --data "$file" --fps 30 "output/$(basename "$file" .json)/"

done

Integration with Video Encoding

# Stream PNG frames to ffmpeg

lotio --data animation.json --output-format png --output - --fps 30 | \

ffmpeg -f image2pipe -r 30 -i - \

-c:v libx264 -pix_fmt yuv420p \

output.mp4

# Stream raw RGBA frames to ffmpeg (faster)

lotio --data animation.json --output-format raw --output - --fps 30 | \

ffmpeg -f rawvideo -pix_fmt rgba -s 720x1280 -r 30 -i - \

-c:v libx264 -pix_fmt yuv420p \

output.mp4

Programmatic Integration (Lambda/Container)

For programmatic use in Lambda functions or containers, you can build the command with optional arguments:

# Basic command structure

lotio --data <input.json> [--output-format <format>] [--output <file>] [OPTIONS] [--fps <fps>] [output_dir]

# With all options (streaming PNG)

lotio --data input.json \

--output-format png \

--output - \

--layer-overrides overrides.json \

--text-padding 0.95 \

--text-measurement-mode pixel-perfect \

--fps 30

# Direct video encoding (no streaming)

lotio --data input.json \

--output-format mov \

--output video.mov \

--layer-overrides overrides.json \

--text-padding 0.95 \

--text-measurement-mode pixel-perfect \

--fps 30

Key points for integration:
- --data is required to specify the input JSON file
- --output-format defaults to png (PNG frames to directory)
- Use --output - to stream to stdout (for piping to ffmpeg)
- --fps is optional (defaults to animation fps or 30)
- output_dir is required for PNG format when not streaming
- All options can be combined
- Options can appear in any order

Custom Frame Range

Currently, Lotio renders all frames. For frame ranges, you can:
1. Render all frames
2. Use external tools to select specific frames
3. Or use the C++ library for programmatic control

See Also