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

tlottie

v0.1.23

Published

Fast, web-worker based Lottie/TGS renderer with adapters for React, SolidJS, Vue, Svelte, Web Components and Vanilla JS, backed by the tlottie Rust/WASM engine.

Readme

tlottie

npm version license

Fast, Web Worker–based Lottie and TGS (Telegram sticker) renderer, backed by the tlottie Rust/WASM engine. Ships adapters for React, SolidJS, Vue, Svelte, Web Components, and Vanilla JS.

Live demo → — one page per adapter.

  • Off the main thread. Parsing and rendering happen in a configurable pool of Web Workers, drawing into an OffscreenCanvas — no jank on the UI thread.
  • Fast core. The underlying tlottie engine benchmarks 23–73% faster frame times than rlottie/thorvg (see its own README for numbers).
  • TGS support. Gzipped Lottie (.tgs, Telegram stickers) is decompressed in-worker using the browser-native DecompressionStream — no pako/fflate dependency.
  • Skeleton loading. Pass an outline SVG and get a CSS mask-image shimmer while the animation loads or if it fails — generate that SVG with the bundled tlottie-outline CLI (bunx tlottie-outline --input animation.json).
  • Small. Each framework adapter is ~3–4KB gzipped; the shared WASM binary (~418KB raw, ~131KB brotli / ~163KB gzip) is fetched once and cached, not bundled per adapter.

Install

bun add tlottie
# or: npm install tlottie / pnpm add tlottie / yarn add tlottie

Framework peer dependencies (react, solid-js, vue, svelte) are optional — only install the one matching the adapter you use.

Quick start

React

import { LottiePlayer } from "tlottie/react";

<LottiePlayer src="/animation.json" loop autoplay />;

SolidJS

import { LottiePlayer } from "tlottie/solid";

<LottiePlayer src="/animation.json" loop autoplay />;

Vue

<script setup>
import { LottiePlayer } from "tlottie/vue";
</script>

<template>
	<LottiePlayer src="/animation.json" loop autoplay />
</template>

Svelte

<script>
	import { LottiePlayer } from "tlottie/svelte";
</script>

<LottiePlayer src="/animation.json" loop autoplay />

Web Component

import "tlottie/webcomponent";
<tlottie-player src="/animation.json" loop autoplay></tlottie-player>

Vanilla JS

import { createTLottiePlayer } from "tlottie/vanilla";

const { tlottie, destroy } = createTLottiePlayer(document.getElementById("app"), {
	src: "/animation.json",
	loop: true,
	autoplay: true,
});

Each adapter also ships a stylesheet for the skeleton shimmer (only needed if you use the outline prop):

import "tlottie/react/style.css"; // or /solid, /vue, /svelte, /vanilla, /webcomponent

Loading data

<LottiePlayer src="https://example.com/animation.json" />
<LottiePlayer src="https://example.com/sticker.tgs" />       {/* gzipped, decompressed automatically */}
<LottiePlayer data={jsonString} />                            {/* raw Lottie JSON string */}
<LottiePlayer data={uint8ArrayBytes} />                        {/* raw bytes, plain or gzipped */}

src fetches are cached in-memory per URL and shared across every player instance on the page — loading the same animation twice never re-fetches.

Skeleton / shimmer loading state

Pass a silhouette SVG (as a raw string) via outline; it's rendered as a CSS mask-image behind the canvas until the animation loads (or shown again if it errors):

<LottiePlayer src="/animation.json" outline={outlineSvgString} />

Generate that outline SVG from a Lottie/.tgs file with the bundled tlottie-outline CLI — no separate install, works via bunx/npx, or as an npm run script in any project that has tlottie installed:

bunx tlottie-outline --input animation.json
# or: npx tlottie-outline --input animation.json
# writes animation-outline.svg next to it
Usage: tlottie-outline --input <file.json|file.tgs> [--output <file.svg>] [--frame <n>] [--size <px>]

  -i, --input   Path to a Lottie JSON or .tgs (gzipped) file. Required.
  -o, --output  Path to write the outline SVG. Defaults to <input-without-extension>-outline.svg.
  -f, --frame   Frame number to trace. Defaults to 0.
  -s, --size    Raster size (px, square) used for tracing — higher is more accurate and slower. Defaults to 512.

It renders the given frame with tlottie's own wasm renderer (same one the library uses), flattens every visible pixel to a black silhouette, and traces that into an optimized SVG path — the same technique, reimplemented, as erfanmola/lottie-output-generator but built on tlottie/wasm instead of thorvg, so it shares this package's gzip decoding and renderer instead of needing its own.

Playback control

Every adapter exposes the underlying TLottie instance (via lottieRefCallback in React/Solid/Svelte, ref+defineExpose in Vue, or the .tlottie property on the custom element / vanilla handle):

tlottie.play();
tlottie.pause();
tlottie.stop();
tlottie.seek(30);
tlottie.setSpeed(1.5);
tlottie.setLoop(true); // or a number of loop repetitions, or false
tlottie.setDirection(-1); // 1 | -1
tlottie.on("load" | "play" | "pause" | "stop" | "frame" | "loopComplete" | "complete" | "error", (payload) => {});

