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

@lemondouble/featheranime2d

v0.1.0-preview.6

Published

그림 한 장으로 만드는, 가벼운 움직이는 애니메이션 캐릭터 렌더러

Readme

@lemondouble/featheranime2d

그림 한 장으로 만드는, 가벼운 움직이는 애니메이션 캐릭터 렌더러입니다. 캐릭터 패키지(.fa2d)를 불러와 표정, 모션, 숨쉬기, 깜빡임, 머리카락 흔들림을 계산하고 PixiJS로 그립니다. 계산은 Rust로 만든 Core를 WASM으로 돌립니다.

설치

pnpm add @lemondouble/featheranime2d pixi.js
  • pixi.js(8.x)는 /pixi, /react를 쓸 때 필요합니다. react(19.x)는 /react를 쓸 때 필요합니다.
  • WASM 파일은 번들러(Vite, Next.js 등)가 new URL(..., import.meta.url)을 보고 빌드 결과에 함께 넣습니다. 따로 설정할 것은 없습니다.

React

"use client";

import { useFeatherAnime2D } from "@lemondouble/featheranime2d/react";
import { useEffect } from "react";

export default function CharacterStage({ expression }: { expression?: string }) {
  const { containerRef, view, error } = useFeatherAnime2D("/characters/witch.fa2d");

  useEffect(() => {
    view?.character.setExpression(expression ?? null);
  }, [view, expression]);

  return <div ref={containerRef} style={{ width: "100%", height: "100%" }} />;
}
  • containerRef를 붙인 요소의 크기에 맞춰, 비율을 유지하며 가운데에 그립니다. 배경은 투명합니다. 화면 밀도(devicePixelRatio)만큼 크게 그리되 2배에서 자릅니다.
  • 휴대폰처럼 원본보다 훨씬 작게 그리는 곳에서는 useFeatherAnime2D(src, { mipmaps: true })로 밉맵을 켭니다. 가는 선이 깨지지 않는 대신 텍스처 메모리를 약 1/3 더 씁니다.
  • PixiJS와 WASM은 effect 안에서 불러오므로, Next.js에서 서버 렌더링을 빼는 처리(dynamic(..., { ssr: false })) 없이 서버 컴포넌트에서 바로 쓸 수 있습니다.
  • 표정처럼 상태인 것은 위처럼 useEffect로 넣고, 모션처럼 한 번 실행하는 것은 view.character.playMotion("wave", "normal")을 직접 부릅니다.

PixiJS

import { CharacterView } from "@lemondouble/featheranime2d/pixi";

const view = await CharacterView.load("/characters/witch.fa2d");
app.stage.addChild(view);
app.ticker.add((ticker) => view.update(ticker.deltaMS / 1000));
view.character.setExpression("happy");
  • 캔버스 픽셀 좌표로 그리므로 화면 크기에 맞추는 일(scale, position)은 앱이 합니다. 캔버스 크기는 view.info.canvas에 있습니다.
  • 촘촘한 화면에서 흐려지지 않게 PixiJS Application을 resolution: devicePixelRatio, autoDensity: true로 만듭니다. 작게 그리는 곳이면 CharacterView.load(src, { mipmaps: true })로 밉맵을 켭니다.
  • info.backdrop이 색이면 앱이 화면을 그 색으로 칠합니다. 그림 배경은 CharacterView가 함께 그립니다.
  • 부위는 캔버스 영역에서 잘라 냅니다. 캔버스 밖까지 그려 둔 부위(모자, 머리카락 등)를 보여 주려면 view.clipMargin = { left: 1000, top: 1000, right: 1000, bottom: 0 }처럼 넓힐 방향과 크기를 정합니다. 음수면 캔버스 안쪽으로 좁힙니다.
  • 다 쓰면 view.destroy()로 텍스처와 WASM 메모리를 해제합니다.

Core만 쓰기

import { Character } from "@lemondouble/featheranime2d";

const character = await Character.load(bytes);   // .fa2d 파일의 바이트
character.update(1 / 60);
character.vertices;       // 모든 slot의 정점 (캔버스 픽셀)
character.slotVisible;    // slot마다 보이는지
character.slotTextures;   // slot마다 그릴 텍스처 번호 (info.textures 순서)
  • 다른 그래픽 라이브러리로 그릴 때 씁니다. slot의 정점 수, uv, 삼각형은 character.info.slots에 있습니다.
  • vertices 같은 버퍼는 WASM 메모리를 복사 없이 보는 뷰라서, 다음 update 전까지만 유효합니다.
  • 시드({ seed })를 빼면 무작위 시드를 씁니다. 같은 시드의 캐릭터들은 같은 순간에 깜빡입니다.

API

| 이름 | 설명 | |---|---| | setExpression(name \| null) | 표정을 바꿉니다. 즉시 바뀝니다 | | playMotion(name, "idle" \| "normal" \| "force") | 모션을 재생합니다. 재생 중인 모션보다 우선순위가 높지 않으면 버리고 false를 돌려줍니다 | | currentExpression, currentMotion | 지금 표정과 재생 중인 모션입니다. 모션이 끝나면 null입니다 | | setInput(parameter, value) | 얼굴 추적, 마우스 시선 같은 외부 입력입니다. 기본값에서 옮긴 만큼 더합니다 | | setLipSync(mouthOpen, speaking) | 실제 음성으로 계산한 입 벌림 값입니다 | | setTalkSimulation(enabled, syllablesPerSecond, loudness) | 음성 없이 말하는 흉내를 냅니다. 켜져 있으면 setLipSync 대신 씁니다 | | setDriverEnabled(driver, enabled) | 모션, 깜빡임, 표정, 외부 입력, 숨쉬기, 물리, 립싱크를 하나씩 켜고 끕니다 |

캐릭터 패키지를 만드는 방법과 형식은 FeatherAnime2D 레포에 있습니다.