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

@three-ws/walk

v0.1.0

Published

A diverse, animated 3D avatar that walks and talks over your web pages — corner companion + full-page playground, with a built-in avatar picker. Powered by Three.js.

Readme

@three-ws/walk

A diverse, animated 3D avatar that walks and talks over your web pages — a corner companion plus a full‑page playground, with a built‑in avatar picker. Powered by Three.js.

Drop a character onto any site. It idles in the corner, turns to follow the cursor, waves when the visitor navigates, and greets each page. Click it and it detaches into a full‑page playground the visitor can steer with the keyboard or an on‑screen d‑pad — strolling across the page from a gentle aerial view, or platforming across your real DOM with gravity and jumps. Walk onto a link and it opens like a doorway into the next page.

Every visitor can choose who walks with them from a built‑in roster of avatars — a robot mascot, humanoids, photoreal people, a fox, dancers, showpieces — or you can supply your own GLB. Whatever they pick is animated correctly whether the rig ships its own clips or needs the shared retargeting library, so nothing ever freezes in a bind/T‑pose.

This is the engine behind the walking companion on three.ws.


Install

npm install @three-ws/walk three

three is a peer dependency — bring your own copy (>= 0.150).

You also need to serve two sets of assets from your origin (or a CDN you point assetBase at):

  • the avatar GLBs the roster references (e.g. /avatars/*.glb), and
  • the shared animation manifest + clips (/animations/manifest.json + clips), used to retarget motion onto rigs that ship no locomotion.

The defaults match the three.ws asset layout. Override them with assetBase, apiBase, and manifestUrl, or pass your own avatars roster.


Quick start

The whole experience, app‑style (auto‑mounts when enabled, honors ?walk= deep links, resumes the playground after a "dive"):

import { createWalkCompanion } from '@three-ws/walk';

const walk = createWalkCompanion();
walk.bootstrap();

Or drive it yourself:

const walk = createWalkCompanion({ defaultAvatarId: 'fox' });

walk.enable(); // mount the corner companion
walk.openPicker(); // let the visitor choose an avatar
walk.setAvatar('michelle'); // swap the live avatar
walk.disable(); // remove it

createWalkCompanion is side‑effect free on import — nothing touches the DOM until you call enable() or bootstrap().


How avatars are animated

Each roster entry declares a rig strategy so it always moves:

| rig | Used for | How it animates | | ---------- | ----------------------------------------------- | ------------------------------------------------------------------------------- | | embedded | self‑animated or non‑humanoid GLBs (robot, fox) | plays the clips baked into the GLB; falls back to the first clip so it never freezes | | shared | humanoids with no locomotion (or only a T‑pose) | retargets the shared clip library (idle/walk/run/wave/jump) onto the rig |

The same unified loader powers both the corner companion and the playground, so adding an avatar makes it work everywhere at once.

Custom roster

import { createWalkCompanion, WALK_AVATARS } from '@three-ws/walk';

createWalkCompanion({
  avatars: [
    ...WALK_AVATARS,
    {
      id: 'mascot',
      name: 'Our Mascot',
      category: 'Brand',
      asset: '/brand/mascot.glb',
      source: 'static',
      rig: 'shared', // retarget the shared library onto it
      accent: '#ff0066',
    },
  ],
  defaultAvatarId: 'mascot',
}).bootstrap();

User‑generated avatars served by a GLB proxy (/api/avatars/<id>/glb) work via makeApiAvatarEntry(id) and resolve at runtime — no need to list them.


API

createWalkCompanion(options?) → control

| option | default | description | | ----------------- | ---------------------------- | -------------------------------------------------------- | | avatars | WALK_AVATARS | roster shown in the picker and resolvable by id | | defaultAvatarId | 'robot' | avatar loaded when none is chosen/stored | | assetBase | '' | base prepended to static GLB paths (e.g. a CDN origin) | | apiBase | '' | base prepended to the /api/avatars/<id>/glb proxy | | manifestUrl | '/animations/manifest.json'| shared animation manifest | | excludedRoutes | full‑screen 3D routes | path prefixes where the companion never mounts | | enablePicker | true | show the avatar picker button | | greeting | built‑in | (path) => string \| null to customise the page greeting | | docsUrl | null | optional "make your own" link in the picker footer | | storagePrefix | 'walk' | localStorage/sessionStorage key prefix |

The returned control object:

control.isEnabled(): boolean
control.enable(): void
control.disable(): void
control.toggle(): void
control.setAvatar(idOrEntry): void   // persist + hot-swap the live avatar
control.openPicker(): void
control.bootstrap(): void            // app-style auto-mount + ?walk= deep links
control.instance                     // the live companion (or null)

Roster helpers

import {
  WALK_AVATARS, DEFAULT_AVATAR_ID, DEFAULT_SHARED_CLIPS,
  getAvatar, defaultAvatar, listCategories,
  makeApiAvatarEntry, resolveAvatarUrl,
} from '@three-ws/walk';

Playground (driven for you by the companion, exported for direct use)

import {
  launchPlayground, exitPlayground, switchPlaygroundMode,
  getPlaygroundMode, shouldDropIn, consumeDropIn, playgroundState,
} from '@three-ws/walk';

Picker UI

import { createAvatarPicker } from '@three-ws/walk';

const picker = createAvatarPicker({
  avatars: WALK_AVATARS,
  currentId: 'robot',
  onSelect: (entry) => console.log('chose', entry.id),
});
picker.show();

Low‑level loader

import { loadWalkAvatar, getAvatar } from '@three-ws/walk';

const { model, controller } = await loadWalkAvatar(getAvatar('xbot'));
scene.add(model);
// each frame:
controller.setState('walk'); // 'idle' | 'walk' | 'run' | 'jump'
controller.update(dt);

URL controls

The companion honours these query params (via bootstrap()):

  • ?walk=1 — force the companion on
  • ?walk=0 — force it off
  • ?walk=play — deep‑link straight into the full‑page playground
  • ?avatar=<id> — load a specific roster avatar (or a user avatar id)

Accessibility & performance

  • One shared WebGL context budget — the companion and playground never run two contexts at once, and the budget coordinates with other Three.js viewers on the page so a busy site never hits the browser's context limit.
  • Reduced motion — respects prefers-reduced-motion; the avatar calms to a steady idle and page "dives" skip the animation.
  • Keyboard — playground steers with arrow keys / WASD; the picker is fully keyboard‑navigable; every control has a visible focus ring.
  • Zero footprint when off — nothing loads (no Three.js fetch) until enabled, and the playground code is a lazy import() paid for only on first detach.

License

Apache‑2.0 © three.ws