speed/loop/direction/fitzModifier props are applied live to the running instance when changed; changing src/data remounts the canvas and reloads.

Configuration

| Prop | Type | Notes | | ------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------- | | src / data | string / string \| Uint8Array | One is required. | | speed | number | Default 1. | | loop | boolean \| number | true = forever, number = play that many times. | | direction | 1 \| -1 | | | autoplay | boolean | Default true. | | fitzModifier | FitzModifier | Telegram Fitzpatrick skin-tone variant. Parse-time only — changing it recreates the instance. | | layerColorReplacements | { layerNamePrefix, color }[] | Recolors layers by name prefix. Parse-time only. | | quality | { antialias?, curveTolerance? } | Render quality knobs. | | workerCount | number | Spins up a dedicated worker pool of this size just for this player. | | forceRender | boolean | Keep rendering while off-screen (skips the IntersectionObserver auto-pause). | | reportFrames | boolean | Emit throttled (~10Hz) frame events, for progress UIs. Off by default (costs a postMessage per emission). | | playOnClick | boolean | Clicking the canvas calls play(). Mainly for non-looping animations: they play once, then replay on each click. |

Worker pool

By default all players share a single worker (one worker already multiplexes any number of animations fine — each worker owns its own WASM module instance, so sizing the default off navigator.hardwareConcurrency just burns memory on typical multi-core machines for no benefit). Raise it if you've profiled a worker-bound workload:

import { configureTLottie } from "tlottie";

configureTLottie({ workerCount: 4 });

Or give one player its own dedicated pool via the workerCount prop.

Eager initialization

By default, the render worker and the wasm binary are both created/fetched lazily — the first Worker spins up when the first player mounts, and the wasm binary isn't requested until that player's animation source has resolved. Call initializeTLottie() any time earlier (module load, route change, hover intent, whatever fits your app) to warm both up ahead of time, so the first real player has nothing left to wait for:

import { initializeTLottie } from "tlottie";

initializeTLottie(); // fire-and-forget is fine
// or: await initializeTLottie({ workerCount: 4, wasmUrl: "/custom/tlottie.wasm" });

Every worker in the (grown-to-full-size) pool is warmed, since each worker owns its own wasm module instance. Safe to call more than once or against multiple pools.

Browser support

Requires OffscreenCanvas, requestAnimationFrame inside a dedicated Worker, and (for .tgs) DecompressionStream. All are available in current Chrome/Edge/Firefox/Safari. No fallback path is implemented for older browsers.

Development

This repo vendors tlottie as a git submodule and ships a prebuilt tlottie.wasm — you don't need a Rust toolchain to work on the JS/TS side.

git clone --recurse-submodules https://github.com/erfanmola/tlottie.git
cd tlottie
bun install
bun run dev      # demo app at localhost:5173, imports straight from src/
bun run lint      # typecheck + biome
bun run build     # builds dist/ for every adapter

Rebuilding src/core/tlottie.wasm from the submodule (only needed after pulling submodule updates or touching the Rust source) requires a Rust toolchain with the wasm32-unknown-unknown target:

bun run build:wasm            # regular std build (the shipped default)
bun run build:wasm:no-std     # optional no_std build -> src/core/tlottie.no-std.wasm

The shipped package keeps the regular std build; the no_std binary is an opt-in cargo feature (wasm,no-std — allocator via dlmalloc over memory.grow, no libc imports) and is only produced when explicitly requested.

The build uses cargo's release profile (opt-level = 3, full codegen quality) plus a wasm-opt -Oz pass for dead-code elimination and stripping (via the binaryen devDependency, no system install needed) — 488KB → 418KB raw, with no measurable render-speed cost (benchmarked; opt-level = "z" gets smaller still but is a real ~50% slower render path, not worth it here). What actually ships over the wire is smaller still, since fetch() transparently negotiates compression: 163KB gzip, 131KB brotli. Make sure whatever serves dist/tlottie.wasm in production sends Content-Encoding (most CDNs and static hosts do this automatically — a bare/unconfigured dev server might not).

Repo layout

  • src/core/ — WASM loader, memory management, gzip decompression, framework-agnostic playback clock
  • src/worker/ — the render worker and its worker-pool
  • src/main/ — main-thread facade (TLottie class), fetch cache, shimmer helper
  • src/{vanilla,webcomponent,react,solid,vue,svelte}/ — framework adapters
  • src/bin/ — the tlottie-outline CLI
  • demo/ — a page per adapter, exercising load/play/error/gzip/resize

CI

  • .github/workflows/pages.yml — builds demo/ and deploys it to GitHub Pages on every push to main.
  • .github/workflows/release.yml — on a package.json version bump landing on main, publishes to npm and creates a matching GitHub Release with auto-generated notes. Needs an NPM_TOKEN repo secret (an npm automation token with publish access) to actually publish; without it the workflow fails at the publish step.

License

MIT — see LICENSE. The underlying tlottie engine is also MIT.