skillZs
★ LIVE SKILL TAGS ★
>>> LIVE SKILLS INDEX <<<
* OPEN SOURCE *
NO LOGIN, NO TRACKING
※ REAL INSTALL DATA ※
← back to all skills
cesiumgs/cesiumjs-skills207 installs

cesiumjs-primitives

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.

How do I install this agent skill?

npx skills add https://github.com/cesiumgs/cesiumjs-skills --skill cesiumjs-primitives
view source ↗

Is this agent skill safe to install?

  • Gen Agent Trust Hubpass

    The skill provides documentation and code examples for using CesiumJS primitives, geometry, and collections for high-performance 3D rendering. No security issues or malicious patterns were detected.

  • Socketpass

    No alerts

  • Snykpass

    Risk: LOW · No issues

What does this agent skill do?

CesiumJS Primitives & Geometry

Applies to: CesiumJS v1.144+ (ES module imports, ?? instead of defaultValue)

Architecture

The Primitive API is the low-level rendering layer beneath the Entity API, trading convenience for performance.

Core formula: Primitive = GeometryInstance[] + Appearance

  • GeometryInstance -- positions a Geometry in world space with per-instance attributes (color, show).
  • Geometry -- vertex data describing a shape (polygon, box, ellipsoid, etc.).
  • Appearance -- GLSL shaders + render state + optional Material that shade the geometry.

Primitives are immutable after first render -- geometry cannot change, but per-instance attributes update via primitive.getGeometryInstanceAttributes(id).

Primitive

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") }),
}));

Key Options

OptionDefaultPurpose
geometryInstances--Single instance or array
appearance--Shading (Appearance subclass)
showtrueToggle visibility
modelMatrixMatrix4.IDENTITYTransform all instances
asynchronoustrueBuild geometry on web worker
releaseGeometryInstancestrueFree geometry after GPU upload
allowPickingtruefalse saves GPU memory
shadowsShadowMode.DISABLEDCast/receive shadows

Batching Multiple Instances

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(),
}));

Batching Volume Geometry (CylinderGeometry Grid)

Volume geometry (Cylinder, Box, Ellipsoid) must be positioned via modelMatrix on each GeometryInstance. Use Matrix4.multiply to combine a world-space anchor with a local offset, then batch all instances into one Primitive.

import {
  Primitive, GeometryInstance, CylinderGeometry,
  PerInstanceColorAppearance, ColorGeometryInstanceAttribute,
  Cartesian3, Matrix4, Transforms, Color, Math as CesiumMath,
} from "cesium";

const center = Cartesian3.fromDegrees(-73.9857, 40.7580);
const anchorFrame = Transforms.eastNorthUpToFixedFrame(center, undefined, new Matrix4());
const instances = [];
const GRID = 10;
const SPACING = 50; // metres

for (let row = 0; row < GRID; row++) {
  for (let col = 0; col < GRID; col++) {
    const xOffset = (col - GRID / 2) * SPACING;
    const yOffset = (row - GRID / 2) * SPACING;
    // Combine anchor ENU frame with a local XYZ offset
    const modelMatrix = Matrix4.multiply(
      anchorFrame,
      Matrix4.fromTranslation(new Cartesian3(xOffset, yOffset, 100), new Matrix4()),
      new Matrix4(),
    );
    instances.push(new GeometryInstance({
      geometry: new CylinderGeometry({
        length: 200,
        topRadius: 8,
        bottomRadius: 8,
        vertexFormat: PerInstanceColorAppearance.VERTEX_FORMAT,
      }),
      modelMatrix,
      attributes: { color: ColorGeometryInstanceAttribute.fromColor(Color.fromRandom({ alpha: 1.0 })) },
    }));
  }
}

scene.primitives.add(new Primitive({
  geometryInstances: instances,
  appearance: new PerInstanceColorAppearance({ flat: true }),
}));

// Frame the grid so the full batch is visible -- a shallow pitch hides cylinders
// behind the foreground; a near-nadir pitch flattens them. Aim for ~-45° (-PI/4)
// at a range that covers the grid footprint (GRID * SPACING) with margin.
const range = GRID * SPACING * 3; // ~1500 m for a 10x10x50m grid
viewer.camera.lookAt(
  center,
  new Cartesian3(0, -range * 0.7, range * 0.7), // offset south + up for -45° pitch
);

Key patterns:

  • Matrix4.multiply(anchorFrame, Matrix4.fromTranslation(offset, result), result) -- compose the ENU frame at a geographic anchor with a local East/North/Up translation.
  • Color.fromRandom({ alpha: 1.0 }) produces fully-opaque random colours suitable for rainbow-coloured batches.
  • Framing matters: for a grid of vertical volumes, prefer a ~-45° (-CesiumMath.PI_OVER_FOUR, ~-0.785 rad) pitch at a range of roughly 3 * gridFootprint. Pitches shallower than ~-0.6 rad can push the grid off-screen or hide it behind buildings; nadir views flatten cylinders into dots and lose the batched "field" appearance. A -0.61 rad pitch is still shallow enough to leave the grid completely out of frame.
  • Verify visibility: after primitive.ready, prefer viewer.camera.flyToBoundingSphere(primitive._boundingSpheres[0], { duration: 0 }) (or viewer.flyTo(primitive)) over hand-tuned offsets if the scenario only requires "the batch is visible".

