@three-ws/agent-ui
v0.2.0
Published
Drop a 3D avatar into your UI and let it react to buttons, inputs, and navigation. The three.ws agent overlay SDK.
Maintainers
Readme
@three-ws/agent-uipackages the avatar-overlay runtime from the three.ws demos into a small Three.js SDK. The avatar lives on a transparent, fullscreen canvas above your DOM and is anchored to real elements: it can stand on a card, fall onto a heading, walk over to focus an input, cover its eyes when you type a password, or run off-screen before a navigation. OnecreateAgentUI()call gets you a handle with imperative methods; oneagent.scan()call wires up declarativedata-agent-*attributes with no per-element JS.
Install
npm install @three-ws/agent-ui threethree (>=0.150.0) is a peer dependency so your bundler de-dupes it. The package
ships source ESM (@three-ws/agent-ui) plus a prebuilt bundle subpath
(@three-ws/agent-ui/bundle).
Quick start
<canvas></canvas>
<div id="hero">three.ws</div>
<script type="module">
import { createAgentUI } from '@three-ws/agent-ui';
const agent = await createAgentUI({
avatar: '/avatars/cz.glb',
clipsBase: '/animations/clips/',
clips: ['idle', 'walk', 'falling'],
});
const hero = document.getElementById('hero');
agent.fallOnto(hero, {
onLand: () => {
agent.fx.dust(hero);
agent.fx.impactPulse(hero);
},
});
agent.scan(); // wires any data-agent-action attributes in the page
</script>createAgentUI(options) resolves once the GLB and clip JSONs load and the avatar is
in scene. The resolved handle exposes the Three.js objects (renderer, scene,
camera, canvas, avatar), animation control, movement/posing behaviors, FX
helpers, and lifecycle (whenReady, destroy).
Imperative API
// Animation — clips are loaded from `clipsBase`
agent.play('idle', { loop: true });
agent.play('covereyes', { loop: false, hold: true });
agent.clip('walk'); // → clip duration in seconds (0 if not loaded)
agent.currentClip; // → name of the clip currently playing
// Movement & posing
agent.standOn(formCard, { anchor: 'top-center' }); // park without traversal
agent.walkTo(emailInput); // walk-cycle over to an element
agent.fallOnto(heading, { duration: 1.4 }); // drop in from above
agent.runOff('right'); // walk off-screen
agent.interceptNavigation(homeLink, { direction: 'right', delay: 1100 });
agent.lookAt(450); // turn yaw toward a screen-X (pixels)
agent.faceFront();
// FX
const stopShadow = agent.fx.proximityShadow(heading); // returns a disposer
agent.fx.dust(buttonEl);
agent.fx.impactPulse(buttonEl);
// Utilities
agent.worldOfElement(el, { anchor: 'center' }); // DOM rect → world-space target
const pick = agent.pickFrom(['nod', 'shrug', 'wave']); // non-repeating picker
agent.whenReady((a) => { /* runs now or on ready */ });
agent.destroy(); // cancels RAF, unlocks root motion, tears down the rendererAnchors accepted by standOn / walkTo / worldOfElement: top-left,
top-right, top-center (default), center, bottom-center, left-of,
right-of.
The same behavior functions are exported standalone for advanced compositions:
createRenderer, loadAvatar, createAnimator, lockRootMotion, worldOfElement,
moveTo, lookAtScreenX, faceFront, walkTo, standOn, fallOnto, runOff,
interceptNavigation, createRandomPicker, caretScreenX, startCaretTracking,
dust, impactPulse, proximityShadow, scan.
Declarative attributes
Mark up the page, call agent.scan() once. It returns a cleanup function that
removes every listener it added.
<div class="card" data-agent-action="stand-on"></div>
<input data-agent-action="track-typing" />
<input data-agent-action="privacy-mode" type="password" />
<a href="/" data-agent-action="navigate-on-click" data-agent-direction="right">Home</a>
<button data-agent-action="react-on-click" data-agent-clip="celebrate">Subscribe</button>| data-agent-action | Behavior |
|---|---|
| stand-on | Park the avatar above this element on ready. |
| track-typing | On focus, walk to the input, play lookdown, then follow the caret with yaw. |
| privacy-mode | On focus, walk to the input, play covereyes once and hold; play idle on blur. |
| navigate-on-click | Intercept the click, run off-screen, then follow href. |
| react-on-click | Play data-agent-clip once on click. |
Optional modifiers: data-agent-direction (left/right), data-agent-clip,
data-agent-anchor, data-agent-delay (ms), data-agent-loop (true/false),
data-agent-hold (true/false).
Options
createAgentUI({
avatar: '/avatars/cz.glb', // GLB URL (you host it)
clipsBase: '/animations/clips/', // base path for clip JSONs
clips: ['idle', 'walk', 'lookdown', 'covereyes', 'falling', 'celebrate'],
subclips: {
// Trim a long Mixamo clip down to just the settle frames
covereyes: { start: 0, end: 48, fps: 30 },
},
container: undefined, // defaults to document.body
canvas: undefined, // explicit canvas; otherwise one is created
pixelsPerUnit: 120,
zIndex: 999,
parallax: true,
crossfade: 0.3,
lights: true,
});Notes
- Assets are yours. The SDK does not bundle the avatar GLB or animation JSONs — point it at whatever you host. Every URL is configurable; the three.ws CDN paths are only the defaults.
- Three.js is a peer dep so your bundler de-dupes it. Works with Three r150+.
- No animation-library dependency. All tweens use built-in
requestAnimationFrameeasings. - Root motion is locked automatically so walk-cycle hip translation doesn't drift the avatar off-screen.
Requirements
- Node
>=18. - Peer dependency:
three>=0.150.0. - A GLB avatar and clip JSONs you host (no bundled assets).
Related packages
@three-ws/avatar— the full avatar SDK with<agent-3d>, lipsync, and an avatar creator.@three-ws/viewer-presets— tuned light rig, floor reflection, and bloom configs.
Links
- Homepage: https://three.ws
- Changelog: https://three.ws/changelog
- Issues: https://github.com/nirholas/three.ws/issues
- License: Apache-2.0 — see LICENSE
