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

@lovelace_lol/polymer

v0.0.7

Published

Pure ClojureScript character agencies for Character Loom.

Readme

Polymer

Clean ClojureScript character agency package for Character Loom.

Production releases publish to npm as @lovelace_lol/polymer on every push to main. Hosts should depend on the npm package (latest or a semver range). PR staging may still use git SHA pins or LoomLarge Depends-on links.

Polymer is intentionally separate from Latticework and Polyester. It is built as a Society of Mind agency system: small CLJS agencies collaborate through streams, plan locally, schedule their own work, and keep side effects inside the agencies responsible for performing them.

See docs/agency-architecture.md for the agency collaboration, scheduler, side-effect, stream, and runtime-target rules.

Agency API

The package exposes an agency system:

const agencies = createCharacterAgencies();
agencies.dispatch({ agency: "blink", command: { type: "enable" } });

agencies.events.subscribe((message) => {});
agencies.snapshot();

Agencies communicate through incoming and outgoing streams of plain data. Messages may be facts, goals, requests, constraints, priorities, status, or diagnostics. Requests that imply side effects are handled by the receiving agency's local planner, scheduler, and effector path.

Host applications may dispatch external commands, observe outgoing streams, and read snapshots for diagnostics or configuration. Host applications should not serve as the normal message bus between Polymer agencies.

The character profile may provide an agencySociety document that controls which existing agencies and message routes are enabled. A host-side graph editor can apply route admission changes without recreating agency-local state:

const nextSociety = agencies.updateSocietyRouting(profile.agencySociety);

The update is deliberately limited to routing. Agency-local configure and priors values are applied when createCharacterAgencies creates the agencies; the graph is not a central planner and cannot bypass their planners or schedulers.

Current Agencies

Polymer currently includes early Blink, Animation, Gaze, Eye/Head Tracking, TTS, LipSync, Prosodic Expression, and Gesture agencies, plus first slices for Conversation, Camera Context, and Transcription. These are implementation milestones, not the limits of the architecture. Each agency should follow the same local GOAP/planner, scheduler, stream, transform, and effector pattern as the package grows.

The text-only LipSync pronunciation fallback and its accuracy limits are documented in docs/lipsync-pronunciation-fallback.md.

Runtime-specific work belongs at the edge. The current animation path can target the existing web runtime, but Polymer core should remain able to support other runtime targets such as Babylon, Unity, Godot, robotics, or future animation libraries through separate runtime-specific boundaries.

Hair appearance and physics are model/runtime capabilities rather than character-behavior agencies. Polymer therefore does not expose a Hair agency, Hair planner, or Hair scheduler. It continues to re-export Embody's hair color, material-normalization, and Wasm physics APIs so hosts can configure the character runtime without importing Embody directly.

Gesture Agency

Gesture is the arm/hand gesticulation agency. It accepts LoomLarge-authored gesture snapshots as plain data and turns them into typed bone-channel animation snippets. It does not call scene bones, host UI state, storage, or the animation runtime. Gesture snapshots are compatible with the Gestures tab profile shape: id/name, optional description or text representation, emoji trigger, left/right/both/custom scope, captured source metadata, duration, priority, affected bones, static bone targets, and optional time-based keyframes.

Gesture commands can still name an exact gesture or emoji, but the planner also accepts gesture goals. A goal asks Gesture to choose from the authored library by intent, tags, scope, required or avoided effectors, cooldown, active conflicts, and capacity before it emits Animation requests.

Gesture GOAP Planning

Gesture GOAP plans for one local world-state transition: a suitable authored arm/hand gesture is selected and safely scheduled through Animation, or the agency records why that goal cannot be satisfied. It is not a second public playback API.

The planner uses the incoming command, the gesture library, active snippets, and Gesture config to decide:

  • which gesture best satisfies an explicit gesture id, emoji mapping, intent, tags, text hints, scope, and bone constraints
  • whether the selected gesture is valid motion and is out of cooldown
  • whether active snippets conflict on affected bones
  • whether capacity is available under maxActive
  • whether to ignore, remove only conflicting snippets, remove the oldest snippets needed for capacity, build a snippet, schedule Animation, and record the result