Updating Per-Instance Attributes

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();
});

PrimitiveCollection

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

Choosing a Vector Data Path

NeedUse
Entity lifecycle, clustering, per-entity styling, time-dynamic valuesGeoJsonDataSource in cesiumjs-entities
One large GeoJSON object with low overhead and primitive-level performanceGeoJsonPrimitive in this skill
Tiled vector data, 3D Tiles LOD, metadata styling, feature pickingMVTDataProvider in cesiumjs-3d-tiles
Fully manual high-throughput point/polyline/polygon buffersBufferPointCollection, BufferPolylineCollection, BufferPolygonCollection

Buffer Primitive Collections (Experimental, 1.140+)

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, and boundingVolumeWC are 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.
  • Providing 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.
  • Use 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 (Experimental, 1.142+)

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().

Built-in Geometry Types (31)

All geometries take shape parameters and a vertexFormat matching the Appearance. Most have a paired *OutlineGeometry. Outlines require a separate Primitive.

Filled + Outline Pattern

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 }),
}));

Geometry Catalog

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).

Positioning Off-Surface Geometry

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) },
});

Appearances (7 Types)

AppearanceUse CaseMaterial?
PerInstanceColorAppearancePer-instance colorNo
MaterialAppearanceArbitrary geometry + MaterialYes
EllipsoidSurfaceAppearanceSurface geometry + Material (fewer attrs)Yes
PolylineColorAppearancePer-instance color polylinesNo
PolylineMaterialAppearancePolylines with MaterialYes
DebugAppearanceVisualize vertex attributesNo
AppearanceBase class / custom shadersOptional

The geometry vertexFormat must match the appearance. Use the appearance's static VERTEX_FORMAT. For PerInstanceColorAppearance without lighting, use FLAT_VERTEX_FORMAT.

MaterialAppearance Example

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
  }),
}));

GroundPrimitive

Drapes geometry onto terrain/3D Tiles. Supported: CircleGeometry, CorridorGeometry, EllipseGeometry, PolygonGeometry, RectangleGeometry. Add to scene.primitives (not scene.groundPrimitives -- both work but scene.primitives is the conventional target when using a public basemap without Ion terrain).

import { GroundPrimitive, GeometryInstance, PolygonGeometry, PolygonHierarchy,
  PerInstanceColorAppearance, ColorGeometryInstanceAttribute, ClassificationType,
  Cartesian3, Color } from "cesium";

scene.primitives.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)) },
  }),
  appearance: new PerInstanceColorAppearance({ flat: true, translucent: true }),
  classificationType: ClassificationType.TERRAIN, // TERRAIN, CESIUM_3D_TILE, or BOTH
}));

GroundPolylinePrimitive

Drapes a polyline on terrain. Add to scene.primitives (not scene.groundPrimitives).

import { GroundPolylinePrimitive, GeometryInstance, GroundPolylineGeometry,
  PolylineColorAppearance, ColorGeometryInstanceAttribute, Cartesian3, Color } from "cesium";

scene.primitives.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(),
}));

ClassificationPrimitive

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,
}));

BillboardCollection

GPU-efficient viewport-aligned images -- far more performant than entities at scale.

Breaking change (1.140, #13253): BillboardCollection and LabelCollection now require WebGL 2, or WebGL 1 with ANGLE_instanced_arrays and MAX_VERTEX_TEXTURE_IMAGE_UNITS > 0. On unsupported devices they no longer render -- gate on scene.context.webgl2 (or feature-detect the extension) if you still target legacy WebGL 1 hardware.

Basic Usage

Compatibility fix (1.143): billboard image loading no longer crashes when an application replaces the global Promise implementation. Use the public image property with a URL, loaded image, or canvas; use setImage for a Resource or callback. Do not branch on native Promise identity 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);

PinBuilder -- Procedural Pin Images

PinBuilder generates canvas-based pin icons at runtime without external image files. Use fromColor for solid-colour pins or fromText for labelled pins. Pass the returned canvas as the billboard image.

When a scenario specifies an ordered list of cities/items and a colour-by-index scheme, iterate the source array in the given order and use the loop index directly as the HSL hue index. Re-ordering the source list (e.g. sorting by latitude) swaps which colour lands on which city and fails visual checks.

import { BillboardCollection, PinBuilder, Cartesian3, Color, VerticalOrigin } from "cesium";

