npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lotio

v1.1.76

Published

High-performance Lottie animation frame renderer for the browser

Readme

Lotio

High-performance Lottie animation frame renderer using Skia. Renders animations to PNG frames for video encoding.

Installation

Homebrew (Recommended)

brew tap matrunchyk/lotio
brew install lotio

This installs:

  • Binary: lotio
  • Headers: /opt/homebrew/include/lotio/ (or /usr/local/include/lotio/)
  • Libraries: /opt/homebrew/lib/ (Skia static libraries)

From Source

Prerequisites (macOS):

brew install fontconfig freetype harfbuzz icu4c libpng ninja [email protected]
xcode-select --install

Build:

# Build lotio (binary build with zero bundled dependencies)
./scripts/build_binary.sh

Usage

Command Line

lotio --data <input.json> [--output-format <format>] [--output <file>] [--debug] [--layer-overrides <config.json>] [--fonts <dir>] [--text-padding <0.0-1.0>] [--text-measurement-mode <fast|accurate|pixel-perfect>] [--fps <fps>] <output_dir>

Options:

  • --data <input.json> - Path to input Lottie animation JSON file (required)
  • --output-format <format> - Output format: png (default), raw, ffv1, or mov
  • --output <file> - Output file path (use - to stream to stdout, required for raw, ffv1, mov formats)
  • --debug - Enable debug output
  • --layer-overrides <config.json> - Path to layer overrides JSON (for text auto-fit, dynamic text values, and image path overrides)
  • --fonts <dir> - Directory containing font files (.ttf); fonts are looked up here first, then in the JSON directory
  • --text-padding <0.0-1.0> - Text padding factor (0.0-1.0, default: 0.97 = 3% padding)
  • --text-measurement-mode <fast|accurate|pixel-perfect> - Text measurement mode: fast | accurate | pixel-perfect (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

Output Formats:

  • png (default) - PNG frames written to directory (or streamed to stdout with --output -)
  • raw - Uncompressed RGBA video file (fastest, largest files, preserves alpha)
  • ffv1 - Lossless FFV1 codec in Matroska container (good compression, preserves alpha)
  • mov - MOV container with QTRLE codec (fast encoding, preserves alpha, widely compatible)

Examples:

# Render to PNG frames (default)
lotio --data animation.json --fps 30 frames/

# Direct video encoding (no ffmpeg binary needed)
lotio --data animation.json --output-format mov --output video.mov --fps 30
lotio --data animation.json --output-format ffv1 --output video.mkv --fps 30
lotio --data animation.json --output-format raw --output video.rgb --fps 30

# Stream PNG to stdout (for piping to ffmpeg)
lotio --data animation.json --output-format png --output - --fps 30 | ffmpeg -f image2pipe -i - output.mp4

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

# With custom font directory
lotio --data animation.json --fonts ./fonts --fps 30 frames/

Docker

Quick start:

docker run --rm -v $(pwd):/workspace matrunchyk/lotio:latest \
  --data data.json --fps 30 --layer-overrides layer-overrides.json --output-format mov --output video.mov

Available images:

  • matrunchyk/lotio:latest - lotio binary with built-in video encoding support

Multi-platform support: The image supports linux/arm64 and linux/amd64.

See Docker Documentation for detailed usage.

Browser (WebAssembly)

Install from npm:

npm install lotio

Basic Usage:

import Lotio, { State, TextMeasurementMode } from 'lotio';

// Load fonts
const fontResponse = await fetch('./fonts/OpenSans-Bold.ttf');
const fontData = new Uint8Array(await fontResponse.arrayBuffer());

// Load animation
const animationResponse = await fetch('./animation.json');
const animationData = await animationResponse.json();

// Create animation instance
const animation = new Lotio({
  fonts: [{ name: 'OpenSans-Bold', data: fontData }],
  fps: 30,
  animation: animationData,
  layerOverrides: { /* optional layer overrides */ },
  textPadding: 0.97,  // Optional: text padding factor (default: 0.97)
  textMeasurementMode: TextMeasurementMode.ACCURATE,  // Optional: TextMeasurementMode.FAST | TextMeasurementMode.ACCURATE | TextMeasurementMode.PIXEL_PERFECT
  wasmPath: './lotio.wasm'
});

// Event handlers (fluent interface)
animation
  .on('error', (error, anim) => {
    console.error('Animation error:', error);
  })
  .on('loaded', (anim) => {
    console.log('Animation loaded');
    anim.start();
  })
  .on('start', (anim) => {
    console.log('Animation started');
  })
  .on('pause', (anim) => {
    console.log('Animation paused');
  })
  .on('stop', (anim) => {
    console.log('Animation stopped');
  })
  .on('end', (anim) => {
    console.log('Animation ended');
  })
  .on('frame', (frameNumber, time, anim) => {
    // Render to canvas
    const canvas = document.getElementById('canvas');
    anim.renderToCanvas(canvas, '#2a2a2a');
  });

// Control methods
animation
  .setFps(60)           // Change FPS
  .seek(10)             // Seek to frame 10
  .start()              // Start playback
  .pause()              // Pause
  .stop();              // Stop and reset

// Getters
const fps = animation.getFps();
const state = animation.getState(); // 'stopped' | 'paused' | 'loaded' | 'error' | 'playing'
const frame = animation.getCurrentFrame();
const info = animation.getAnimationInfo();

// Render current frame to canvas
const canvas = document.getElementById('canvas');
animation.renderToCanvas(canvas, '#ffffff');

// Cleanup
animation.destroy();

Full Example with Canvas:

<!DOCTYPE html>
<html>
<head>
  <title>Lotio Animation</title>
</head>
<body>
  <canvas id="canvas"></canvas>
  <button id="playBtn">Play</button>
  <button id="pauseBtn">Pause</button>
  <button id="stopBtn">Stop</button>
  
  <script type="module">
    import Lotio from 'lotio';
    
    let animation;
    
    async function init() {
      // Load font
      const fontRes = await fetch('./fonts/OpenSans-Bold.ttf');
      const fontData = new Uint8Array(await fontRes.arrayBuffer());
      
      // Load animation
      const animRes = await fetch('./animation.json');
      const animData = await animRes.json();
      
      // Create animation
      animation = new Lotio({
        fonts: [{ name: 'OpenSans-Bold', data: fontData }],
        fps: 30,
        animation: animData,
        wasmPath: './lotio.wasm'
      });
      
      const canvas = document.getElementById('canvas');
      
      // Render frames
      animation.on('frame', () => {
        animation.renderToCanvas(canvas);
      });
      
      // Controls
      document.getElementById('playBtn').onclick = () => animation.start();
      document.getElementById('pauseBtn').onclick = () => animation.pause();
      document.getElementById('stopBtn').onclick = () => animation.stop();
    }
    
    init();
  </script>
</body>
</html>

Samples

The samples/ directory contains example Lottie animations and configurations:

  • samples/sample1/ - Basic animation with layer overrides

    • data.json - Lottie animation file
    • layer-overrides.json - Text and image customization configuration
    • output/ - Rendered frames (run lotio to generate)
  • samples/sample2/ - Animation with external images

    • data.json - Lottie animation file with image references
    • images/ - External image assets referenced by the animation
    • output/ - Rendered frames (run lotio to generate)
  • samples/sample5/ - Text under track matte (Bodymovin-style, tp omitted); lotio's Skottie build includes a track-matte patch so the matte source is the nearest prior layer with td !== 0 and it renders correctly without editing data.json.

Try the samples:

# Sample 1: Basic animation with text customization
cd samples/sample1
lotio --data data.json --layer-overrides layer-overrides.json --fps 30 output/

# Sample 2: Animation with external images
cd samples/sample2
lotio --data data.json --fps 30 output/

Using as a Library

Headers

Headers are installed at /opt/homebrew/include/lotio/ (or /usr/local/include/lotio/):

#include <lotio/core/animation_setup.h>
#include <lotio/text/text_processor.h>
#include <lotio/utils/logging.h>

Linking

Link with Skia libraries:

g++ -I/opt/homebrew/include -L/opt/homebrew/lib \
    -llotio -lskottie -lskia -lskparagraph -lsksg -lskshaper \
    -lskunicode_icu -lskunicode_core -lskresources -ljsonreader \
    your_app.cpp -o your_app

Or use pkg-config (recommended):

g++ $(pkg-config --cflags --libs lotio) your_app.cpp -o your_app

Using Skia Directly

The lotio package includes Skia headers and libraries, so you can use Skia features directly in your code:

// Use Skia directly
#include <skia/core/SkCanvas.h>
#include <skia/core/SkSurface.h>
#include <skia/modules/skottie/include/Skottie.h>

// Use lotio
#include <lotio/core/animation_setup.h>

int main() {
    // Use Skia API directly
    SkImageInfo info = SkImageInfo::MakeN32(800, 600, kOpaque_SkAlphaType);
    auto surface = SkSurfaces::Raster(info);
    
    // Use lotio functions
    AnimationSetupResult result = setupAndCreateAnimation("input.json", "");
    
    return 0;
}

Compile with:

g++ $(pkg-config --cflags --libs lotio) your_app.cpp -o your_app

The pkg-config file includes all necessary include paths:

  • -I${includedir} - Lotio headers
  • -I${includedir}/skia - Skia core headers
  • -I${includedir}/skia/gen - Skia generated headers

CI/CD Pipeline

The project uses GitHub Actions workflows for automated building, testing, and deployment:

graph TB
    subgraph triggers["Event Triggers"]
        mainPush["Push to main<br/>(creates semver tag)"]
        tagPush["Tag push v*"]
        lotioChanges["Changes to<br/>src/** or Dockerfile.lotio<br/>or build_binary.sh"]
        docsChanges["Changes to<br/>docs/** or examples/**"]
        manual["Manual<br/>workflow_dispatch"]
    end

    subgraph lotioWorkflow["build-lotio.yml<br/>Concurrency: build-lotio<br/>Cancel in-progress: true"]
        buildLotioImg["Build Skia & lotio<br/>push matrunchyk/lotio"]
    end

    subgraph releaseWorkflow["release.yml<br/>Concurrency: release<br/>Cancel in-progress: true"]
        versionTag["Generate version<br/>& create tag"]
        buildMac["Build macOS<br/>binary & dev package"]
        buildWasm["Build WASM<br/>library"]
        buildHomebrew["Build Homebrew<br/>bottle"]
        buildDocs["Build<br/>documentation"]
        publishRelease["Create GitHub<br/>release"]
        
        versionTag --> buildMac
        versionTag --> buildWasm
        versionTag --> buildHomebrew
        buildWasm --> buildDocs
        buildHomebrew --> buildDocs
        buildDocs --> publishRelease
    end

    subgraph testWorkflow["test.yml<br/>Concurrency: test<br/>Cancel in-progress: true"]
        testDocker["Test Docker<br/>image"]
        testWasm["Test JS/WASM<br/>library"]
        testHomebrew["Test Homebrew<br/>package"]
    end

    subgraph pagesWorkflow["pages.yml<br/>Concurrency: pages<br/>Cancel in-progress: false"]
        buildPages["Build & deploy<br/>documentation"]
    end

    mainPush --> lotioWorkflow
    mainPush --> releaseWorkflow
    mainPush --> pagesWorkflow
    
    tagPush --> lotioWorkflow
    tagPush --> releaseWorkflow
    
    lotioChanges --> lotioWorkflow
    docsChanges --> pagesWorkflow
    
    manual --> lotioWorkflow
    manual --> releaseWorkflow
    manual --> testWorkflow
    manual --> pagesWorkflow

    lotioWorkflow -->|Docker image ready| releaseWorkflow
    releaseWorkflow -->|workflow_run<br/>after completion| testWorkflow

    style lotioWorkflow fill:#e1f5ff
    style releaseWorkflow fill:#fff4e1
    style testWorkflow fill:#e8f5e9
    style pagesWorkflow fill:#f3e5f5

Workflow Descriptions

build-lotio.yml - Builds and publishes matrunchyk/lotio Docker image

  • Purpose: Create lotio binary Docker image using pre-built Skia base image
  • Triggers: Main branch push, tag pushes, source code changes, Dockerfile.lotio changes, build_binary.sh changes, manual dispatch
  • Logic: Uses matrunchyk/skia:latest as base image (Skia pre-built), only compiles lotio source
  • Build chain: Dockerfile.skiaDockerfile.lotio (uses pre-built Skia)
  • Concurrency: Single instance per workflow (cancels in-progress runs when new one starts)
  • Output: matrunchyk/lotio:latest and matrunchyk/lotio:v1.2.3 (multi-platform: arm64, amd64)
  • Architecture tags: Also creates -arm64 and -amd64 tags for clarity

release.yml - Builds all release artifacts and creates GitHub release

  • Purpose: Build and package all distribution formats (binaries, WASM, Homebrew, docs)
  • Triggers: Push to main (creates semver tag automatically), tag pushes (v*), manual dispatch
  • Logic:
    • Generates semver version from tag or creates new tag on main push
    • Builds Skia from scratch using build_binary.sh (zero bundled dependencies, fast build)
    • Builds in parallel: macOS, Linux, WASM, Homebrew
    • Injects version into all artifacts
  • Concurrency: Single instance per workflow (cancels in-progress runs when new one starts)
  • Output: macOS dev package, WASM package, Homebrew bottle, GitHub release

test.yml - Integration tests for all built artifacts

  • Purpose: Validate that all release artifacts work correctly
  • Triggers: After release.yml completes successfully, manual dispatch
  • Tests:
    • Docker image: --help, --version, library functionality, video generation with --debug
    • JS/WASM library: Load, API functions, frame rendering
    • Homebrew package: Installation, --help, --version, basic functionality
  • Concurrency: Single instance per workflow (cancels in-progress runs when new one starts)

pages.yml - Builds and deploys documentation to GitHub Pages

  • Purpose: Generate and deploy documentation with version injection
  • Triggers: Changes to docs, examples, or build scripts; manual dispatch
  • Logic: Installs lotio npm package, injects version from git tag
  • Concurrency: Single instance per workflow (does not cancel in-progress runs)
  • Output: Deployed to GitHub Pages

Project Structure

src/
├── core/          # Core functionality (argument parsing, animation setup, rendering)
├── text/          # Text processing (configuration, font handling, sizing)
└── utils/         # Utilities (logging, string utils, crash handling)

IDE Setup

The project includes IDE configuration for Cursor/VS Code:

  • .vscode/c_cpp_properties.json - C/C++ extension settings
  • .clangd - clangd language server settings

Reload Cursor/VS Code after cloning: Cmd+Shift+P → "Reload Window"

Troubleshooting

Skia build fails:

  • Ensure all dependencies are installed
  • Check sufficient disk space (Skia build is large)
  • Review error messages in scripts/build_binary.sh output

Linker errors:

  • Verify Skia libraries exist in third_party/skia/skia/out/Release/
  • Check library paths in build script

IDE include errors:

  • Reload Cursor/VS Code
  • Verify .vscode/c_cpp_properties.json has correct paths

License

See individual component licenses:

  • Skia: third_party/skia/skia/LICENSE