Recently I noticed a new terrain dataset on Cesium ion: Cesium World Bathymetry. Previously, when developing 3D globe applications with Cesium, the default terrain data was Cesium World Terrain. So, what is the difference between them? I wrote a demo and found that Cesium World Bathymetry focuses more on seafloor topography, such as trenches, coral reefs, and nearshore depth variations. Today I will give an introduction.
What is Cesium World Bathymetry
Cesium World Bathymetry is a global-coverage bathymetry and terrain tileset in quantized-mesh 1.0 format, which integrates multi-source bathymetry and nearshore terrain into a single global basemap. The primary source is GEBCO 2023, overlaid with higher-resolution local bathymetry products and supplemented with inland terrain.
The official use cases include oil & gas exploration, unmanned underwater vehicle navigation, offshore wind farm management, climate-related analysis, and more. For GIS professionals, common applications are 3D coastal zone visualization, seafloor geomorphology education, and overlaying custom DEMs on this global basemap for comparison.
Basic Data Resolution:
The approximate resolutions provided by the official source are as follows:
- Swiss lakes: about 1 m, 2 m, 3 m
- U.S. East Coast, U.S. island territories, and parts of Alaska, Oregon, California: about 3 m, 10 m
- Northern Gulf of Mexico: about 12 m
- Great Barrier Reef: about 30 m
- U.S. West Coast: about 90 m
- The rest of the world: about 450 m
CesiumJS Quick Start Demo
Log in to Cesium ion, and add Cesium World Bathymetry to My Assets. The Asset ID is 2426648. Newer versions of CesiumJS can also use Cesium.Terrain.fromWorldBathymetry() directly, without manually specifying the ID.
In this example, I made three main modifications: first, loading World Bathymetry and requesting vertex normals; second, disabling fog and ground atmosphere and enabling directional lighting to make the seafloor relief clearer; third, applying an elevation color ramp with contour lines, setting vertical exaggeration to 3x, and flying the camera to the western coast of Taiwan. Replace YOUR_ION_ACCESS_TOKEN with your own token to run it. The result looks like this:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Cesium World Bathymetry Demo</title>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.142/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.142/Build/Cesium/Widgets/widgets.css" rel="stylesheet" />
<style>
html, body, #cesiumContainer { width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; }
</style>
</head>
<body>
<div id="cesiumContainer"></div>
<script>
Cesium.Ion.defaultAccessToken = "YOUR_ION_ACCESS_TOKEN";
const viewer = new Cesium.Viewer("cesiumContainer", {
terrain: Cesium.Terrain.fromWorldBathymetry({
requestVertexNormals: true,
}),
timeline: false,
animation: false,
baseLayerPicker: false,
geocoder: false,
});
const scene = viewer.scene;
const globe = scene.globe;
const camera = scene.camera;
// When visualizing bathymetry, fog and ground atmosphere tend to wash out the relief.
scene.fog.enabled = false;
globe.showGroundAtmosphere = false;
globe.enableLighting = true;
globe.maximumScreenSpaceError = 1.0;
scene.verticalExaggeration = 3.0;
scene.light = new Cesium.DirectionalLight({
direction: new Cesium.Cartesian3(1, 0, 0),
});
const scratchNormal = new Cesium.Cartesian3();
scene.preRender.addEventListener(function () {
const surfaceNormal = globe.ellipsoid.geodeticSurfaceNormal(
camera.positionWC,
scratchNormal
);
Cesium.Cartesian3.negate(surfaceNormal, surfaceNormal);
scene.light.direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.add(surfaceNormal, camera.rightWC, surfaceNormal),
scene.light.direction
);
});
// Elevation color ramp + contour lines (color scale inspired by cmocean deep)
const minHeight = -10000.0;
const maxHeight = 2000.0;
const contourLineSpacing = 500.0;
const range = maxHeight - minHeight;
const d = (height) => (height - minHeight) / range;
function getColorRamp() {
const ramp = document.createElement("canvas");
ramp.width = 100;
ramp.height = 15;
const ctx = ramp.getContext("2d");
const grd = ctx.createLinearGradient(0, 0, 100, 0);
grd.addColorStop(d(maxHeight), "#B79E6C");
grd.addColorStop(d(100.0), "#FBFFEE");
grd.addColorStop(d(0.0), "#F9FCCA");
grd.addColorStop(d(-500.0), "#BDE7AD");
grd.addColorStop(d(-1000.0), "#81D2A3");
grd.addColorStop(d(-1500.0), "#5AB7A4");
grd.addColorStop(d(-2000.0), "#4C9AA0");
grd.addColorStop(d(-2500.0), "#437D9A");
grd.addColorStop(d(-4000.0), "#3E6194");
grd.addColorStop(d(-5000.0), "#424380");
grd.addColorStop(d(-8000.0), "#392D52");
grd.addColorStop(d(minHeight), "#291C2F");
ctx.fillStyle = grd;
ctx.fillRect(0, 0, ramp.width, ramp.height);
return ramp;
}
function getElevationContourMaterial() {
return new Cesium.Material({
fabric: {
type: "ElevationColorContour",
materials: {
contourMaterial: { type: "ElevationContour" },
elevationRampMaterial: { type: "ElevationRamp" },
},
components: {
diffuse:
"(1.0 - contourMaterial.alpha) * elevationRampMaterial.diffuse + contourMaterial.alpha * contourMaterial.diffuse",
alpha: "max(contourMaterial.alpha, elevationRampMaterial.alpha)",
},
},
translucent: false,
});
}
function updateGlobeMaterial() {
const material = getElevationContourMaterial();
const exaggeration = scene.verticalExaggeration;
let shadingUniforms = material.materials.elevationRampMaterial.uniforms;
shadingUniforms.image = getColorRamp();
shadingUniforms.minimumHeight = minHeight * exaggeration;
shadingUniforms.maximumHeight = maxHeight * exaggeration;
shadingUniforms = material.materials.contourMaterial.uniforms;
shadingUniforms.width = 1.0;
shadingUniforms.spacing = contourLineSpacing * exaggeration;
shadingUniforms.color = Cesium.Color.BLACK.withAlpha(0.5);
globe.material = material;
}
updateGlobeMaterial();
// Fly to the western coast of Taiwan to observe the bathymetric relief of the Taiwan Strait
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(119.6, 24.0, 180000.0),
orientation: {
heading: Cesium.Math.toRadians(90.0),
pitch: Cesium.Math.toRadians(-45.0),
roll: 0.0,
},
duration: 2.5,
});
</script>
</body>
</html>If you only need minimal integration, you can also use these two lines as shown in the official documentation.
viewer.scene.setTerrain(
new Cesium.Terrain(Cesium.CesiumTerrainProvider.fromIonAssetId(2426648))
);For more detailed instructions, refer to the official blog, where you can adjust parameters such as imagery basemap selection, vertical exaggeration, and contour line density. Also, the current dataset provides relatively higher resolution for areas like the Great Barrier Reef, Mariana Trench, and coastal Philippines, which are suitable for demo purposes.
Comparison with World Terrain
From the demo above, it is clear that Cesium World Terrain and World Bathymetry have different focuses: one on land, the other on the ocean. If your application primarily covers land, it is recommended to use Cesium World Terrain; if it leans toward the ocean, use World Bathymetry. Of course, you can also automatically switch based on the camera's location. Note that default satellite imagery over open ocean tends to be dark, making bathymetric relief hard to discern. You can adopt my approach of using a color ramp to highlight elevation differences, or use a brighter imagery basemap.
Summary
In the past, when building 3D globes with Cesium, the ocean floor was often just a flat blue area. If you wanted to display seafloor terrain, you had to tile your own bathymetric DEM data, which involved data processing and hosting costs. Now that Cesium has integrated World Bathymetry into the ion global content library, underwater terrain can be accessed using the same pipeline as land 3D data. This greatly simplifies coastal zone visualization, offshore wind farm representation, and seafloor geomorphology applications—making it a valuable dataset for ocean GIS enthusiasts.
Currently, only a few regions have high-resolution data, and the official team will update it over time. Those interested can keep an eye on it. Have you worked on similar scenarios? Feel free to share in the comments.
References
- Cesium World Bathymetry product page: https://cesium.com/platform/cesium-ion/content/cesium-world-bathymetry/
- Introducing Cesium World Bathymetry: https://cesium.com/blog/2024/01/23/introducing-cesium-world-bathymetry/
- Working with Cesium World Bathymetry in CesiumJS: https://cesium.com/blog/2024/01/29/cesium-world-bathymetry-in-cesiumjs/