In production, the satellite flight on this site showed "3D rendering unavailable on this device". Locally it worked. Production has a strict Content-Security-Policy:
script-src 'self'That one line broke two separate things.
#1. WebAssembly needs its own permission
Cesium decodes parts of its data with WebAssembly. Under script-src 'self', Chrome refuses to compile WASM. The fix is narrow and does not allow eval for JavaScript:
script-src 'self' 'wasm-unsafe-eval'#2. Viewer brings Knockout, and Knockout brings new Function
Cesium.Viewer is the batteries-included widget: timeline, animation dial, base-layer picker. Its UI binds through Knockout, which compiles bindings with new Function(). That is exactly what script-src 'self' exists to block, and I did not want to add unsafe-eval to the whole site for a few buttons I did not use.
CesiumWidget is the same globe without the Knockout widgets. In current Cesium it also has entities, so markers and ellipses still work:
import { CesiumWidget, EllipsoidTerrainProvider } from 'cesium';
const viewer = new CesiumWidget(stage, {
baseLayer: false,
terrainProvider: new EllipsoidTerrainProvider(),
requestRenderMode: true,
});#Lessons
- "3D unavailable" was a policy error dressed up as a GPU error. Read the console for CSP violations first.
- Prefer the smallest component that does the job. It usually needs fewer exceptions in your security policy.
- Keep production headers in local testing: I now serve the built site with the same CSP that production uses before every deploy.