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

moodstone

v1.0.1

Published

Animated, procedurally generated agent avatars for React Native. Nine moods, seven cuts. Rendered with Skia.

Readme

Moodstone

npm version MIT license

Dozens of Moodstone avatars in every cut, mood and palette colour

Animated, procedurally generated avatars for AI agents in React Native. Give an agent a mood and the face reacts. Rendered with React Native Skia on the UI thread.

Try every mood, cut and colour in the browser at moodstone.expo.app.

import { Moodstone } from "moodstone";

<Moodstone color="pine" mood="thinking" size={48} />;
  • Nine moods, each a seamless loop.
  • Seven cuts (silhouettes) that morph into each other, with eyes that fit each shape.
  • Twelve palette colours or any hex, on a surface lit by a slowly orbiting light.
  • UI-thread animation that leaves the JS thread idle.
  • PNG and SVG exports, including an animated SVG of the whole loop.

Install

npx expo install @shopify/react-native-skia react-native-reanimated react-native-worklets
npm install moodstone

Needs React 19+, React Native 0.78+, Skia 2+, Reanimated 4+ and Worklets 0.7+.

Moods

Every mood works with any cut and colour; the previews mix them.

| Mood | Cut | Color | Preview | When the agent is… | | ------------ | ---------- | ----------- | ----------------------------------------------------------------------------------- | --------------------------------------- | | idle | circle | pine | | ready, waiting for input | | observing | squircle | lemon | | reading context, awaiting a tool result | | thinking | hexagon | rose | | searching, making a short tool call | | processing | badge | lilac | | reasoning at length | | working | square | tangerine | | streaming output | | done | burst | sky | | finished successfully | | failed | diamond | cherry | | errored | | invalid | hexagon | plum | | rejecting the input | | inactive | squircle | mint | | offline or paused |

Props

| Prop | Type | Default | Notes | | --------------- | --------------------------- | -------------- | ---------------------------------------------------------------------- | | mood | Mood | "idle" | Restarts the loop when it changes. | | color | palette key or hex | "pine" | | | cut | Cut | "circle" | Silhouette. Morphs when it changes. | | tune | Tune | | Adjusts the cut, see Tuning a cut. | | shape | Partial<ShapeParams> | | Custom silhouette, like { lobes: 12, depth: 0.11 }. Not with tune. | | eyeColor | "white" \| "black" \| hex | "white" | | | seed | [number, number, number] | DEFAULT_SEED | Light start angle, drift and orbit. randomSeed() makes one. | | size | number | 64 | In dp. | | animated | boolean | true | false draws one frame, the mood's key pose. Use it for long lists. | | paused | boolean | false | Freezes the loop. | | phase | 0..1 | | Loop position of a still, or start offset of a loop. | | morphDuration | number | 460 | In ms. 0 snaps. | | style | StyleProp<ViewStyle> | | Applied to the canvas. | | ref | Ref<MoodstoneHandle> | | See Ref handle. |

Tuning a cut

tune accepts only the parameters the cut exposes, listed in CUT_TUNES:

| Cut | Tunable | | ------------------------------- | ------------------------- | | circle | n, ax | | squircle, square, diamond | n, rot, ax | | hexagon | sides, round, rot | | badge, burst | lobes, depth, sharp |

<Moodstone cut="hexagon" tune={{ sides: 5 }} />  // ok
<Moodstone cut="circle" tune={{ rot: 30 }} />    // type error: rotating a circle does nothing

The type check needs a literal cut. For a cut held in state, cast the pair to CutTune; keys the cut doesn't expose are ignored at runtime.

Ref handle

const ref = useRef<MoodstoneHandle>(null);
<Moodstone ref={ref} mood="done" />;

const image = ref.current?.snapshot({ size: 1024, background: "#0e1113" }); // SkImage
const base64 = image?.encodeToBase64(); // PNG
ref.current?.restart();
const spec = ref.current?.getSpec(); // AvatarSpec

snapshot renders offscreen at any size, stills included, and takes a phase to capture another moment. restart replays the mood from its rest pose. getSpec returns the resolved spec for the SVG exports below.

SVG exports

import {
  renderAvatarSvg,
  renderAnimatedAvatarSvg,
  stillFrame,
  DEFAULT_SEED,
  PALETTE,
  type AvatarSpec,
} from "moodstone";

const spec: AvatarSpec = {
  seed: DEFAULT_SEED,
  color: PALETTE.pine,
  cut: "circle",
  mood: "thinking",
  eyeColor: "#FFFFFF",
};
const still = renderAvatarSvg(spec, stillFrame(spec), { size: 240 });
const loop = renderAnimatedAvatarSvg(spec, { size: 240, fps: 24 });

stillFrame(spec) is the mood's key pose; computeFrame(spec, t) gives any other moment. The animated SVG uses SMIL and loops seamlessly. No GIF or video encoder is bundled, but computeFrame and renderAvatarImage give you every frame to encode one.

For a tuned or custom shape, set the spec's shape (tunedShape(cut, tune) builds a tuned one) and pass the spec through resolveSpec so the eyes fit.

From the command line

A clone of the repo exports SVG files straight from the source, with Node 22.18+ and no build:

npm run export-svg -- --mood thinking --cut hexagon --color sky
npm run export-svg -- --mood all --animated --out ~/Desktop/moodstone

--help lists every flag. Without --out, files go to ./avatars/. npm run avatars regenerates the previews this README shows, in assets/.

Core and Skia layer

moodstone/core has no React Native imports. Its pure computeFrame(spec, t) describes every frame as plain numbers, and drives both the Skia component and the SVG exports. The main entry re-exports it and adds the Skia pieces the component is built from, for drawing into your own canvas: useAvatarSpec, drawAvatarSkia, surfacePath and makeSurfaceShader.

Example app

example/ is an Expo app with a one-screen Studio for trying colours, cuts, moods, eyes and tuning. npm install at the root sets it up, and it loads the library from src/, so edits show up without a build. Deep links set its state, so a script can drive it:

cd example && npx expo start --ios
xcrun simctl openurl booted "exp://<host>:8081/--/?mood=failed&cut=burst&lobes=9"

Parameters: color, cut, mood, motion (animated or still), eyes, theme, scroll (top or end) and any tunable parameter.

It also runs in a browser, where Skia draws through CanvasKit (WebAssembly) instead of natively. A build is live at moodstone.expo.app, and the same parameters go in its query string, like ?mood=failed&cut=burst&lobes=9. To run it locally:

cd example && npm run web
open "http://localhost:8081/?mood=failed&cut=burst&lobes=9"

npm run export:web builds it as a static site in example/dist/.

Development

npm install     # the library and the example workspace
npm run check   # ESLint, Prettier and tsc
npm run format  # Prettier, then ESLint --fix
npm run build   # bob → lib/

Credits

The single-colour agent face whose personality lives in two eyes comes from Plane's Agent Avatar Lab; the surface, geometry, palette, timing and code here are original. Reading the face as what a persistent agent is doing was inspired by Designing Grok Bot for a world of persistent agents.

License

MIT