cesiumgs/cesiumjs-skills/cesiumjs-models-particles

Claude Code and agent skill by cesiumgs/cesiumjs-skills

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

CesiumJS models, glTF, and particle effects - Model, KHR_meshopt_compression, CAD glTF extensions, EdgeDisplayMode, ModelAnimation, ModelNode, ParticleSystem, emitters, GPM extensions. Use when loading compressed or CAD-style glTF/GLB models, controlling edge rendering, playing model animations, positioning particles, or working with geospatial positioning metadata.

Install

npx vskill@latest install cesiumgs/cesiumjs-skills/cesiumjs-models-particles

SKILL.md

CesiumJS Models, glTF & Particle Effects

Version baseline: CesiumJS v1.144.

Quick Reference

ClassPurpose
ModelLow-level glTF/GLB primitive; positioned via modelMatrix
ModelAnimationActive animation instance on a model
ModelAnimationCollectionCollection at model.activeAnimations
ModelNodeNamed node with modifiable transform
ModelFeaturePer-feature styling/picking for feature-ID models
EdgeDisplayModeControls draft glTF edge-visibility rendering on Model/Cesium3DTileset
ParticleSystemBillboard-based particle manager (fire, smoke, rain)
ParticleSingle particle with position, velocity, life
ParticleBurstScheduled burst of particles
BoxEmitter / CircleEmitterEmit within box volume / flat disk
ConeEmitter / SphereEmitterEmit from cone tip / within sphere

The Entity API exposes models through ModelGraphics (see cesiumjs-entities). The Primitive API uses Model.fromGltfAsync for full control over modelMatrix, animations, and node transforms.


Loading a glTF/GLB Model

Always use the async factory -- never call the constructor directly.

js
import { Model, Cartesian3, Transforms, HeadingPitchRoll, Math as CesiumMath } from "cesium";
const model = await Model.fromGltfAsync({ url: "path/to/model.glb" });
viewer.scene.primitives.add(model);
Public Sample Models

CesiumJS ships sample models usable without ion tokens:

https://raw.githubusercontent.com/CesiumGS/cesium/main/Apps/SampleData/models/CesiumAir/Cesium_Air.glb
https://raw.githubusercontent.com/CesiumGS/cesium/main/Apps/SampleData/models/CesiumMan/Cesium_Man.glb
https://raw.githubusercontent.com/CesiumGS/cesium/main/Apps/SampleData/models/CesiumMilkTruck/CesiumMilkTruck.glb

CesiumJS 1.143 decodes KHR_meshopt_compression automatically, including the v1 attribute codec and COLOR filter. Do not import a decoder or private loader helper. When loading compressed glTF, CAD-style lines/points/edges, or constant-LOD textures, read REFERENCE.md for the complete support and authoring matrix. The same loader behavior applies to glTF content inside 3D Tiles.

Positioned Model with Heading
js
const position = Cartesian3.fromDegrees(-123.074, 44.050, 5000);
const hpr = new HeadingPitchRoll(CesiumMath.toRadians(135), 0, 0);
const model = await Model.fromGltfAsync({
url: "CesiumAir.glb",
modelMatrix: Transforms.headingPitchRollToFixedFrame(position, hpr),
minimumPixelSize: 128, // never smaller than 128 px on screen
maximumScale: 20000, // cap for minimumPixelSize enlargement
scale: 2.0, // uniform scale multiplier
});
viewer.scene.primitives.add(model);
Visual eval framing: for model screenshots, the model must be fully inside
the frame and recognizable, not clipped at the bottom edge. Use
minimumPixelSize 256-400 for public sample aircraft, add a subtle
silhouetteColor/silhouetteSize, and place the camera with explicit
coordinates aimed at the known model position. Avoid shallow pitches that put
the model below the frame or rely on viewer.flyTo(model), which is
version-sensitive for Model primitives.
Avoiding Distorted Model Appearance