Host integrations should prefer the character agency network so Gesture's animation requests are routed to Animation inside Polymer:

const agencies = createCharacterAgencies({
  animation: { engine },
  gesture: {
    gestures: profile.characterGestures,
    emojiMappings: profile.gestureEmojiMappings,
  },
});

agencies.dispatch({ agency: "gesture", command: { type: "playEmoji", emoji: "👋" } });
agencies.dispatch({
  agency: "gesture",
  command: {
    type: "gesture.goal",
    goal: {
      intent: "greeting",
      tags: ["wave"],
      scope: "right",
      avoidBones: ["HAND_L"],
    },
  },
});

The host supplies the gesture library and may observe streams for diagnostics, but Gesture's own scheduler is responsible for replacement, cancellation, and completion cleanup before Animation owns the runtime side effect.

Character host (Embody scene + model loading)

Polymer re-exports Embody's character host so LoomLarge and other hosts can set up a character without depending on Embody directly.

Default path — pass a container element and a character URL:

import {
  createCharacterHost,
  createCharacterAgencies,
} from '@lovelace_lol/polymer';

const host = await createCharacterHost({
  container: document.getElementById('viewport'),
  character: {
    modelUrl: '/characters/jonathan.glb',
    presetType: 'cc4',
  },
});

const agencies = createCharacterAgencies({
  animation: { engine: host.engine },
});

Named scene types (studio, showcase, inspection, void) bundle background, lighting preset, and shadow-plane defaults; enumerate them via CHARACTER_SCENE_TYPES / CHARACTER_SCENE_TYPE_IDS:

const host = await createCharacterHost({
  container,
  character: { modelUrl: '/characters/jonathan.glb' },
  scene: { type: 'showcase' },
});

Advanced path — inject an existing Three.js scene:

const host = await createCharacterHost({
  container: mountEl,
  character: { modelUrl: '/characters/jonathan.glb', presetType: 'cc4' },
  external: { scene, renderer, camera },
});

Polymer stays agency-oriented: it does not own the render loop. Use createCharacterHost / loadCharacterModel for scene+model setup, then inject host.engine into createCharacterAgencies.

For an already-managed Three.js scene, Polymer also exposes a CLJS-owned host that talks directly to Embody's Rust/Wasm RuntimeCore:

import {
  createRustEmbodyHostUnbound,
  initEmbodyCore,
  supportsRustEmbodyPreset,
} from '@lovelace_lol/polymer';

await initEmbodyCore();
if (supportsRustEmbodyPreset('cc4')) {
  const engine = createRustEmbodyHostUnbound({
    profileMode: 'preset',
    presetId: 'cc4',
    profile: characterProfileOverrides,
  });
  engine.onReady({ model, meshes });
  engine.start();
}

The CLJS host owns the JavaScript API and render-loop bridge. Rust owns preset lookup, profile merge and intake, compiled bindings, transitions, and packed frame evaluation. Unsupported presets must stay on the compatibility host until they are explicitly embedded; they are never treated as CC4 aliases.

Git Install Contract

Polymer Git-SHA consumers should import checked-in JavaScript artifacts from dist. They should not need Java, Shadow CLJS, or a local Polymer build during application install.

The package intentionally has no prepare, install, or postinstall build lifecycle. Polymer CI runs the CLJS build, verifies that dist exposes the browser ESM API that Git-SHA consumers import, and prepublishOnly repeats the build/test/API check before an npm publish.

Worker Protocol Artifact

The package also ships @lovelace_lol/polymer/worker/agency-worker.js as an opt-in transport around the complete character agency API. It accepts createCharacterAgencies, dispatch, snapshot, and dispose messages and returns character events, snapshots, and lifecycle messages as plain data. The Worker does not add another planner or route between agencies; Polymer's character network keeps that responsibility.

CI executes the compiled artifact in a real Node worker thread. LoomLarge does not use this artifact yet, and this PR does not decide which agencies should be placed in workers. Selective placement, profiling, transferables, and worker pool decisions remain tracked in issue #62.