Skip to content

Animation

PolyCSS can play usable glTF / GLB animation clips by sampling them into polygon frames. The parser exposes ParseResult.animation; usePolyAnimation and createPolyAnimationMixer drive a mesh handle over time.

Animation in PolyCSS is a polygon-frame pipeline: the parser turns glTF / GLB animation data into a sampler, and the renderer receives ordinary Polygon[] frames.

When loadMesh() or parseGltf() finds usable animation clips, the returned ParseResult.animation exposes clip metadata and a sample() function. Sampling evaluates the source animation at a given time, applies the animated pose to the mesh, and returns polygons for that moment.

usePolyAnimation and createPolyAnimationMixer sit on top of that sampler. They manage actions, looping, playback speed, fades, and cross-fades, then apply each sampled frame to a mesh handle.

Use the core mixer directly. The mesh handle returned by scene.add() satisfies PolyAnimationTarget.

import {
createPolyCamera,
createPolyScene,
createPolyAnimationMixer,
loadMesh,
} from "@layoutit/polycss";
const camera = createPolyCamera({ rotX: 65, rotY: 45 });
const scene = createPolyScene(host, { camera });
const result = await loadMesh("/character.glb", { meshResolution: "lossless" });
const mesh = scene.add(result, { merge: false, stableDom: true });
if (result.animation?.clips.length) {
const mixer = createPolyAnimationMixer(mesh, result.animation);
mixer.clipAction(result.animation.clips[0].name).reset().play();
let last = performance.now();
function tick(now: number) {
mixer.update((now - last) / 1000);
last = now;
requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
}
  • usePolyAnimation owns its requestAnimationFrame loop. Vanilla callers own the loop and call mixer.update(deltaSeconds) themselves.
  • usePolyAnimation and the core mixer expose familiar action methods: play, stop, reset, fadeIn, fadeOut, crossFadeTo, setLoop, setEffectiveTimeScale, and setEffectiveWeight.
  • Cross-fading assumes the sampled clips share matching polygon counts and vertex order. That is true for clips from the same parsed mesh.
  • LoopOnce, LoopRepeat, and LoopPingPong match the three.js numeric constants.
  • dispose() still matters: parser-created blob URLs should be revoked when the model is no longer used.
  • Loading Meshes: Parser options and mesh lifecycle.
  • Core Types: ParseAnimationController, PolyAnimationMixer, and clip types.
  • Performance: Why skeletal animation is the render-loop exception.