Models can appear stretched or warped when scale is applied non-uniformly or when the orientation matrix is built incorrectly. Common pitfalls:

  • Always use `Transforms.headingPitchRollToFixedFrame` (or the Entity API's `headingPitchRollQuaternion`) for ground-aligned orientation. Hand-rolled quaternions often invert pitch/roll axes and produce vertically stretched silhouettes.
  • Use `scale` (uniform) for size, not a non-uniform `Matrix4.fromScale`. A non-uniform scale baked into modelMatrix will distort the model. Reserve Matrix4.fromScale for per-node tweaks (e.g., stretching a single turret), not the whole model.
  • Pair `minimumPixelSize` with a sensible `maximumScale`. Without a cap, distant small models can balloon to fill the frame and look elongated when the camera is close.
Key Model.fromGltfAsync Options
OptionTypeDefault
url`string\Resource`required
modelMatrixMatrix4IDENTITY
scalenumber1.0
minimumPixelSizenumber0.0
maximumScalenumber--
showbooleantrue
color / colorBlendMode / colorBlendAmountColor / ColorBlendMode / number-- / HIGHLIGHT / 0.5
edgeDisplayModeEdgeDisplayModeSURFACES_ONLY
silhouetteColor / silhouetteSizeColor / numberRED / 0.0
shadowsShadowModeENABLED
heightReferenceHeightReferenceNONE
customShaderCustomShader--
idany--
allowPickingbooleantrue

Readiness and Lifecycle

fromGltfAsync resolves once glTF JSON is parsed, but WebGL resources may still load. Wait for readyEvent before accessing animations, nodes, or boundingSphere.

js
const model = await Model.fromGltfAsync({ url: "robot.glb" });
viewer.scene.primitives.add(model);
model.readyEvent.addEventListener(() => {
console.log("Bounding sphere:", model.boundingSphere);
});
js
// Synchronous check
if (model.ready) { const bs = model.boundingSphere; }

Animations

Managed through model.activeAnimations (ModelAnimationCollection).

Play by Name / Play All
js
model.readyEvent.addEventListener(() => {
// Single animation
const anim = model.activeAnimations.add({
name: "Walk", // glTF animation name
loop: Cesium.ModelAnimationLoop.REPEAT, // NONE | REPEAT | MIRRORED_REPEAT
multiplier: 1.0, // playback speed (must be > 0)
});
anim.start.addEventListener((m, a) => console.log(`Started: ${a.name}`));
// Or play all animations at once
model.activeAnimations.addAll({
loop: Cesium.ModelAnimationLoop.REPEAT,
multiplier: 0.5,
});
});

Additional add options: index, reverse, startTime, stopTime, delay, removeOnStop, animationTime (custom time callback).

Animations require `viewer.clock.shouldAnimate = true` to advance. A walking character will appear frozen mid-pose (or distorted if at the seam between keyframes) if the clock is stopped.

Animation Events
js
animation.start.addEventListener((model, animation) => { });
animation.update.addEventListener((model, animation, time) => { });
animation.stop.addEventListener((model, animation) => { });
// Collection-level
model.activeAnimations.animationAdded.addEventListener((model, anim) => { });
js
model.activeAnimations.remove(animation); // remove one
model.activeAnimations.removeAll(); // remove all

Model Nodes

Override named node transforms for procedural animation (e.g., turret rotation).

js
model.readyEvent.addEventListener(() => {
const node = model.getNode("Turret");
node.matrix = Cesium.Matrix4.fromScale(
new Cesium.Cartesian3(5.0, 1.0, 1.0), node.matrix
);
});

Properties: name (read-only), id (read-only index), show (boolean), matrix (Matrix4 -- set to undefined to restore original and re-enable glTF animations).


Coloring, Silhouettes, and Feature Picking

js
// Tint + silhouette
model.color = Cesium.Color.RED.withAlpha(0.5);
model.colorBlendMode = Cesium.ColorBlendMode.MIX;
model.colorBlendAmount = 0.5;
model.silhouetteColor = Cesium.Color.YELLOW;
model.silhouetteSize = 2.0;
Edge Display Mode (Experimental, 1.142+)

For glTF assets using the draft EXT_mesh_primitive_edge_visibility extension, EdgeDisplayMode controls whether extension-provided edges are hidden, composited over surfaces, or rendered alone. Models without the extension are unaffected.

js
import { EdgeDisplayMode, Model } from "cesium";
const model = await Model.fromGltfAsync({
url: "/models/cad-part.glb",
edgeDisplayMode: EdgeDisplayMode.SURFACES_AND_EDGES,
});
viewer.scene.primitives.add(model);
model.edgeDisplayMode = EdgeDisplayMode.EDGES_ONLY; // CAD-style wireframe
model.edgeDisplayMode = EdgeDisplayMode.SURFACES_ONLY; // default

When a glTF has EXT_mesh_features or EXT_structural_metadata, picking returns a ModelFeature:

js
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction((movement) => {
const picked = viewer.scene.pick(movement.endPosition);
if (picked instanceof Cesium.ModelFeature) {
picked.getPropertyIds().forEach((name) => {
console.log(`${name}: ${picked.getProperty(name)}`);
});
picked.color = Cesium.Color.YELLOW;
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

Height Reference

js
// Primitive API -- scene is required for height reference
const model = await Model.fromGltfAsync({
url: "truck.glb",
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
scene: viewer.scene,
});
// Entity API
viewer.entities.add({
position: Cartesian3.fromDegrees(-75.59, 40.03),
model: { uri: "truck.glb", heightReference: Cesium.HeightReference.CLAMP_TO_GROUND },
});

Values: NONE, CLAMP_TO_GROUND, RELATIVE_TO_GROUND, CLAMP_TO_TERRAIN, RELATIVE_TO_TERRAIN, CLAMP_TO_3D_TILE, RELATIVE_TO_3D_TILE.


Particle Systems

ParticleSystem renders billboard-based effects. Position with modelMatrix (world) and emitterModelMatrix (local offset).

Always set `viewer.clock.shouldAnimate = true` before adding a particle system -- particles only move when the clock is running. A stopped clock produces a static "blob" at the emitter origin rather than a directional plume.

Producing a Legible Vertical Plume

A common failure mode is rendering a diffuse spherical blob instead of a recognizable rising column. To make plumes read as vertical:

  • Use `CircleEmitter` or a narrow `ConeEmitter` for upward-biased velocity. SphereEmitter and BoxEmitter radiate in all directions and produce blob-like shapes.
  • Bias velocity strongly upward -- emitters' local +Z is up in the modelMatrix frame; set modelMatrix via Transforms.eastNorthUpToFixedFrame so +Z is local up.
  • Use a multi-second `minimumParticleLife` / `maximumParticleLife` (e.g., 1.5-4.0) so particles travel far enough to form a visible column before fading.
  • Scale particles over their lifetime (startScale small, endScale 3-6x larger) so the plume widens with height, matching real smoke.
  • Keep the emitter footprint smaller than the visible plume. For crater smoke, use a small disk or point source; oversized ground ellipses read as the subject instead of a source marker.
  • Avoid solving clipping by making the plume huge. If the top is clipped, first increase camera range or lower particle life/speed/endScale; do not let the plume fill all four edges of the frame.
  • Wait for several seconds of simulation time before screenshotting -- the plume needs to develop. Advance the clock or use viewer.clock.shouldAnimate = true and wait.
Smoke Trail
js
import { ParticleSystem, CircleEmitter, Color, Cartesian2, Transforms, Cartesian3 } from "cesium";
viewer.clock.shouldAnimate = true; // required -- particles don't move on a stopped clock
const smokeSystem = new ParticleSystem({
image: "smoke.png",
startColor: Color.LIGHTGRAY.withAlpha(0.7),
endColor: Color.WHITE.withAlpha(0.0),
startScale: 1.0,
endScale: 5.0,
emissionRate: 10,
minimumSpeed: 1.0,
maximumSpeed: 4.0,
minimumParticleLife: 1.2,
maximumParticleLife: 3.0,
imageSize: new Cartesian2(25, 25), // pixel size
emitter: new CircleEmitter(2.0), // radius in meters
modelMatrix: Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(-75.157, 39.978)),
lifetime: 16.0,
loop: true,
});
viewer.scene.primitives.add(smokeSystem);
Volcanic / Crater Smoke Calibration

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
84
Repository forks
12
7-day trend score
22

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 16 Jun 2026, 4 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/cesiumgs/cesiumjs-skills/cesiumjs-models-particles/badge)](https://verified-skill.com/skills/cesiumgs/cesiumjs-skills/cesiumjs-models-particles)

Something wrong with this skill or its page? Tell us.