cesiumgs/cesiumjs-viewer-setup
CesiumJS viewer setup - Viewer, CesiumWidget, widgets, Ion token, Scene configuration, SceneMode, factory helpers, geocoders, platform services. Use when initializing a CesiumJS application, configuring viewer widgets, setting Ion access tokens, creating default terrain or imagery, or bootstrapping a 3D globe.
npx skills add https://github.com/CesiumGS/cesiumjs-skills --skill cesiumjs-viewer-setup
Reference for bootstrapping CesiumJS applications: Viewer, CesiumWidget, Ion/GoogleMaps/ITwinPlatform configuration, widgets, factory helpers, geocoder services, viewer mixins, Credits, and related enums.
import { Ion, Viewer, Terrain } from "cesium";
import "cesium/Build/Cesium/Widgets/widgets.css";
// Always set your Ion token before any other Cesium calls
Ion.defaultAccessToken = "YOUR_CESIUM_ION_ACCESS_TOKEN";
const viewer = new Viewer("cesiumContainer", {
terrain: Terrain.fromWorldTerrain(),
});
Required HTML: <div id="cesiumContainer" style="width:100%;height:100vh"></div>
import { Ion } from "cesium";
Ion.defaultAccessToken = "YOUR_TOKEN"; // required for ion assets
Ion.defaultServer = "https://your-ion-server.example.com/"; // optional: self-hosted
import { IonResource, Cesium3DTileset } from "cesium";
const resource = await IonResource.fromAssetId(96188);
const tileset = await Cesium3DTileset.fromUrl(resource);
viewer.scene.primitives.add(tileset);
import { GoogleMaps, createGooglePhotorealistic3DTileset, Viewer, IonGeocodeProviderType } from "cesium";
GoogleMaps.defaultApiKey = "YOUR_GOOGLE_MAPS_API_KEY"; // optional: without key, served via ion
const viewer = new Viewer("cesiumContainer", {
geocoder: IonGeocodeProviderType.GOOGLE, // required with Google 3D Tiles
});
const tileset = await createGooglePhotorealistic3DTileset({
onlyUsingWithGoogleGeocoder: true,
});
viewer.scene.primitives.add(tileset);
import { ITwinPlatform, ITwinData } from "cesium";
ITwinPlatform.defaultAccessToken = "YOUR_ITWIN_TOKEN";
const tileset = await ITwinData.createTilesetForIModel(viewer, "imodel-id");
// 1.140+ (#13208): Reality Data of type GaussianSplat3DTiles is now supported
const splats = await ITwinData.createTilesetForRealityDataId(
iTwinId,
realityDataId,
ITwinPlatform.RealityDataType.GaussianSplat3DTiles,
);
viewer.scene.primitives.add(splats);
new Viewer(container, options?) -- container is a DOM element or its string ID.
| Option | Default | Purpose |
|--------|---------|---------|
| animation | true | Playback controls |
| baseLayerPicker | true | Imagery/terrain switcher |
| fullscreenButton | true | Fullscreen toggle |
| vrButton | false | WebVR toggle |
| geocoder | IonGeocodeProviderType.DEFAULT | Search bar (false to hide) |
| homeButton | true | Reset to home view |
| infoBox | true | Entity info popup |
| sceneModePicker | true | 2D/3D/Columbus toggle |
| selectionIndicator | true | Selection reticle |
| timeline | true | Time scrubber |
| navigationHelpButton | true | Mouse/touch help |
| projectionPicker | false | Perspective/ortho toggle |
| Option | Default | Purpose |
|--------|---------|---------|
| sceneMode | SceneMode.SCENE3D | Initial scene mode |
| scene3DOnly | false | Lock to 3D, saves GPU memory |
| shadows | false | Shadow casting |
| terrainShadows | ShadowMode.RECEIVE_ONLY | Terrain shadow mode |
| requestRenderMode | false | Render only on changes |
| maximumRenderTimeChange | 0.0 | Max sim-time delta for render |
| msaaSamples | 4 | MSAA (1 to disable) |
| orderIndependentTranslucency | true | Translucent ordering |
| mapMode2D | MapMode2D.INFINITE_SCROLL | 2D scroll behavior |
| Option | Default | Purpose |
|--------|---------|---------|
| baseLayer | ImageryLayer.fromWorldImagery() | Base imagery (false for none; needs baseLayerPicker: false) |
| terrain | none | Async terrain helper (cannot combine with terrainProvider) |
| terrainProvider | EllipsoidTerrainProvider | Sync terrain provider |
| globe | new Globe() | false for no globe (space scenes) |
| skyBox | auto (WGS84) | false disables sky/sun/moon |
| skyAtmosphere | auto (WGS84) | false disables limb glow |
import { Viewer, Ion, Terrain } from "cesium";
Ion.defaultAccessToken = "YOUR_TOKEN";
const viewer = new Viewer("cesiumContainer", {
animation: false, baseLayerPicker: false, fullscreenButton: false,
geocoder: false, homeButton: false, infoBox: false,
sceneModePicker: false, selectionIndicator: false,
timeline: false, navigationHelpButton: false,
terrain: Terrain.fromWorldTerrain(),
});
No UI widgets, no Knockout dependency. Suitable for custom UIs or embedding.
import { CesiumWidget, Ion } from "cesium";
Ion.defaultAccessToken = "YOUR_TOKEN";
const widget = new CesiumWidget("cesiumContainer", { shouldAnimate: true });
// Exposes: widget.scene, widget.camera, widget.entities
| Value | Description |
|-------|-------------|
| SceneMode.SCENE3D | Standard 3D globe (default) |
| SceneMode.SCENE2D | Top-down orthographic map |
| SceneMode.COLUMBUS_VIEW | 2.5D flat map with height |
| SceneMode.MORPHING | Transitioning between modes |
import { Viewer, SceneMode } from "cesium";
const viewer = new Viewer("cesiumContainer", { sceneMode: SceneMode.SCENE2D });
viewer.scene.morphTo3D(2.0); // animated transition
viewer.scene.morphToColumbusView(2.0);
const scene = viewer.scene;
scene.globe.depthTestAgainstTerrain = true; // entities interact with terrain
scene.globe.enableLighting = true; // sun-based lighting
// Key sub-objects
scene.camera; // Camera
scene.primitives; // PrimitiveCollection
scene.groundPrimitives; // PrimitiveCollection (ground-clamped)
scene.imageryLayers; // ImageryLayerCollection
scene.postProcessStages;
scene.requestRender(); // trigger frame in requestRenderMode
import { createOsmBuildingsAsync, Cesium3DTileStyle } from "cesium";
// Default styling (colors from OSM tags)
const tileset = await createOsmBuildingsAsync();
viewer.scene.primitives.add(tileset);
// Custom style
const styled = await createOsmBuildingsAsync({
style: new Cesium3DTileStyle({
color: { conditions: [
["${feature['building']} === 'hospital'", "color('#0000FF')"],
[true, "color('#ffffff')"],
]},
}),
});
import { createGooglePhotorealistic3DTileset, IonGeocodeProviderType } from "cesium";
// Must use Google geocoder
const viewer = new Viewer("cesiumContainer", { geocoder: IonGeocodeProviderType.GOOGLE });
const tileset = await createGooglePhotorealistic3DTileset({ onlyUsingWithGoogleGeocoder: true });
viewer.scene.primitives.add(tileset);
Preferred for the terrain constructor option. Non-blocking with error events.
import { Viewer, Terrain } from "cesium";
// World terrain with normals and water
const viewer = new Viewer("cesiumContainer", {
terrain: Terrain.fromWorldTerrain({ requestVertexNormals: true, requestWaterMask: true }),
});
// Bathymetry (ocean floor)
const viewer2 = new Viewer("cesiumContainer", {
terrain: Terrain.fromWorldBathymetry({ requestVertexNormals: true }),
});
import { Terrain, CesiumTerrainProvider } from "cesium";
const terrain = new Terrain(CesiumTerrainProvider.fromUrl("https://my-terrain.example.com"));
viewer.scene.setTerrain(terrain);
terrain.readyEvent.addEventListener((provider) => {
viewer.scene.globe.enableLighting = true;
});
terrain.errorEvent.addEventListener((error) => console.error("Terrain failed:", error));
Lower-level: return raw providers. Use when you need the provider directly.
import { createWorldTerrainAsync, createWorldImageryAsync, IonWorldImageryStyle } from "cesium";
const terrainProvider = await createWorldTerrainAsync({ requestVertexNormals: true });
viewer.terrainProvider = terrainProvider;
const imageryProvider = await createWorldImageryAsync({ style: IonWorldImageryStyle.AERIAL_WITH_LABELS });
IonWorldImageryStyle: AERIAL (default) | AERIAL_WITH_LABELS | ROAD
The geocoder option accepts false, an IonGeocodeProviderType, or a GeocoderService[].
IonGeocodeProviderType: DEFAULT | GOOGLE (required with Google tiles) | BING
import { Viewer, CartographicGeocoderService, IonGeocoderService, OpenCageGeocoderService } from "cesium";
// Multiple services (searched in order)
const viewer = new Viewer("cesiumContainer", {
geocoder: [
new CartographicGeocoderService(), // accepts "lat, lon" input
new IonGeocoderService({ scene: viewer.scene }),
],
});
const myGeocoder = {
async geocode(input, type) {
// type: GeocodeType.SEARCH or GeocodeType.AUTOCOMPLETE
const resp = await fetch(`https://api.example.com/search?q=${input}`);
const data = await resp.json();
return data.map((item) => ({
displayName: item.name,
destination: Cartesian3.fromDegrees(item.lon, item.lat),
}));
},
};
const viewer = new Viewer("cesiumContainer", { geocoder: [myGeocoder] });
import { Viewer, viewerDragDropMixin, viewerCesium3DTilesInspectorMixin,
viewerCesiumInspectorMixin, viewerPerformanceWatchdogMixin, viewerVoxelInspectorMixin } from "cesium";
const viewer = new Viewer("cesiumContainer");
// Drag-and-drop CZML/GeoJSON/KML loading
viewer.extend(viewerDragDropMixin, { dropTarget: "cesiumContainer", clearOnDrop: true });
viewer.dropError.addEventListener((handler, name, error) => console.error(error));
viewer.extend(viewerCesium3DTilesInspectorMixin); // 3D Tiles debug panel
viewer.extend(viewerCesiumInspectorMixin); // general scene inspector
viewer.extend(viewerPerformanceWatchdogMixin); // low-FPS warning
viewer.extend(viewerVoxelInspectorMixin); // voxel debug panel
| Property | Type |
|----------|------|
| viewer.scene | Scene |
| viewer.camera | Camera |
| viewer.entities | EntityCollection |
| viewer.dataSources | DataSourceCollection |
| viewer.imageryLayers | ImageryLayerCollection |
| viewer.terrainProvider | TerrainProvider |
| viewer.clock / clockViewModel | Clock / ClockViewModel |
| viewer.canvas | HTMLCanvasElement |
| viewer.screenSpaceEventHandler | ScreenSpaceEventHandler |
| viewer.selectedEntity / trackedEntity | Entity |
| viewer.shadows | boolean |
| viewer.resolutionScale | number (default 1.0) |
await viewer.flyTo(entity, { duration: 3.0, offset: headingPitchRange }); // animated
await viewer.zoomTo(tileset); // instant
viewer.destroy(); // free all resources
import { Credit, FrameRateMonitor } from "cesium";
// Custom credit (showOnScreen = true)
viewer.creditDisplay.addStaticCredit(new Credit("Data by Example Corp", true));
// Monitor frame rate
const monitor = FrameRateMonitor.fromScene(viewer.scene);
monitor.lowFrameRate.addEventListener(() => console.warn("Low FPS"));
monitor.nominalFrameRate.addEventListener(() => console.log("FPS recovered"));
import { Ion, Viewer, Terrain, createOsmBuildingsAsync, Cartesian3, Math as CesiumMath } from "cesium";
Ion.defaultAccessToken = "YOUR_TOKEN";
const viewer = new Viewer("cesiumContainer", {
terrain: Terrain.fromWorldTerrain(), animation: false, timeline: false,
});
viewer.scene.primitives.add(await createOsmBuildingsAsync());
viewer.scene.camera.flyTo({
destination: Cartesian3.fromDegrees(-74.019, 40.6912, 750),
orientation: { heading: CesiumMath.toRadians(20), pitch: CesiumMath.toRadians(-20) },
});
const viewer = new Viewer("cesiumContainer", {
globe: false, skyAtmosphere: false, baseLayerPicker: false,
});
const viewer = new Viewer("cesiumContainer", {
requestRenderMode: true, maximumRenderTimeChange: Infinity,
});
// Call viewer.scene.requestRender() after programmatic changes
import { Viewer, ImageryLayer, OpenStreetMapImageryProvider } from "cesium";
const viewer = new Viewer("cesiumContainer", {
baseLayerPicker: false,
baseLayer: new ImageryLayer(new OpenStreetMapImageryProvider({
url: "https://tile.openstreetmap.org/",
})),
});
import { Viewer, SceneMode, WebMercatorProjection } from "cesium";
const viewer = new Viewer("cesiumContainer", {
sceneMode: SceneMode.COLUMBUS_VIEW, mapProjection: new WebMercatorProjection(),
});
requestRenderMode: true for mostly-static apps. Reduces CPU/GPU and battery drain. Call scene.requestRender() after changes.scene3DOnly: true when 2D/Columbus View is not needed. Saves GPU memory per geometry instance.animation: false, timeline: false) to reduce DOM overhead.msaaSamples: 1 on low-power devices. Default 4 balances quality.resolutionScale (e.g., 0.75) on HiDPI displays for better frame rates.Terrain.fromWorldTerrain() over await createWorldTerrainAsync() -- non-blocking with error events.requestVertexNormals: true on terrain for proper lighting at negligible cost.viewer.destroy() when removing from DOM to free WebGL contexts.Take cesiumgs/cesiumjs-viewer-setup from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.