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

egao-ts

v0.1.1

Published

WebGL2 player for 1-bit packed monochrome face animations

Readme

egao-ts

A small TypeScript package for displaying low-resolution monochrome animated faces in the browser.

egao-ts plays 1-bit packed monochrome face animations with WebGL2. Packed bytes are uploaded as an integer texture; a fragment shader unpacks each logical pixel. The published package ships the player plus prebuilt faces. The GIF converter stays in this repository for local use after cloning.

GIF / animation source
        |
        v
   egao converter
        |
        v
TypeScript module
Uint16Array delays
Uint8Array packed frames
        |
        v
 EgaoPlayer / WebGL2
        |
        v
R8UI packed texture
        |
        v
fragment shader bit unpack
        |
        v
crisp monochrome face

1-bit animation model

Each frame is a monochrome bitmap packed eight horizontal pixels per byte:

  • bit 7 is the left-most pixel in the group
  • bit 0 is the right-most pixel
  • unused trailing bits in a row are zero

For a 128 x 64 face:

bytesPerRow = 16
bytesPerFrame = 1024

Frame i starts at i * bytesPerFrame in the concatenated Uint8Array.

Install

bun add egao-ts

This repository uses Bun for package management, tests, and builds.

Play a face

import { EgaoPlayer } from "egao-ts";
import { idle } from "egao-ts/anims/idle";
import { wake } from "egao-ts/anims/wake";

const player = new EgaoPlayer({
  canvas: document.querySelector("#face")!,
});

player.play(idle, { loop: true });

button.addEventListener("click", () => {
  player.play(wake, {
    loop: false,
    onComplete: () => player.play(idle, { loop: true }),
  });
});

The published package is browser-safe. It does not include the GIF converter.

Prebuilt animations

Import only the faces you use. Each subpath is tree-shakable and does not pull in other frame payloads.

import { idle } from "egao-ts/anims/idle";
import { wake } from "egao-ts/anims/wake";
import { listen } from "egao-ts/anims/listen";

Shipped faces: idle, wake, listen, think, speak, sleep, happy, surprised.

Prebuilt faces are converted from Huy Khoong's extracted Dasai Mochi emote GIFs (content, intro, proud, confused_2, music, sleepy, happy, laugh) to 128x64 monochrome using gif2cpp-style settings: contain, area/bilinear scale, threshold 128. Playback still samples integer 1-bit pixels; optional effects.glow softens the OLED-style upscale.

Convert a GIF (clone this repo)

The converter and egao CLI are not published to npm. Clone the repository and run them locally with Bun:

git clone https://github.com/implicit-invocation/egao-ts.git
cd egao-ts
bun install
bun run src/converter/cli.ts convert input.gif \
  --name idle \
  --out src/anims/idle.ts \
  --width 128 \
  --height 64 \
  --fit contain \
  --threshold 128

Programmatic conversion is Bun/build-time only and imported from source:

import { convertGifToAnimation } from "./src/converter/index.ts";

Useful options: --fit contain|cover|stretch, --invert, --loop true|false, --dither none|ordered|floyd-steinberg, --fps <n>, --clamp-delay.

Delays above 65535 ms are rejected unless --clamp-delay is set.

Tree-shaking

  • sideEffects is false
  • the root package does not import animation modules
  • egao-ts/anims exports names/metadata only
  • import { idle } from "egao-ts/anims/idle" and require("egao-ts/anims/idle") load only that payload

Browser support

WebGL2 is required. If canvas.getContext("webgl2") fails, EgaoPlayer throws EgaoWebGLUnsupportedError. There is no Canvas2D fallback.

Default fit mode is contain with nearest-neighbor scaling. Colors are presentation-only; the source data stays 1-bit.

If respectReducedMotion is true and the user prefers reduced motion, looping decorative animations stay on the first frame.

Asset licensing

You are responsible for the rights to any GIF you convert. The shipped prebuilt faces are converted from Huy Khoong's Dasai Mochi clone assets. Huy Khoong notes that the original imagery belongs to the Dasai Mochi creators; redistribute or publish only if you have the rights to do so.

Scripts

bun install
bun test
bun run build
bun run build:anims
bun run check:exports
bun run example

Regenerate bundled faces from assets/prebuilt-src/*.gif with bun run build:anims.