egao-ts
v0.1.1
Published
WebGL2 player for 1-bit packed monochrome face animations
Maintainers
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 face1-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 = 1024Frame i starts at i * bytesPerFrame in the concatenated Uint8Array.
Install
bun add egao-tsThis 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 128Programmatic 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
sideEffectsisfalse- the root package does not import animation modules
egao-ts/animsexports names/metadata onlyimport { idle } from "egao-ts/anims/idle"andrequire("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 exampleRegenerate bundled faces from assets/prebuilt-src/*.gif with bun run build:anims.
