Skip to content

PolyCSS Morph

@layoutit/polycss-morph is an imperative, framework-agnostic layer for models whose geometry and paint resources are built ahead of time, then updated through a stable DOM graph.

Terminal window
npm install @layoutit/polycss-morph

Use the regular Polygon[] path when a mesh does not need prepared updates. Use Morph when a model needs stable DOM elements plus morph targets, semantic controls, springs, joint skinning, or prepared playback.

The Node and browser dependency graphs are separate:

EntryEnvironmentPurpose
@layoutit/polycss-morph/prepareNodeTurn glTF/GLB sources into deterministic model packages with CSS triangle plans and browser fallbacks.
@layoutit/polycss-morphBrowser or shared codeValidate, load, mount, and update prepared models.

The Node preparer creates static-prepared and morph-regions models. Custom tooling can create validated joint-skin and prepared-playback models.

import { preparePolyMorphModel } from "@layoutit/polycss-morph/prepare";
await preparePolyMorphModel({
configPath: "./source/prepare.json",
outputRoot: "./public/model/package",
});

Preparation writes resources before manifest.json. Re-running with check: true verifies the complete existing inventory and bytes without rewriting them.

import {
createPolyMorphDeformationRuntime,
loadPolyMorphPackage,
mountPolyMorphModel,
} from "@layoutit/polycss-morph";
const loaded = await loadPolyMorphPackage("/model/");
const mounted = mountPolyMorphModel(host, loaded.model, {
resources: loaded.resources,
});
const deformation = createPolyMorphDeformationRuntime(loaded.model);
const frame = deformation.sample({
tick: 0,
morphWeights: { "pin-c06-r04-lift": 0.5 },
});
mounted.apply({ leaves: frame.leafUpdates });

The same imperative API is used from vanilla, React, or Vue applications. Morph does not ship framework wrappers. Prepared playback commits explicitly: apply sample.update, then call runtime.commit(sample) only after mounted.apply(...) succeeds.

If an application already owns a retained DOM graph, createPolyMorphPreparedDomTarget adopts its source-ordered model, shape, and leaf elements as indexed write targets. Morph deduplicates requested values and invalidates the writers at destroy; the application retains DOM teardown ownership.

This 768-polygon model from the three.js morph-target example exposes two prepared targets. Spherify moves the cube toward a sphere; Swirl applies the source twist target. Both sliders may be combined while the same DOM leaves remain mounted. Drag anywhere on the stage to orbit the model.

Loading model…

Drag to orbit · 768 DOM polygons · MIT

The page samples only when a slider changes. Geometry follows the slider directly; releasing it commits the nearest prepared lighting state with at most two ancestor custom-property writes:

const deformation = createPolyMorphDeformationRuntime(model);
const mounted = mountPolyMorphModel(host, loaded.model, {
resources: loaded.resources,
});
function update(spherify: number, twist: number) {
const frame = deformation.sample({
tick: tick++,
morphWeights: { spherify, twist },
});
mounted.apply({ leaves: frame.leafUpdates });
}

Morph owns no scheduler. Applications decide whether weights come from direct input, state, an animation sampler, or a spring.

ProfileBehavior
static-preparedRetained rendering with no deformation.
morph-regionsSparse morph targets, semantic controls, springs, and clip channels.
joint-skinHierarchical joint transforms and normalized weighted skinning.
prepared-playbackSource-ordered retained model, shape, transform, visibility, opacity, and image-row changes.

Mount once, then sample and apply:

  • the application owns input and timing;
  • Morph owns no animation scheduler;
  • leaf elements retain identity until teardown;
  • unchanged samples perform no writes;
  • sparse samples visit only affected leaves;
  • runtime updates do not reconstruct topology or redraw prepared image resources.

Morph chooses the triangle paint path once when it mounts. It uses corner-shape where available, a larger CSS border triangle in Firefox, and each leaf’s prepared polygon-sized alpha-atlas slice in WebKit/Safari. Mount uses the loader’s verified image bytes and revokes their object URLs at teardown; the browser never refetches, generates, or redraws those pages.

Morph owns the prepared model format and sparse DOM updates. Your application owns input, timing, presentation, model-specific preparation, and product behavior.