Stamps copies of its content across a set of points — the way to place many instances (a grid of spheres, particles over a mesh's surface) without authoring each one by hand.
JavaScript API:
Cloner
The cloner is a SceneNode that instances geometry on the GPU: it takes
what to clone from its content children (any clonable node — a mesh, a spline)
and where to place them from a points-source. One points-source and one small
template become thousands of positioned copies, and when the points-source
changes the clones follow automatically.
Key attributes. mode chooses how clones are drawn: object renders each as
a full instance (its own transform, pickable), vertex merges them into one
particle-like batch that is cheaper at high counts. clone-scale uniformly
resizes every clone. align-to-normal orients clones to the source surface's
normal (with alignment-axis selecting which local axis points along it) — useful
for studding a surface. illumination-min-scale / illumination-max-scale size
clones by how much light they receive.
Related. The points-source child decides the layout: sk-grid-points-source
for a lattice, sk-mesh-vertex-points-source to sit on a mesh's vertices,
and the other *-points-source shapes. To drive each clone individually on the
GPU (push them from a point, weight them by a field) add a
sk-simple-point-constraint — the per-clone counterpart to the single-node
constraints.
Serve these docs to run the live example.
WebGPU needs a secure context — open this page via grunt serve
rather than double-clicking the file. The Markup tab works from disk.
<!-- A side-by-side demo of `align-to-normal` + `alignment-axis`. Both halves
are built identically: a lattice cloner (#lattice-y / #lattice-z) clones a
vertical reference line at every point of a 3×3 grid — so you can SEE the
clone positions — and a SECOND cloner reuses those exact positions via
<sk-cloner-points-source cloner="#…"> (sharing the lattice's GPU transform
buffer rather than recomputing a grid). That second cloner ROUND-ROBINS
three templates across the positions: a gold CONE, a blue cube spinning
about its y-axis, and a group holding BOTH (one template, stamped
together). Templates can be a mesh, a SPLINE, or a group.
The two second-cloners differ ONLY in `alignment-axis`. `align-to-normal`
orients each clone to its source surface's normal; the grid is flat so
that normal points straight up (+y). `alignment-axis` picks WHICH local
axis follows it: LEFT uses `+y`, so each cone's own tip (its +y) points up
and the cones stand UPRIGHT; RIGHT uses `+z`, so each cone's side (its +z)
points up and the cones lie TIPPED on their sides. Same clones, one
attribute changed, a visibly different result. -->
<sk-scene>
<sk-directional-light p="40deg" h="25deg" intensity="1.0"></sk-directional-light>
<sk-ambient-light intensity="0.4"></sk-ambient-light>
<sk-plain-material id="cone-mat" base-color="#d8a24a"></sk-plain-material>
<sk-plain-material id="cube-mat" base-color="#5b7b9a"></sk-plain-material>
<sk-plain-material id="line-mat" base-color="#8bab8a"></sk-plain-material>
<!-- Ground plane under the cloners: standard dark-gray floor + darker grid. -->
<sk-plain-material id="ground-mat" base-color="#3a3a3a"></sk-plain-material>
<sk-plain-material id="grid-mat" base-color="#1c1c1c"></sk-plain-material>
<sk-plane y="-0.5" width="18" height="9" width-segments="12" height-segments="6">
<sk-surface-paint material="#ground-mat"></sk-surface-paint>
<sk-edge-paint material="#grid-mat"></sk-edge-paint>
</sk-plane>
<!-- ===== LEFT: alignment-axis="+y" (cones stand upright) ===== -->
<!-- Lattice: the position source — a 3×3 grid marked by vertical lines. -->
<sk-cloner id="lattice-y" mode="object" x="-4">
<sk-grid-points-source count-x="3" count-y="1" count-z="3"
spacing-x="1.8" spacing-y="1.8" spacing-z="1.8"></sk-grid-points-source>
<sk-spline>
<sk-points-spline-source basis="linear"
points="[[0,0,0],[0,1.6,0]]"></sk-points-spline-source>
<sk-stroke-paint material="#line-mat" width="2.5"></sk-stroke-paint>
</sk-spline>
</sk-cloner>
<!-- Round-robin cone / spinning cube / group — aligned +y (tips follow the
up-normal, so the cones stand upright). -->
<sk-cloner mode="object" x="-4" align-to-normal alignment-axis="+y">
<sk-cloner-points-source cloner="#lattice-y"></sk-cloner-points-source>
<sk-cone bottom-radius="0.35" height="1.0" orientation="+y">
<sk-surface-paint material="#cone-mat"></sk-surface-paint>
</sk-cone>
<sk-cube size="0.6">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-animation duration="2500ms" iterations="Infinity">
<sk-keyframe offset="0" h="0deg"></sk-keyframe>
<sk-keyframe offset="1" h="360deg"></sk-keyframe>
</sk-animation>
</sk-cube>
<sk-scene-node>
<sk-cone bottom-radius="0.35" height="1.0" orientation="+y">
<sk-surface-paint material="#cone-mat"></sk-surface-paint>
</sk-cone>
<sk-cube size="0.6">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-animation duration="2500ms" iterations="Infinity">
<sk-keyframe offset="0" h="0deg"></sk-keyframe>
<sk-keyframe offset="1" h="360deg"></sk-keyframe>
</sk-animation>
</sk-cube>
</sk-scene-node>
</sk-cloner>
<sk-overlay-annotation x="-4" y="3.2" anchor="center">
<div style="color:#fff; background:rgba(0,0,0,0.55); padding:2px 6px;
border-radius:3px; font:12px sans-serif;">alignment-axis="+y"</div>
</sk-overlay-annotation>
<!-- ===== RIGHT: alignment-axis="+z" (cones tip onto their sides) ===== -->
<sk-cloner id="lattice-z" mode="object" x="4">
<sk-grid-points-source count-x="3" count-y="1" count-z="3"
spacing-x="1.8" spacing-y="1.8" spacing-z="1.8"></sk-grid-points-source>
<sk-spline>
<sk-points-spline-source basis="linear"
points="[[0,0,0],[0,1.6,0]]"></sk-points-spline-source>
<sk-stroke-paint material="#line-mat" width="2.5"></sk-stroke-paint>
</sk-spline>
</sk-cloner>
<!-- The SAME round-robin templates — aligned +z (sides follow the
up-normal, so the cones tip over). -->
<sk-cloner mode="object" x="4" align-to-normal alignment-axis="+z">
<sk-cloner-points-source cloner="#lattice-z"></sk-cloner-points-source>
<sk-cone bottom-radius="0.35" height="1.0" orientation="+y">
<sk-surface-paint material="#cone-mat"></sk-surface-paint>
</sk-cone>
<sk-cube size="0.6">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-animation duration="2500ms" iterations="Infinity">
<sk-keyframe offset="0" h="0deg"></sk-keyframe>
<sk-keyframe offset="1" h="360deg"></sk-keyframe>
</sk-animation>
</sk-cube>
<sk-scene-node>
<sk-cone bottom-radius="0.35" height="1.0" orientation="+y">
<sk-surface-paint material="#cone-mat"></sk-surface-paint>
</sk-cone>
<sk-cube size="0.6">
<sk-surface-paint material="#cube-mat"></sk-surface-paint>
<sk-animation duration="2500ms" iterations="Infinity">
<sk-keyframe offset="0" h="0deg"></sk-keyframe>
<sk-keyframe offset="1" h="360deg"></sk-keyframe>
</sk-animation>
</sk-cube>
</sk-scene-node>
</sk-cloner>
<sk-overlay-annotation x="4" y="3.2" anchor="center">
<div style="color:#fff; background:rgba(0,0,0,0.55); padding:2px 6px;
border-radius:3px; font:12px sans-serif;">alignment-axis="+z"</div>
</sk-overlay-annotation>
</sk-scene>
| Attribute | Type | Default | Description |
|---|---|---|---|
id |
<id> |
— | The element's unique identifier — the standard HTML global id attribute. |
draggable |
<boolean> |
false |
Gets whether this node can be dragged. |
xanimatable |
<number> |
0 |
X position. |
yanimatable |
<number> |
0 |
Y position. |
zanimatable |
<number> |
0 |
Z position. |
hanimatable |
<angle> |
0 |
Heading (Y-axis rotation) in radians. |
panimatable |
<angle> |
0 |
Pitch (X-axis rotation) in radians. |
banimatable |
<angle> |
0 |
Bank (Z-axis rotation) in radians. |
quaternionanimatable |
<quaternion> ({ <number>, <number>, <number>, <number> }) |
— | Rotation as quaternion (overrides h, p, b if provided) |
sxanimatable |
<number> |
1 |
X-axis scale factor. |
syanimatable |
<number> |
1 |
Y-axis scale factor. |
szanimatable |
<number> |
1 |
Z-axis scale factor. |
s |
<number> |
— | Uniform scale factor for all axes (overrides sx, sy, sz if provided) write-only |
overlay |
<boolean> |
false |
Whether this cloner's entire rendered output (all clones) renders as |
overlay-occluded-opacity |
<number> |
0.3 |
Alpha multiplier for overlay fragments occluded by scene geometry. |
cast-shadow |
<boolean> |
true |
Gets whether this cloner's instances cast shadows. |
mode |
<cloner-mode> ("object" | "vertex") |
— | Cloning operation mode. |
align-to-normal |
<boolean> |
— | Whether to orient clones to align with point normals. |
alignment-axis |
<orientation-axis> ("+x" | "-x" | "+y" | "-y" | "+z" | "-z") |
— | Which local axis of the cloned content aligns with the normal direction. |
apply-scale |
<boolean> |
— | Whether to apply each point's per-clone scale (the source localMatrix's |
clone-scaleanimatable |
<number> |
— | Uniform scale factor applied to each clone. |
illumination-enabled |
<boolean> |
false |
Whether illumination-based scaling is enabled. |
illumination-min-scaleanimatable |
<number> |
— | Minimum scale factor for illumination-based scaling. |
illumination-max-scaleanimatable |
<number> |
— | Maximum scale factor for illumination-based scaling. |
illumination-use-shadows |
<boolean> |
false |
Whether to use shadow map for illumination visibility calculations. |
Standard DOM event-handler content attributes — the value is JavaScript run when the event fires. They behave exactly as on any HTML element.
onclick, ondblclick, onauxclick, oncontextmenu, onpointerdown, onpointerup, onpointermove, onpointercancel, onpointerover, onpointerout, onpointerenter, onpointerleave, ongotpointercapture, onlostpointercapture, onwheel, ondragstart, ondrag, ondragenter, ondragover, ondragleave, ondrop, ondragend
One or more clonable children defining the content to instance — meshes (sphere/cube/cylinder/etc.), splines, SceneNode groups (all descendants cloned together as one template unit), or nested cloners; multiple direct children alternate across points. Plus zero or more point-source / point-constraint / field-constraint children to place the clones. Point constraints also attach to mesh vertices and spline polyline vertices, not only cloners (Issues #2495, #2513).