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)
- Absolute paths: Used as-is (e.g.,
/path/to/file.json) - Relative paths: Resolved relative to the current working directory (cwd) where you execute lotio
- Example: If you run
lotio --data animation.jsonfrom/home/user/project/, it looks for/home/user/project/animation.json - The cwd is where you run the command, not where the lotio binary is located
Image Paths in imagePaths (layer-overrides.json)
- 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.jsonis at/workspace/config/overrides.jsonandimagePathshas"image_0": "images/logo.png", it resolves to/workspace/config/images/logo.png - This is different from command-line arguments, which use the current working directory
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
--data <input.json>- Path to Lottie animation JSON file (required)- Absolute paths: Used as-is (e.g.,
/path/to/animation.json) - Relative paths: Resolved relative to the current working directory (cwd) where lotio is executed
- Example: If you run
lotio --data animation.jsonfrom/home/user/project/, it resolves to/home/user/project/animation.json
- Example: If you run
- The parent directory of this file is used as the base directory for resolving relative image paths in the Lottie JSON
--output-format <format>- Output format:png(default),raw,ffv1, ormov(optional)png: PNG frames written to directory (default behavior)raw: Uncompressed RGBA video file (requires--output, fastest, largest files, preserves alpha)ffv1: Lossless FFV1 codec in Matroska container (requires--outputwith.mkvextension, good compression, preserves alpha)mov: MOV container with QTRLE codec (requires--output, fast encoding, preserves alpha, widely compatible)--output <file>- Output file path (required forraw,ffv1,movformats)--fps <fps>- Frames per second for output (optional, default: animation fps or 30)<output_dir>- Directory to save rendered frames (required forpngformat when not streaming; use--output -to stream)
Options
--data <input.json>- Path to input Lottie animation JSON file (required)--output-format <format>- Output format:png(default),raw,ffv1, ormov- When using
raw,ffv1, ormov, lotio encodes video directly (no ffmpeg binary needed) - All video formats preserve alpha channel for transparency
--output <file>- Output file path (use-to stream to stdout, required forraw|ffv1|movformats)--debug- Enable debug output--layer-overrides <config.json>- Path to layer overrides JSON (for text auto-fit, dynamic text values, and image path overrides)- Absolute paths: Used as-is (e.g.,
/path/to/layer-overrides.json) - Relative paths: Resolved relative to the current working directory (cwd) where lotio is executed
- Example: If you run
lotio --layer-overrides config/overrides.jsonfrom/home/user/project/, it resolves to/home/user/project/config/overrides.json
- Example: If you run
- The parent directory of this file is used as the base directory for resolving relative image paths in
imagePaths --fonts <dir>- Directory containing font files (e.g..ttf). When set, fonts are looked up here first, then in the JSON file's directory and itsfonts/subdirectory. Paths follow the same resolution as other CLI arguments (absolute as-is, relative to cwd).--text-padding <0.0-1.0>- Text padding factor (default: 0.97 = 3% padding)--text-measurement-mode <fast|accurate|pixel-perfect>- Text measurement accuracy mode (default: accurate)--fps <fps>- Frames per second for output (optional, default: animation fps or 30)--version- Print version information and exit--help, -h- Show help message
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.
- Range:
0.0to1.0 - Default:
0.97(3% padding) - Example:
--text-padding 0.95uses 95% of width (5% padding)
Text Measurement Mode
The --text-measurement-mode option controls the accuracy vs performance trade-off for measuring text width:
fast: Fastest measurement using basic font metrics. Good for most cases but may underestimate width for some fonts.accurate(default): Good balance of accuracy and performance. Uses SkTextBlob bounds which accounts for kerning and glyph metrics. Recommended for most use cases.pixel-perfect: Most accurate measurement by rendering text and scanning actual pixels. Accounts for anti-aliasing and subpixel rendering. Slower but most precise.
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:
- Under a null parent with opacity 0 — If a text layer’s parent (or ancestor) is a null layer (
ty: 3) with constant opacityks.o.k === 0, the text will not render. Fix in data.json by setting that null’slayers[].ks.o.kto100, or fix in AE/Bodymovin. - 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.tppoints to the wrong layer), it may render invisible. Whentpis omitted, lotio's patch uses the nearest prior layer withtd !== 0; no data.json fix needed in that case. Otherwise fix in data.json by settingttto0or correctingtp, or adjust in AE/Bodymovin. - Masked by a track matte layer — If the layer above the text’s topmost parent has
td !== 0(track matte) and the fill layer’stp(when present) points to the wrong layer, the text may be fully masked. Whentpis omitted, lotio’s patch uses the nearest priortd !== 0layer. Otherwise fix in data.json by settingtdto0and"hd": trueon 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
- No intermediate files
- Direct to stdout as PNG
- Perfect for video encoding pipelines
Performance Tips
- Use raw RGBA for video: Use
--output-format raw --output -when piping to ffmpeg for maximum speed (up to 15x faster than PNG) - Stream for video: Use
--output -when piping to ffmpeg to avoid disk I/O - Adjust FPS: Lower FPS means fewer frames to render (faster)
- 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"
- Check the path to your JSON file specified with
--data - Relative paths are resolved relative to the current working directory (cwd) where you run lotio
- Example: If you're in
/home/user/project/and runlotio --data animation.json, it looks for/home/user/project/animation.json - Use absolute paths if relative paths don't work
- Verify the file exists and you have read permissions
"Output directory cannot be created"
- Ensure you have write permissions
- Create the directory first:
mkdir -p output_dir
"Text layer not found"
- Verify the layer name matches exactly (case-sensitive)
- Check the Lottie JSON structure
"Font loading failed"
- Ensure fonts are available in the system font directories, or use
--fonts <dir>to point to a directory containing the required.ttffiles (e.g. next to the JSON or a shared project fonts folder) - Check font file paths in the Lottie JSON; by default lotio looks in the JSON file's directory and its
fonts/subdirectory
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
- Overview - General information about Lotio
- JS Library - Browser/WebAssembly usage
- C++ Library - Programmatic C++ usage