const pinBuilder = new PinBuilder();
const cities = [
  { name: "Boston",       lng: -71.0589, lat: 42.3601 },
  { name: "New York",     lng: -74.0060, lat: 40.7128 },
  { name: "Philadelphia", lng: -75.1652, lat: 39.9526 },
  { name: "Washington DC",lng: -77.0369, lat: 38.9072 },
  { name: "Miami",        lng: -80.1918, lat: 25.7617 },
];

const billboards = scene.primitives.add(new BillboardCollection({ scene }));

cities.forEach((city, index) => {
  billboards.add({
    position: Cartesian3.fromDegrees(city.lng, city.lat),
    // Color.fromHsl(hue 0-1, saturation, lightness) produces evenly-spaced hues
    image: pinBuilder.fromColor(Color.fromHsl(index / cities.length, 0.8, 0.5), 48),
    verticalOrigin: VerticalOrigin.BOTTOM,
  });
});

// Text label pin: pinBuilder.fromText("A", Color.ROYALBLUE, 48)
// fromColor / fromText return a canvas -- pass directly as image

Color.fromHsl(hue, saturation, lightness) -- generates colours across the spectrum by varying hue (0–1 wraps full circle). Useful for rainbow-colouring N items: Color.fromHsl(i / n, 0.8, 0.5). Color.fromRandom({ alpha }) -- random hue/saturation/lightness with fixed alpha.

LabelCollection

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),
});

PointPrimitiveCollection

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),
});

CloudCollection and PolylineCollection

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 }),
});

Polyline via Primitive

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 }),
}));

Enums

EnumValuesUsed By
ArcTypeGEODESIC, RHUMB, NONEPolylineGeometry, PolygonGeometry
CornerTypeROUNDED, MITERED, BEVELEDCorridorGeometry, PolylineVolumeGeometry
ClassificationTypeTERRAIN, CESIUM_3D_TILE, BOTHGroundPrimitive, ClassificationPrimitive
PrimitiveTypePOINTS, LINES, TRIANGLES, etc.Low-level Geometry
CloudTypeCUMULUSCloudCollection

Camera Framing for Primitive Scenes

Programmatic checks (primitive count, camera position) can pass while the visual output completely misses the rendered geometry. A few rules of thumb that recur in visual evaluations:

  • Volume-geometry grids (cylinders/boxes): use -PI/4 (-0.785 rad, -45°) pitch from a range of ~3 * gridFootprint. Pitches shallower than ~-0.6 rad can park the grid behind the camera or out of frame; pitches near -PI/2 flatten vertical extent and make the batch look like dots.
  • Surface polygons draped on terrain (GroundPrimitive): with steeper pitches (~-50°) and far ranges, the polygon shrinks to a thin strip at the bottom edge. Reduce range or use a flatter pitch so the polygon occupies the centre of the frame.
  • Continental polylines (Route 66, transcontinental routes): a near-nadir top-down view from ~5–6 Mm above the centroid keeps the full path visible.
  • Pin chains along a coast/route: centre the camera on the midpoint of the chain at a range that covers the chain's bounding box with margin on all sides.

When in doubt, viewer.flyTo(primitive) or viewer.camera.flyToBoundingSphere(primitive._boundingSpheres[0]) after primitive.ready will frame the batch automatically.

Performance Tips

  1. Batch aggressively. Combine thousands of GeometryInstances into one Primitive for a single draw call.
  2. Use PerInstanceColorAppearance when each instance only needs a distinct color.
  3. Set flat: true on PerInstanceColorAppearance when lighting is unneeded; uses FLAT_VERTEX_FORMAT.
  4. Set allowPicking: false on Primitives that will never be picked to save GPU memory.
  5. Keep asynchronous: true (default). Check primitive.ready before accessing instance attributes.
  6. Prefer fewer large collections for Billboard, Label, and PointPrimitive. Group by update frequency.
  7. Use BlendOption.OPAQUE on BillboardCollection/PointPrimitiveCollection when all items are opaque (up to 2x gain).
  8. Use buffer primitive collections for large vector data when flyweight updates are acceptable.
  9. Precompute buffer collection bounding volumes for large animated collections, but remember they are world-space in 1.142+.
  10. Use GroundPrimitive for terrain draping instead of entity heightReference.
  11. Separate fill and outline into two Primitives -- they cannot share a draw call.
  12. Match vertexFormat exactly to the appearance to skip unused vertex attribute computation.
  13. Use EllipsoidSurfaceAppearance over MaterialAppearance for surface geometry -- fewer vertex attributes.

See Also

  • cesiumjs-entities -- High-level Entity API wrapping primitives with time-dynamic properties.
  • cesiumjs-3d-tiles -- Use MVTDataProvider for tiled vector data as runtime 3D Tiles.
  • cesiumjs-materials-shaders -- Material (Fabric) system consumed by Appearances, post-processing.
  • cesiumjs-spatial-math -- Cartesian3, Matrix4, Transforms, coordinate conversions for positioning geometry.

Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.

<a href="https://skillzs.dev/skills/cesiumgs/cesiumjs-skills/cesiumjs-primitives">View cesiumjs-primitives on skillZs</a>