# How to Implement Screen-Space Detection Overlays in CesiumJS

> Implement high-performance screen-space detection overlays in CesiumJS with reusable canvas layers. Render detection markers efficiently without WebGL scene graph changes. Learn more now.

- Repository: [Bilawal Sidhu/gods-eye-view](https://github.com/bilawalsidhu/gods-eye-view)
- Tags: how-to-guide
- Published: 2026-09-04

---

**God's Eye View provides a high-performance, reusable screen-space detection overlay for CesiumJS that uses dual HTML5 Canvas layers with CSS blend modes to render detection markers without modifying the WebGL scene graph.**

Screen-space detection overlays in CesiumJS allow you to annotate 3D geospatial data with 2D markers that track world positions while remaining sharp and readable at any camera distance. The God's Eye View (GEV) repository implements this pattern using thin Canvas2D layers that sit above the Cesium canvas but below UI chrome, enabling real-time rendering of detection markers without polluting the scene graph or interfering with the 3D render pipeline.

## Architecture of the Overlay System

The GEV overlay architecture isolates presentation logic from Cesium-specific rendering by separating DOM management from drawing operations.

### Dual-Canvas DOM Structure

At the core of [`src/overlays/worldOverlay.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/overlays/worldOverlay.js), the `ensureOverlayDom()` function (lines 19-34) lazily creates a DOM host containing two canvas elements. The parent `div` (`id="world-overlay-root"`) contains a standard canvas (`id="world-overlay-canvas"`) for normal cards and a detection surface canvas (`id="world-overlay-detection-surface"`) for detection graphics. This separation allows the system to apply different blending strategies to informational labels versus interactive detection markers.

### CSS Blend Mode Integration

The detection surface is appended directly to the Cesium container and configured with `mix-blend-mode: screen` (discussed in lines 34-42). This CSS rule ensures that detection graphics blend with the underlying 3D scene while maintaining the ability to draw over it. By positioning the canvases outside the WebGL context but inside the viewer container, the overlay avoids creating intermediate stacking contexts that would block the blend operation.

## Coordinate Projection and Viewport Synchronization

Accurate screen-space positioning requires continuous synchronization between the 3D world, the camera, and the 2D canvas pixel grid.

### World-to-Screen Transformation

To project a Cesium Cartesian position into pixel coordinates, the system calls `Cesium.SceneTransforms.worldToWindowCoordinates(viewer.scene, position)`. This returns a `Cartesian2` object containing `x` and `y` values representing the screen-space location. The `paintOverlayEntry` function in [`src/overlays/worldOverlayDraw.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/overlays/worldOverlayDraw.js) consumes these coordinates to position markers using the standard Canvas2D API.

### Handling High-DPI Displays

The `ensureCanvasSize()` function in [`src/overlays/worldOverlay.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/overlays/worldOverlay.js) (lines 105-112) recomputes canvas dimensions on every viewport resize, accounting for `window.devicePixelRatio`. This ensures that detection markers remain crisp on high-DPI displays by sizing the canvas backing store to match physical pixels rather than CSS pixels, then scaling the 2D context appropriately.

## Rendering Detection Markers

All drawing logic resides in [`src/overlays/worldOverlayDraw.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/overlays/worldOverlayDraw.js), which provides pure-JavaScript Canvas2D utilities independent of Cesium scene management.

### Canvas2D Drawing Utilities

The drawing module exports several performance-critical helpers. The `roundedRectPath()` function (lines 16-27) generates path data for rounded rectangles without allocating new objects. For text rendering, `measureWorldOverlayText()` (lines 80-99) implements a cached width measurement system that prevents the expensive cost of recalculating text metrics every frame. These utilities are used by the main rendering loop to draw detection markers as filled circles, rings, or custom SVG-derived graphics.

### Distance and Altitude Scaling

To maintain consistent readability regardless of camera height, the overlay system implements distance-based scaling utilities within [`worldOverlayDraw.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/worldOverlayDraw.js). These functions calculate the apparent size of markers based on the camera's altitude and distance to the target, ensuring that detection graphics do not become unreadably small when zoomed out or overwhelming when zoomed in close.

### UI Occlusion Prevention

The overlay respects UI chrome through `WORLD_OVERLAY_OCCLUDER_SELECTORS`, which enumerates elements like `#title-bar` and `.hud-top-left` that must remain visible. Before committing a marker placement, the `overlayRectIntersectsAny()` function (lines 85-103 in [`worldOverlay.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/worldOverlay.js)) checks candidate rectangles against these occluder boundaries. If a collision is detected, the algorithm either slides the label to a non-overlapping position or drops it entirely, guaranteeing that critical interface elements are never obscured.

## Integration and Lifecycle Management

Applications interact with the overlay system through the public API exposed in [`src/ui.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/ui.js).

### Initialization API

To activate the overlay, call `initWorldOverlay(viewer)`, which creates the DOM host, attaches the canvases, and registers a `requestRender` loop that drives the `drawWorldOverlay()` function. Once initialized, `setDetectionMode()` controls the density of displayed markers, accepting values of `'OFF'`, `'SPARSE'`, `'BALANCED'`, or `'DENSE'` to filter visualization intensity based on operational requirements.

### Data Source Interface

The overlay host expects data sources to implement a minimal interface: an `entries` property containing a Map of identifiers to objects with `position` data, and an `update(viewer)` method called each frame. When you add a source via `viewer.scene.primitives.add()`, GEV automatically iterates through `entries`, projects positions to screen space, and renders corresponding detection markers.

### Cleanup and Destruction

When removing the map or changing contexts, `destroyWorldOverlayDraw()` (exported from [`worldOverlayDraw.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/worldOverlayDraw.js), lines 50-66) removes font-loading listeners, clears the text-measure cache, and deletes DOM elements. The companion `installWorldOverlayFontInvalidation()` function ensures that cached text measurements are recalculated if web fonts load after initialization, preventing layout shifts or measurement errors.

## Complete Implementation Example

The following snippet demonstrates enabling the detection overlay with a custom data source:

```javascript
import * as Cesium from 'cesium';
import { initWorldOverlay, setDetectionMode } from './src/ui.js';

// 1️⃣ Create the Cesium Viewer
const viewer = new Cesium.Viewer('cesiumContainer', {
  // ... your Cesium options …
});

// 2️⃣ Initialise the screen‑space overlay host
await initWorldOverlay(viewer);   // ⇢ creates DOM + canvases

// 3️⃣ Turn the detection overlay on
setDetectionMode('BALANCED');    // ⇢ draws detection markers for the active source

// 4️⃣ Add a custom detection source
const mySource = {
  entries: new Map(),
  update(viewer) {
    // Example: put a detection marker at the Eiffel Tower
    const position = Cesium.Cartesian3.fromDegrees(2.2945, 48.8584, 0);
    this.entries.set('eiffel', { position, variant: 'track' });
  },
};
viewer.scene.primitives.add(mySource);   // GEV will pick it up automatically

```

## Summary

- **Screen-space detection overlays in CesiumJS** are implemented in God's Eye View using dual HTML5 Canvas layers that sit outside the WebGL render pipeline but inside the viewer container.
- The **`ensureOverlayDom()`** function creates a dedicated DOM host with separate canvases for standard cards and detection graphics, using **`mix-blend-mode: screen`** for seamless visual integration.
- World positions are projected to pixels using **`Cesium.SceneTransforms.worldToWindowCoordinates`**, with **`ensureCanvasSize()`** handling high-DPI viewport synchronization.
- Drawing operations are optimized through cached text measurement in **`measureWorldOverlayText()`** and path generation in **`roundedRectPath()`**, all contained in **[`src/overlays/worldOverlayDraw.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/overlays/worldOverlayDraw.js)**.
- UI occlusion is prevented via **`overlayRectIntersectsAny()`**, which checks against selectors defined in **`WORLD_OVERLAY_OCCLUDER_SELECTORS`**.
- The public API in **[`src/ui.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/ui.js)** provides **`initWorldOverlay()`** and **`setDetectionMode()`** for initialization and density control, while **`destroyWorldOverlayDraw()`** handles complete teardown.

## Frequently Asked Questions

### How does the overlay system avoid interfering with Cesium's WebGL renderer?

The overlay appends HTML5 Canvas elements directly to the Cesium container as DOM siblings to the WebGL canvas, rather than injecting geometry into the scene graph. According to the implementation in [`src/overlays/worldOverlay.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/overlays/worldOverlay.js), the detection surface uses CSS `mix-blend-mode: screen` to composite with the 3D imagery without requiring depth buffer access or primitive commands. This separation ensures that camera movements, terrain loading, and entity updates in Cesium do not trigger expensive JavaScript-side re-renders of the overlay.

### What is the purpose of the two separate canvas elements?

The dual-canvas architecture separates concerns between informational cards and detection graphics. As defined in `ensureOverlayDom()` (lines 19-34), the `world-overlay-canvas` handles standard annotation cards, while the `world-overlay-detection-surface` receives detection-specific graphics with different blending requirements. This separation allows detection markers to use `mix-blend-mode: screen` for highlighting effects without affecting the opacity or blending of standard text labels, which may need different compositing rules.

### How does the system handle high-DPI displays and resizing?

The `ensureCanvasSize()` function in [`src/overlays/worldOverlay.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/overlays/worldOverlay.js) (lines 105-112) executes on every viewport resize event. It queries `window.devicePixelRatio` and sets the canvas internal width and height to match the physical pixel count, then scales the 2D context to maintain correct coordinate mapping. This approach ensures that detection markers remain sharp on Retina displays and 4K monitors without requiring the application code to manually handle pixel density conversions.

### How are detection markers prevented from covering critical UI elements?

Before rendering each marker, the system checks for collisions with UI chrome using `overlayRectIntersectsAny()` (lines 85-103). This function compares the candidate marker rectangle against the bounding boxes of elements listed in `WORLD_OVERLAY_OCCLUDER_SELECTORS`, such as `#title-bar` and `.hud-top-left`. If an intersection is detected, the placement algorithm either shifts the marker to an adjacent clear area or suppresses rendering for that frame, ensuring that navigation controls and status indicators remain fully visible and interactive.