Docker Usage
Lotio provides Docker images for easy deployment and consistent rendering across different environments:
matrunchyk/lotio:latest- Contains lotio binary with built-in video encoding support
Quick Start
Best Practice: Use --output-format mov for direct video encoding:
docker pull matrunchyk/lotio:latest
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--data data.json \
--output-format mov \
--output video.mov \
--fps 30
This provides direct video encoding with built-in support. See Examples for more options.
Pulling from Docker Hub
Pre-built Docker images are available on Docker Hub:
docker pull matrunchyk/lotio:latest
docker pull matrunchyk/lotio:v1.2.3
The images are automatically built and pushed for each release. Available tags:
- latest - Always points to the most recent release (multi-platform manifest)
- v1.2.3 - Specific version tag (multi-platform manifest)
- v1.2.3-arm64 - Architecture-specific tag for ARM64
- v1.2.3-amd64 - Architecture-specific tag for x86_64
Multi-platform support: The image supports linux/arm64 and linux/amd64. Docker automatically selects the correct platform.
Building the Images
Build Chain
The Docker images use a multi-stage build chain for optimized builds:
Dockerfile.skia → matrunchyk/skia:latest
↓
Dockerfile.lotio → matrunchyk/lotio:latest
Building from Source
Build lotio image:
docker buildx build \
--platform linux/arm64,linux/amd64 \
-t matrunchyk/lotio:test \
-f Dockerfile.lotio \
--build-arg SKIA_IMAGE=matrunchyk/skia:latest \
--push .
Note: The lotio image uses matrunchyk/skia:latest as base (built separately using build_skia_docker_multi.sh).
Image Details
matrunchyk/lotio:latest:
- Base Image: matrunchyk/skia:latest (contains pre-built Skia)
- Architecture: Multi-platform (linux/arm64, linux/amd64)
- Includes:
- lotio binary (/usr/local/bin/lotio)
- lotio static library (/usr/local/lib/liblotio.a)
- lotio headers (/usr/local/include/lotio/)
- Skia libraries and headers (from base image)
- Built-in video encoding support (libavcodec/libavformat)
- Runtime dependencies
Basic Usage
Render Animation to Video (Recommended - Best Performance)
Best Practice: Use --output-format mov for direct video encoding:
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--output-format mov \
--data data.json --fps 30 \
--output video.mov
This combination provides:
- Fastest encoding (up to 15x faster than PNG mode, 65% faster than ProRes 4444 10-bit)
- Smallest file sizes (75% smaller than ProRes)
- Automatic dimension extraction from input JSON
- Full alpha channel support
- Direct encoding using built-in libavcodec/libavformat (no external FFmpeg binary needed)
With Input Directory
docker run --rm \
-v "$(pwd)/samples:/workspace/input:ro" \
-v "$(pwd):/workspace/output" \
matrunchyk/lotio:latest \
--data /workspace/input/data.json --fps 30 --output /workspace/output/video.mov
Command-Line Arguments
Lotio Arguments
All standard lotio arguments are supported:
--output-format <format>- Output format:png(default),raw,ffv1, ormov(direct video encoding)--output <file>- Output file path (use-to stream to stdout, required forraw|ffv1|movformats)- Automatically extracts dimensions from input JSON (using
jqorgrepfallback) - Dimensions can be overridden with
--widthand--heightflags - Dimensions are required for raw RGBA mode (must be available from JSON or flags)
- Best for maximum performance when encoding to video
--debug- Enable debug output--layer-overrides <file>- Path to layer overrides JSON- Absolute paths: Used as-is (e.g.,
/workspace/input/layer-overrides.json) - Relative paths: Resolved relative to the current working directory (cwd) inside the container
- Example: If the container's cwd is
/workspaceand you use--layer-overrides config/overrides.json, it resolves to/workspace/config/overrides.json
- Example: If the container's cwd is
- The parent directory of this file is used as the base directory for resolving relative image paths in
imageLayers.filePath --fonts <dir>- Directory containing font files (e.g..ttf). When set, fonts are looked up here first, then in the JSON directory and itsfonts/subdirectory. Paths resolved like other CLI args (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 mode (default: accurate)--version- Print version information and exit--help, -h- Show help message--data <input.json>- Input Lottie animation file (required)- Absolute paths: Used as-is (e.g.,
/workspace/input/data.json) - Relative paths: Resolved relative to the current working directory (cwd) inside the container
- Example: If the container's cwd is
/workspaceand you use--data data.json, it resolves to/workspace/data.json
- Example: If the container's cwd is
- The parent directory of this file is used as the base directory for resolving relative image paths in the Lottie JSON
--fps <fps>- Frames per second (optional, default: animation fps or 30)<output_dir>- Output directory (required for PNG format when not streaming; use--output -to stream)
Note: Frames are output as PNG by default. Use --output-format to encode video directly.
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).
Text Measurement Mode
The --text-measurement-mode option controls the accuracy vs performance trade-off:
fast: Fastest measurement using basic font metricsaccurate(default): Good balance, accounts for kerning and glyph metricspixel-perfect: Most accurate, accounts for anti-aliasing and subpixel rendering
Examples
Direct Video Encoding (No FFmpeg Binary Required)
New in latest version: Use --output-format to encode video directly without requiring the FFmpeg binary:
# MOV format with QTRLE codec (fast, preserves alpha, widely compatible)
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--data data.json \
--output-format mov \
--output video.mov \
--fps 30
# FFV1 format (lossless, good compression, preserves alpha)
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--data data.json \
--output-format ffv1 \
--output video.mkv \
--fps 30
# Raw RGBA format (fastest, no encoding, largest files, preserves alpha)
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--data data.json \
--output-format raw \
--output video.rgb \
--fps 30
Benefits:
- No FFmpeg binary dependency (uses libavcodec/libavformat libraries)
- All formats preserve alpha channel for transparency
- Faster than piping to FFmpeg (no inter-process communication overhead)
- Perfect for stage 2 processing (compositing with other videos)
Format comparison:
- mov (QTRLE): Fast encoding, medium file size, widely compatible
- ffv1: Medium encoding speed, good compression, lossless
- raw: Fastest (zero encoding), largest files, perfect for further processing
Recommended: Best Performance (qtrle + Raw RGBA)
This is the recommended approach for best performance:
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--data data.json \
--output-format mov \
--output video.mov \
--fps 30
Why this is best:
- Direct encoding (bypasses ffmpeg binary, fastest)
- Smallest file sizes (QTRLE codec)
- Full alpha channel support
- Automatic dimension extraction from JSON
Basic Video Generation
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--data data.json --fps 30 --output-format mov --output video.mov
With Layer Overrides
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--data data.json \
--layer-overrides layer-overrides.json \
--fps 30 \
--output-format mov \
--output video.mov
Direct Video Encoding (Maximum Performance)
Recommended: Use --output-format mov for direct encoding (bypasses ffmpeg, fastest).
# Direct encoding (fastest)
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--data data.json \
--output-format mov \
--output video.mov \
--fps 30
Performance: Raw RGBA mode is significantly faster than PNG mode, making it ideal for high-volume rendering pipelines.
Dimensions: The entrypoint script automatically extracts dimensions from the input JSON file (using jq to read w and h fields, with grep as fallback). Dimensions are required for raw RGBA mode - if they cannot be extracted from JSON, the script will exit with an error.
With Custom Codec Selection
# MOV format with QTRLE codec (default, fastest)
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--output-format mov \
--data data.json \
--fps 30 \
--output video.mov
With Custom Text Padding and Measurement Mode
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--data data.json \
--layer-overrides layer-overrides.json \
--text-padding 0.95 \
--text-measurement-mode pixel-perfect \
--fps 30 \
--output-format mov \
--output video.mov
With Debug Output
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--data data.json \
--debug \
--fps 30 \
--output-format mov \
--output video.mov
Using PNG Output Format
If you want to render frames as PNG files:
# Render frames to disk
docker run --rm \
-v "$(pwd):/workspace" \
matrunchyk/lotio:latest \
--data data.json --fps 30 frames/
Output Format
The lotio image supports multiple output formats with alpha channel support. The default video codec is qtrle (QuickTime RLE) for maximum speed.
Video Specifications
- Default Codec: QuickTime RLE (
qtrle) - Fastest option, smallest files - Alternative Codecs: ProRes 4444 (8-bit or 10-bit) - Better compatibility, higher quality
- Container: MOV (QuickTime)
- Transparency: Full alpha channel support
- Input Format:
image2pipe(PNG frames) orrawvideo(raw RGBA bytes) depending on mode
Codec Details
QuickTime RLE (qtrle) - Default:
- Pixel Format: argb
- Fastest encoding (65% faster than ProRes 4444 10-bit)
- Smallest file sizes (75% smaller than ProRes)
- Best for simple graphics/text content
- May have playback issues in some players
ProRes 4444 (8-bit):
- Pixel Format: yuva444p
- Balanced option, QuickTime compatible
- Smooth playback
ProRes 4444 (10-bit):
- Pixel Format: yuva444p10le
- Highest quality, slower encoding
- Widely compatible with video editing software
Volume Mounts
Input Files
Mount your Lottie JSON files and any dependencies:
-v "$(pwd)/samples:/workspace/input:ro"
- Use
:ro(read-only) for input files - Include text configuration files in the same mount
- Include any image assets referenced in the Lottie JSON
Output Directory
Mount a directory for output:
-v "$(pwd):/workspace/output"
The output video will be written to the path specified by --output.
Layer Overrides
Layer overrides work the same as the CLI. Mount your layer overrides file and reference it:
docker run --rm \
-v "$(pwd)/samples:/workspace/input:ro" \
-v "$(pwd):/workspace/output" \
matrunchyk/lotio:latest \
--data /workspace/input/data.json \
--layer-overrides /workspace/input/layer-overrides.json \
--fps 30 \
--output /workspace/output/video.mov
Example layer-overrides.json:
{
"textLayers": {
"Text_1": {
"minSize": 50,
"maxSize": 222,
"fallbackText": "Default text",
"textBoxWidth": 720,
"value": "Custom text value"
},
"Text_2": {
"value": "Another text"
}
},
"imageLayers": {
"image_0": {
"filePath": "images/",
"fileName": "logo.png"
},
"image_1": {
"filePath": "/workspace/input/",
"fileName": "bg.png"
}
}
}
Image Layers in Layer Overrides
Path Resolution:
- Absolute paths: Used as-is (e.g., /workspace/input/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/input/layer-overrides.json and filePath is "images/", it resolves relative to /workspace/input/
- Important: This is different from input.json and --layer-overrides paths, which are resolved relative to the current working directory
- 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
Notes:
- Image paths in the original Lottie JSON (data.json) are resolved relative to data.json's parent directory
- If an asset ID is not in imageLayers, the original u and p from data.json are used
- Both filePath and fileName are optional - if not specified, defaults from assets[].u and assets[].p are used
Fonts
Fonts can be provided in two ways:
--fonts <dir>— Mount a directory and pass it to lotio. Fonts are looked up in this directory first, then in the JSON directory and itsfonts/subdirectory.
docker run --rm \
-v "$(pwd)/myfonts:/workspace/fonts:ro" \
-v "$(pwd)/animation.json:/workspace/input.json:ro" \
-v "$(pwd):/workspace/output" \
matrunchyk/lotio:latest \
--data /workspace/input.json --fonts /workspace/fonts --fps 30 --output /workspace/output/video.mov
- System fonts (fontconfig) — Mount fonts into a system font directory so fontconfig can find them:
docker run --rm \
-v "$(pwd)/fonts:/usr/local/share/fonts:ro" \
-v "$(pwd)/animation.json:/workspace/input.json:ro" \
-v "$(pwd):/workspace/output" \
matrunchyk/lotio:latest \
--data /workspace/input.json --fps 30 --output /workspace/output/video.mov
Fonts should be in TTF or OTF format. With --fonts, lotio looks in that directory by name (e.g. OpenSans-Bold.ttf); with fontconfig, fonts are discovered by fontconfig.
Environment Variables
The container sets up the following environment variables:
PATH: Includes/usr/local/binLD_LIBRARY_PATH: Includes Skia and FFmpeg library paths
Troubleshooting
Video Not Generated
Check that:
- Input file path is correct and mounted
- Relative paths are resolved relative to the current working directory (cwd) inside the container
- Use absolute paths (e.g., /workspace/input/data.json) for clarity
- Output directory is writable
- Sufficient disk space is available
- Image paths in imageLayers are correct
- Relative paths in imageLayers.filePath are resolved relative to the layer-overrides.json file's directory (NOT the current working directory)
Text Not Replacing
Verify:
- Layer overrides file is mounted and accessible (supports both absolute and relative paths)
- Layer names in layer-overrides.json match layer names in Lottie JSON
- Layer overrides file is valid JSON
Image Path Issues
If images aren't loading:
- Verify image paths in imageLayers are correct
- Relative paths in imageLayers.filePath are resolved relative to the layer-overrides.json file's directory (NOT the current working directory)
- Example: If layer-overrides.json is at /workspace/config/overrides.json and imageLayers has "image_0": { "filePath": "images/", "fileName": "logo.png" }, it resolves to /workspace/config/images/logo.png
- Absolute paths in imageLayers.filePath are used as-is
- URLs (http://, https://) and data URIs (data:) are NOT supported in imageLayers
- Ensure image files exist and are accessible from within the container
Font Issues
If fonts aren't loading:
- Use --fonts <dir> to point to a mounted directory containing the required .ttf files (e.g. --fonts /workspace/fonts), or mount fonts into a system font path for fontconfig
- Ensure the fonts directory is mounted and readable
- Check font file format (TTF/OTF)
- Verify font names match those in Lottie JSON (e.g. OpenSans-Bold → OpenSans-Bold.ttf)
Debug Mode
Enable debug output to see detailed information:
docker run --rm \
-v "$(pwd)/animation.json:/workspace/input.json:ro" \
-v "$(pwd):/workspace/output" \
matrunchyk/lotio:latest \
--data /workspace/input.json --debug --fps 30 --output /workspace/output/video.mov
Performance
Optimization Features
The Docker container includes several performance optimizations:
- Direct Video Encoding (
--output-format mov|ffv1|raw): Fastest option, bypasses ffmpeg binary - Direct encoding within lotio using libavcodec/libavformat
- Eliminates inter-process communication overhead
-
Best for high-volume rendering pipelines
-
Optimized Codec Selection: Default
qtrlecodec for maximum speed - Fastest encoding (up to 65% faster than ProRes 4444 10-bit)
- Smallest file sizes (75% smaller than ProRes)
-
Best for simple graphics/text content
-
Constant Frame Rate (CFR): Ensures smooth playback and faster encoding
- Explicit frame rate setting
- Optimized timescale (30000)
- Better frame timing precision
Performance Comparison
| Mode | Encoding Time | File Size | Use Case |
|---|---|---|---|
| PNG + ProRes 4444 10-bit | ~13s | 76M | Baseline (original) |
| Raw RGBA + ProRes 4444 8-bit | ~1.86s | 76M | Balanced (24% faster) |
| Raw RGBA + qtrle | ~0.85s | 19M | Fastest (65% faster, 75% smaller) |
Total improvement: ~15x faster than original implementation.
Container Performance
The Docker container uses:
- Multi-threaded rendering (one thread per CPU core)
- Optimized Skia build with official release settings
- Hardware-accelerated encoding where available
For best performance:
- Use --output-format mov for maximum speed (direct encoding)
- Use SSD storage for input/output
- Allocate sufficient CPU cores to the container
- Use local volume mounts (not network filesystems)
CI/CD Integration
The Docker image can be used in CI/CD pipelines:
- name: Render animation
run: |
docker pull matrunchyk/lotio:latest
docker run --rm \
-v "${{ github.workspace }}/animation.json:/workspace/input.json:ro" \
-v "${{ github.workspace }}/output:/workspace/output" \
matrunchyk/lotio:latest \
--data /workspace/input.json --fps 30 \
--output /workspace/output/video.mov
Build Optimizations
Docker Build Chain
The build chain is optimized for speed:
- Dockerfile.skia - Builds Skia once (takes 15-20 minutes, but cached)
- Dockerfile.lotio - Uses pre-built Skia + builds lotio with built-in video encoding (takes 2-3 minutes)
Total build time: ~20-30 minutes (first time), but subsequent builds are much faster due to caching.
Lambda/Container Integration
When using the lotio image in Lambda functions or containers, you can use lotio directly with built-in video encoding support:
Command Format
lotio [LOTIO_OPTIONS] --data <input.json> [--fps <fps>] --output-format <format> --output <video.mov>
Key Points
- Direct video encoding: Use
--output-format mov|ffv1|rawfor direct encoding (no external FFmpeg binary needed) - All lotio options supported:
--layer-overrides,--fonts,--text-padding,--text-measurement-mode,--debugall work - Dimension extraction: Dimensions are automatically extracted from JSON (using
jqorgrepfallback)
Complete Example
lotio \
--layer-overrides /tmp/overrides.json \
--text-padding 0.95 \
--text-measurement-mode pixel-perfect \
--data /tmp/input.json \
--fps 30 \
--output-format mov \
--output /tmp/output.mov
TypeScript/Node.js Integration Example
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
// Build command arguments
const args = [
"--data", inputJsonPath,
"--output-format", "mov",
"--output", outputVideoPath
];
// Add optional arguments
if (layerOverridesPath) {
args.push("--layer-overrides", layerOverridesPath);
}
if (textPadding !== undefined) {
args.push("--text-padding", textPadding.toString());
}
if (textMeasurementMode) {
args.push("--text-measurement-mode", textMeasurementMode);
}
if (fps !== undefined) {
args.push("--fps", fps.toString());
}
// Execute
await execAsync(`lotio ${args.map(a => `"${a}"`).join(" ")}`);
Limitations
- Architecture: Multi-platform support (
linux/arm64,linux/amd64) - Platform: Designed for Linux containers (may work on macOS/Windows with proper Docker setup)
- Fonts: Requires fonts to be mounted; system fonts not included
See Also
- CLI Usage - Command-line interface documentation
- Overview - General overview of Lotio
- C++ Library - Programmatic usage