Best for
- Use when rendering performance-critical static or vector geometry, loading GeoJSON without entities, creating custom shapes, batching draw calls, or using low-level collections.
CesiumGS/cesiumjs-skills/skills/cesiumjs-primitives/SKILL.md
CesiumJS primitives and geometry - Primitive, GeometryInstance, Appearance, BufferPrimitive collections, GeoJsonPrimitive, Billboard/Label/PointPrimitive collections, built-in geometry shapes, ground primitives, classification. Use when rendering performance-critical static or vector geometry, loading GeoJSON without entities, creating custom shapes, batching draw calls, or using low-level collections.
Decision brief
Applies to: CesiumJS v1.143+ (ES module imports, ?? instead of defaultValue)
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/CesiumGS/cesiumjs-skills --skill "skills/cesiumjs-primitives"Inspect the Agent Skill "cesiumjs-primitives" from https://github.com/CesiumGS/cesiumjs-skills/blob/066c44ba85b4001cd5084d96179d6b73fc1a32e1/skills/cesiumjs-primitives/SKILL.md at commit 066c44ba85b4001cd5084d96179d6b73fc1a32e1. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
The Primitive API is the low-level rendering layer beneath the Entity API, trading convenience for performance.
Review the “Primitive” section in the pinned source before continuing.
Review the “Key Options” section in the pinned source before continuing.
All instances in one Primitive share a single draw call.
Review the “Updating Per-Instance Attributes” section in the pinned source before continuing.
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 93/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 114 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Applies to: CesiumJS v1.143+ (ES module imports,
??instead ofdefaultValue)
The Primitive API is the low-level rendering layer beneath the Entity API, trading convenience for performance.
Core formula: Primitive = GeometryInstance[] + Appearance
Primitives are immutable after first render -- geometry cannot change, but per-instance attributes update via primitive.getGeometryInstanceAttributes(id).
import {
Viewer, Primitive, GeometryInstance, EllipseGeometry,
EllipsoidSurfaceAppearance, Material, Cartesian3, Math as CesiumMath,
} from "cesium";
const viewer = new Viewer("cesiumContainer");
const scene = viewer.scene;
const primitive = scene.primitives.add(new Primitive({
geometryInstances: new GeometryInstance({
geometry: new EllipseGeometry({
center: Cartesian3.fromDegrees(-100.0, 40.0),
semiMinorAxis: 250000.0,
semiMajorAxis: 400000.0,
rotation: CesiumMath.PI_OVER_FOUR,
vertexFormat: EllipsoidSurfaceAppearance.VERTEX_FORMAT, // must match appearance
}),
id: "myEllipse", // returned by Scene.pick()
}),
appearance: new EllipsoidSurfaceAppearance({ material: Material.fromType("Stripe") }),
}));
| Option | Default | Purpose |
|---|---|---|
geometryInstances | -- | Single instance or array |
appearance | -- | Shading (Appearance subclass) |
show | true | Toggle visibility |
modelMatrix | Matrix4.IDENTITY | Transform all instances |
asynchronous | true | Build geometry on web worker |
releaseGeometryInstances | true | Free geometry after GPU upload |
allowPicking | true | false saves GPU memory |
shadows | ShadowMode.DISABLED | Cast/receive shadows |
All instances in one Primitive share a single draw call.
import {
Primitive, GeometryInstance, RectangleGeometry, EllipseGeometry,
PerInstanceColorAppearance, ColorGeometryInstanceAttribute,
Cartesian3, Rectangle, Color,
} from "cesium";
scene.primitives.add(new Primitive({
geometryInstances: [
new GeometryInstance({
geometry: new RectangleGeometry({
rectangle: Rectangle.fromDegrees(-140, 30, -100, 40),
vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
}),
id: "rect",
attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.RED.withAlpha(0.5)) },
}),
new GeometryInstance({
geometry: new EllipseGeometry({
center: Cartesian3.fromDegrees(-80, 35),
semiMinorAxis: 200000.0,
semiMajorAxis: 300000.0,
vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
}),
id: "ellipse",
attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.BLUE.withAlpha(0.5)) },
}),
],
appearance: new PerInstanceColorAppearance(),
}));
import { ColorGeometryInstanceAttribute, ShowGeometryInstanceAttribute } from "cesium";
// Wait for async geometry compilation
const removeListener = scene.postRender.addEventListener(() => {
if (!primitive.ready) return;
const attrs = primitive.getGeometryInstanceAttributes("rect");
attrs.color = ColorGeometryInstanceAttribute.toValue(Color.YELLOW);
attrs.show = ShowGeometryInstanceAttribute.toValue(true);
removeListener();
});
Nestable container -- scene.primitives is itself a PrimitiveCollection.
import { PrimitiveCollection, BillboardCollection, LabelCollection } from "cesium";
const group = new PrimitiveCollection();
group.add(new BillboardCollection());
group.add(new LabelCollection());
scene.primitives.add(group);
group.show = false; // toggle all children
| Need | Use |
|---|---|
| Entity lifecycle, clustering, per-entity styling, time-dynamic values | GeoJsonDataSource in cesiumjs-entities |
| One large GeoJSON object with low overhead and primitive-level performance | GeoJsonPrimitive in this skill |
| Tiled vector data, 3D Tiles LOD, metadata styling, feature picking | MVTDataProvider in cesiumjs-3d-tiles |
| Fully manual high-throughput point/polyline/polygon buffers | BufferPointCollection, BufferPolylineCollection, BufferPolygonCollection |
Use BufferPointCollection, BufferPolylineCollection, and BufferPolygonCollection
for very large vector datasets where Entity/DataSource overhead is too high. These
APIs were introduced in 1.140 (#13212) and refined through 1.142; they are
experimental and use flyweight primitive objects: reuse one BufferPoint,
BufferPolyline, or BufferPolygon when adding or iterating thousands of items.
import {
BlendOption,
BoundingSphere,
BufferPoint,
BufferPointCollection,
BufferPointMaterial,
Cartesian3,
Color,
} from "cesium";
const positions = [
Cartesian3.fromDegrees(-75.16, 39.95),
Cartesian3.fromDegrees(-73.98, 40.75),
];
const points = scene.primitives.add(new BufferPointCollection({
primitiveCountMax: positions.length,
allowPicking: true,
blendOption: BlendOption.TRANSLUCENT,
boundingVolume: BoundingSphere.fromPoints(positions), // world space in 1.142+
}));
const point = new BufferPoint();
const material = new BufferPointMaterial({
color: Color.CYAN.withAlpha(0.65),
outlineColor: Color.WHITE.withAlpha(0.9),
outlineWidth: 2,
size: 10,
});
positions.forEach((position, featureId) => {
points.add({
position,
featureId,
material,
}, point);
});
const picked = scene.pick(windowPosition);
if (picked?.collection === points) {
console.log(picked.index, picked.primitive.featureId);
}
Breaking change (1.141, #13448):
BufferPrimitiveCollection.modelMatrix,boundingVolume, andboundingVolumeWCare now readonly -- you may mutate the object in place, but reassigning the property (collection.modelMatrix = ...) throws. Update the existing matrix/volume instead of swapping in a new one.
1.142 notes:
boundingVolume is now world-space, not local/model-space. If you provide it manually, include the collection modelMatrix transform yourself.boundingVolume skips automatic recomputation; this helps large animated collections but makes you responsible for keeping the volume valid.blendOption is supported on all three buffer collections and enables alpha from BufferPrimitiveMaterial#color; BufferPointCollection also honors outlineColor.alpha.BlendOption.OPAQUE only when every material is fully opaque; use TRANSLUCENT or mixed blending when alpha varies.In 1.143, BufferPointCollection no longer leaks outlineColor into the fill
when outlineWidth is 0. Set the width to 0 to disable outlines; remove
transparent-outline workarounds that would otherwise complicate batching.
GeoJsonPrimitive loads GeoJSON directly into buffer primitive collections,
bypassing GeoJsonDataSource and the Entity layer. Prefer it for large static
or bulk-updated vector datasets. Keep using GeoJsonDataSource when you need
Entity conveniences, time-dynamic properties, clustering, or DataSource lifecycle
integration.
import { GeoJsonPrimitive } from "cesium";
const counties = await GeoJsonPrimitive.fromUrl("/data/counties.geojson", {
allowPicking: true,
});
scene.primitives.add(counties);
console.log(counties.featureCount);
console.log(counties.points); // BufferPointCollection | undefined
console.log(counties.polylines); // BufferPolylineCollection | undefined
console.log(counties.polygons); // BufferPolygonCollection | undefined
// Picking returns the GeoJsonPrimitive pick object, including source properties.
const picked = scene.pick(windowPosition);
if (picked?.parentPrimitive === counties) {
const featureId = picked.primitive.featureId;
console.log(counties.getId(featureId));
console.log(counties.getProperties(featureId));
}
GeoJsonPrimitive.fromGeoJson(parsedObject) is available when the GeoJSON is
already in memory. Source feature IDs are exposed through ids/getId(), and
source properties through properties/getProperties().
All geometries take shape parameters and a vertexFormat matching the Appearance. Most have a paired *OutlineGeometry. Outlines require a separate Primitive.
import {
Primitive, GeometryInstance, PolygonGeometry, PolygonOutlineGeometry,
PolygonHierarchy, PerInstanceColorAppearance, ColorGeometryInstanceAttribute,
Cartesian3, Color,
} from "cesium";
const positions = Cartesian3.fromDegreesArray([-115, 37, -115, 32, -107, 33, -102, 35]);
// Fill primitive
scene.primitives.add(new Primitive({
geometryInstances: new GeometryInstance({
geometry: new PolygonGeometry({
polygonHierarchy: new PolygonHierarchy(positions),
vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
}),
attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.CYAN.withAlpha(0.5)) },
}),
appearance: new PerInstanceColorAppearance(),
}));
// Outline primitive (separate draw call)
scene.primitives.add(new Primitive({
geometryInstances: new GeometryInstance({
geometry: new PolygonOutlineGeometry({ polygonHierarchy: new PolygonHierarchy(positions) }),
attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.WHITE) },
}),
appearance: new PerInstanceColorAppearance({ flat: true }),
}));
Every XxxGeometry has a matching XxxOutlineGeometry unless noted.
Surface (work with GroundPrimitive): CircleGeometry, CorridorGeometry, EllipseGeometry, PolygonGeometry, RectangleGeometry.
Volume (need modelMatrix): BoxGeometry (fromDimensions()), CylinderGeometry (cone when topRadius != bottomRadius), EllipsoidGeometry, SphereGeometry, FrustumGeometry, PlaneGeometry.
Path: CorridorGeometry (buffered path), PolylineVolumeGeometry (2D shape extruded along path), WallGeometry (vertical curtain).
Polygon: PolygonGeometry (holes via PolygonHierarchy), CoplanarPolygonGeometry (non-Earth-surface).
Line (no outline): PolylineGeometry (pixel-width), SimplePolylineGeometry (1px), GroundPolylineGeometry (GroundPolylinePrimitive only).
Box, Ellipsoid, Cylinder, and Frustum need a modelMatrix on the GeometryInstance.
import { GeometryInstance, BoxGeometry, PerInstanceColorAppearance,
ColorGeometryInstanceAttribute, Cartesian3, Matrix4, Transforms, Color } from "cesium";
const modelMatrix = Matrix4.multiplyByTranslation(
Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(-105, 40)),
new Cartesian3(0, 0, 250000), new Matrix4(),
);
new GeometryInstance({
geometry: BoxGeometry.fromDimensions({
dimensions: new Cartesian3(400000, 300000, 500000),
vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
}),
modelMatrix,
id: "floatingBox",
attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.CORAL) },
});
| Appearance | Use Case | Material? |
|---|---|---|
PerInstanceColorAppearance | Per-instance color | No |
MaterialAppearance | Arbitrary geometry + Material | Yes |
EllipsoidSurfaceAppearance | Surface geometry + Material (fewer attrs) | Yes |
PolylineColorAppearance | Per-instance color polylines | No |
PolylineMaterialAppearance | Polylines with Material | Yes |
DebugAppearance | Visualize vertex attributes | No |
Appearance | Base class / custom shaders | Optional |
The geometry vertexFormat must match the appearance. Use the appearance's static VERTEX_FORMAT. For PerInstanceColorAppearance without lighting, use FLAT_VERTEX_FORMAT.
import { Primitive, GeometryInstance, WallGeometry, MaterialAppearance, Material, Cartesian3 } from "cesium";
scene.primitives.add(new Primitive({
geometryInstances: new GeometryInstance({
geometry: new WallGeometry({
positions: Cartesian3.fromDegreesArrayHeights([-115, 44, 200000, -110, 44, 200000, -105, 44, 200000]),
vertexFormat: MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat,
}),
}),
appearance: new MaterialAppearance({
material: Material.fromType("Checkerboard"),
faceForward: true, // shade both sides
}),
}));
Drapes geometry onto terrain/3D Tiles. Supported: CircleGeometry, CorridorGeometry, EllipseGeometry, PolygonGeometry, RectangleGeometry.
import { GroundPrimitive, GeometryInstance, PolygonGeometry, PolygonHierarchy,
ColorGeometryInstanceAttribute, ClassificationType, Cartesian3, Color } from "cesium";
scene.groundPrimitives.add(new GroundPrimitive({
geometryInstances: new GeometryInstance({
geometry: new PolygonGeometry({
polygonHierarchy: new PolygonHierarchy(
Cartesian3.fromDegreesArray([-112, 36, -112, 36.1, -111.9, 36.1]),
),
}),
id: "groundPolygon",
attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.RED.withAlpha(0.5)) },
}),
classificationType: ClassificationType.TERRAIN, // TERRAIN, CESIUM_3D_TILE, or BOTH
}));
import { GroundPolylinePrimitive, GeometryInstance, GroundPolylineGeometry,
PolylineColorAppearance, ColorGeometryInstanceAttribute, Cartesian3, Color } from "cesium";
scene.groundPrimitives.add(new GroundPolylinePrimitive({
geometryInstances: new GeometryInstance({
geometry: new GroundPolylineGeometry({
positions: Cartesian3.fromDegreesArray([-112.13, 36.05, -112.09, 36.10, -112.13, 36.17]),
width: 4.0,
loop: true,
}),
attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.LIME.withAlpha(0.7)) },
}),
appearance: new PolylineColorAppearance(),
}));
Highlights volumes classifying terrain or 3D Tiles. Valid: BoxGeometry, CylinderGeometry, EllipsoidGeometry, PolylineVolumeGeometry, SphereGeometry, plus extruded surface geometries.
import { ClassificationPrimitive, GeometryInstance, BoxGeometry, PerInstanceColorAppearance,
ColorGeometryInstanceAttribute, ClassificationType, Cartesian3, Transforms, Color } from "cesium";
scene.primitives.add(new ClassificationPrimitive({
geometryInstances: new GeometryInstance({
geometry: BoxGeometry.fromDimensions({
dimensions: new Cartesian3(100, 100, 50),
vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
}),
modelMatrix: Transforms.eastNorthUpToFixedFrame(Cartesian3.fromDegrees(-75.59, 40.04, 25)),
attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.YELLOW.withAlpha(0.5)) },
}),
classificationType: ClassificationType.BOTH,
}));
GPU-efficient viewport-aligned images -- far more performant than entities at scale.
Breaking change (1.140, #13253):
BillboardCollectionandLabelCollectionnow require WebGL 2, or WebGL 1 withANGLE_instanced_arraysandMAX_VERTEX_TEXTURE_IMAGE_UNITS > 0. On unsupported devices they no longer render -- gate onscene.context.webgl2(or feature-detect the extension) if you still target legacy WebGL 1 hardware.
Compatibility fix (1.143): billboard image loading no longer crashes when an application replaces the global
Promiseimplementation. Use the publicimageproperty with a URL, loaded image, or canvas; usesetImagefor aResourceor callback. Do not branch on nativePromiseidentity or retain compatibility shims for this bug.
import { BillboardCollection, Cartesian3, Color, NearFarScalar,
HeightReference, HorizontalOrigin, VerticalOrigin } from "cesium";
const billboards = scene.primitives.add(new BillboardCollection({ scene }));
const b = billboards.add({
position: Cartesian3.fromDegrees(-75.59, 40.04),
image: "marker.png",
horizontalOrigin: HorizontalOrigin.CENTER,
verticalOrigin: VerticalOrigin.BOTTOM,
heightReference: HeightReference.CLAMP_TO_GROUND,
scaleByDistance: new NearFarScalar(1000, 1.5, 1e7, 0.3),
});
b.position = Cartesian3.fromDegrees(-75.60, 40.05); // update dynamically
billboards.remove(b);
import { LabelCollection, Cartesian3, Cartesian2, Color, LabelStyle, VerticalOrigin } from "cesium";
const labels = scene.primitives.add(new LabelCollection({ scene }));
labels.add({
position: Cartesian3.fromDegrees(-75.59, 40.04, 300),
text: "Philadelphia",
font: "16px sans-serif",
fillColor: Color.WHITE,
outlineColor: Color.BLACK,
outlineWidth: 2,
style: LabelStyle.FILL_AND_OUTLINE,
verticalOrigin: VerticalOrigin.BOTTOM,
pixelOffset: new Cartesian2(0, -10),
});
import { PointPrimitiveCollection, Cartesian3, Color, NearFarScalar } from "cesium";
const points = scene.primitives.add(new PointPrimitiveCollection());
points.add({
position: Cartesian3.fromDegrees(-75.59, 40.04),
pixelSize: 10,
color: Color.YELLOW,
outlineColor: Color.BLACK,
outlineWidth: 2,
scaleByDistance: new NearFarScalar(1000, 1.0, 1e7, 0.1),
});
import { CloudCollection, PolylineCollection, Cartesian3, Cartesian2, Color, Material } from "cesium";
// Procedural cumulus clouds
const clouds = scene.primitives.add(new CloudCollection());
clouds.add({
position: Cartesian3.fromDegrees(-75.59, 40.04, 1500),
scale: new Cartesian2(40, 12),
maximumSize: new Cartesian3(40, 12, 15),
slice: 0.36,
});
// Low-level polyline collection
const polylines = scene.primitives.add(new PolylineCollection());
polylines.add({
positions: Cartesian3.fromDegreesArray([-75, 40, -70, 42, -65, 38]),
width: 3.0,
material: Material.fromType("Color", { color: Color.AQUA }),
});
import { Primitive, GeometryInstance, PolylineGeometry, PolylineColorAppearance,
ColorGeometryInstanceAttribute, Cartesian3, Color, ArcType } from "cesium";
scene.primitives.add(new Primitive({
geometryInstances: new GeometryInstance({
geometry: new PolylineGeometry({
positions: Cartesian3.fromDegreesArray([0, 0, 5, 0]),
width: 10.0,
vertexFormat: PolylineColorAppearance.VERTEX_FORMAT,
arcType: ArcType.GEODESIC, // GEODESIC, RHUMB, or NONE
}),
attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.WHITE) },
}),
appearance: new PolylineColorAppearance({ translucent: false }),
}));
| Enum | Values | Used By |
|---|---|---|
ArcType | GEODESIC, RHUMB, NONE | PolylineGeometry, PolygonGeometry |
CornerType | ROUNDED, MITERED, BEVELED | CorridorGeometry, PolylineVolumeGeometry |
ClassificationType | TERRAIN, CESIUM_3D_TILE, BOTH | GroundPrimitive, ClassificationPrimitive |
PrimitiveType | POINTS, LINES, TRIANGLES, etc. | Low-level Geometry |
CloudType | CUMULUS | CloudCollection |
PerInstanceColorAppearance when each instance only needs a distinct color.flat: true on PerInstanceColorAppearance when lighting is unneeded; uses FLAT_VERTEX_FORMAT.allowPicking: false on Primitives that will never be picked to save GPU memory.asynchronous: true (default). Check primitive.ready before accessing instance attributes.BlendOption.OPAQUE on BillboardCollection/PointPrimitiveCollection when all items are opaque (up to 2x gain).heightReference.vertexFormat exactly to the appearance to skip unused vertex attribute computation.EllipsoidSurfaceAppearance over MaterialAppearance for surface geometry -- fewer vertex attributes.MVTDataProvider for tiled vector data as runtime 3D Tiles.Frequently asked questions
Applies to: CesiumJS v1.143+ (ES module imports, ?? instead of defaultValue)
The source record exposes this install command: npx skills add https://github.com/CesiumGS/cesiumjs-skills --skill "skills/cesiumjs-primitives". Inspect the command and pinned source before running it.