# How to Perform Headless Cesium Scene Capture Using the God’s Eye View Tools

> Learn how to perform headless Cesium scene capture using the gods-eye-view tools. Render 3D globes to JPEG images off-screen with Puppeteer and headless Chromium.

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

---

**The `bilawalsidhu/gods-eye-view` repository provides a dedicated command-line utility—`tools/cesium-render.mjs`—that renders CesiumJS 3D globes to JPEG images completely off-screen using Puppeteer and headless Chromium.**

Headless Cesium scene capture enables automated pipelines to generate high-resolution 3D imagery without browser windows or manual interaction. The God’s Eye View codebase orchestrates a temporary HTTP server, CesiumJS `Viewer` instances, and progressive tile refinement to produce publication-ready screenshots from geographic coordinates.

## Architecture of the Capture Tool

The rendering workflow centers on two primary files that isolate heavy rendering work inside a headless browser process.

### `tools/cesium-render.mjs`

This Node.js CLI driver handles argument parsing, API key management, and Puppeteer orchestration. It spawns a temporary HTTP server to serve static Cesium assets from `node_modules/cesium/Build/Cesium` alongside a minimal HTML loader.

### [`tools/cesium-render.html`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/tools/cesium-render.html)

The HTML page loaded by Puppeteer creates a Cesium `Viewer` with all UI widgets hidden and the default globe disabled. It invokes `Cesium.createGooglePhotorealistic3DTileset(apiKey)` to stream photorealistic tiles, then exposes camera positioning helpers to the Node.js controller via browser-side JavaScript execution.

## Camera Positioning Modes

The tool supports two distinct methods for framing the final shot.

### Direct Position Mode

Supply explicit camera coordinates using `--lat` and `--lon`. The camera places directly at the specified geographic location, oriented by `--heading` and `--pitch` parameters.

### Look-At Target Mode

Use `--lookat-lat` and `--lookat-lon` to focus on a specific point. The script calculates an offset position behind the heading direction using the formula `height / tan(pitch)` for horizontal distance. It then converts this offset to geographic coordinates and derives precise orientation vectors using `Cesium.EllipsoidGeodesic` to ensure the target sits exactly in the frame center.

## The Capture Workflow

The `tools/cesium-render.mjs` script executes a nine-stage pipeline to ensure high-fidelity output.

1. **Argument Parsing** – Validates camera parameters, image dimensions, and screen-space error (SSE) targets.
2. **API Key Resolution** – Checks the `--key` flag first, then the `GOOGLE_MAPS_API_KEY` environment variable, and finally the `.env` file in the project root. Abort occurs if no key is found.
3. **Server Initialization** – Serves [`tools/cesium-render.html`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/tools/cesium-render.html) and Cesium assets on a transient local port.
4. **Viewer Creation** – Puppeteer instantiates the headless browser, loads the page, and creates the tileset with an initial coarse SSE of 16 for rapid loading.
5. **Ground Height Sampling** – Positions a temporary camera 500 meters above the target to sample terrain elevation via Cesium’s globe picking.
6. **Progressive Tile Refinement** – Iteratively lowers the tileset’s `maximumScreenSpaceError` from 16 down to the user-specified target (default 2), waiting for tile stability between each reduction.
7. **Final Camera Placement** – Computes the true camera position using either direct coordinates or the look-at offset formula, setting altitude to `groundHeight + --height`.
8. **Rendering** – Forces multiple render frames to ensure texture streaming completes, then captures a screenshot of the `<canvas>` element.
9. **Cleanup** – Writes the timestamped JPEG to the output directory (default `output/`) and terminates the server and browser instance.

## Command-Line Usage Examples

### Basic Direct Capture

Render Austin, Texas from a southwest approach at 720p resolution:

```bash
node tools/cesium-render.mjs \
  --lat 30.266476 \
  --lon -97.73719 \
  --heading 270 \
  --pitch -15 \
  --height 8 \
  --width 1280 \
  --height-px 720 \
  --outdir captures \
  --key YOUR_GOOGLE_MAPS_API_KEY

```

### Look-At Mode with High Resolution

Capture New York City looking north at 1440p with a 90-degree field of view:

```bash
node tools/cesium-render.mjs \
  --lookat-lat 40.7112 \
  --lookat-lon -74.0123 \
  --heading 180 \
  --pitch -30 \
  --height 25 \
  --fov 90 \
  --width 2560 \
  --height-px 1440 \
  --outdir captures \
  --key YOUR_GOOGLE_MAPS_API_KEY

```

## Programmatic Integration

Import the CLI into other Node.js scripts using `child_process` for batch generation:

```javascript
import { spawn } from 'node:child_process';

const args = [
  '--lookat-lat', '40.7128',
  '--lookat-lon', '-74.0060',
  '--heading', '90',
  '--pitch', '-20',
  '--height', '15',
  '--width', '1920',
  '--height-px', '1080',
  '--outdir', 'screenshots',
];

const proc = spawn('node', ['tools/cesium-render.mjs', ...args], {
  env: { 
    ...process.env, 
    GOOGLE_MAPS_API_KEY: process.env.GOOGLE_MAPS_API_KEY 
  },
});

proc.stdout.pipe(process.stdout);
proc.stderr.pipe(process.stderr);

await new Promise((resolve) => proc.on('close', resolve));

```

## Summary

- **`tools/cesium-render.mjs`** provides a complete headless Cesium scene capture pipeline using Puppeteer and a temporary HTTP server.
- **Two camera modes** support both direct positioning (`--lat`/`--lon`) and calculated look-at targeting (`--lookat-lat`/`--lookat-lon`).
- **Progressive SSE refinement** ensures final images use the highest available detail level by stepping down from 16 to the target error threshold.
- **Automatic ground sampling** adjusts camera altitude relative to terrain elevation rather than sea level.
- **Flexible authentication** accepts API keys via CLI flags, environment variables, or `.env` files.

## Frequently Asked Questions

### What API key is required for headless Cesium rendering?

The script requires a **Google Maps API key** to access the Photorealistic 3D Tileset via `Cesium.createGooglePhotorealistic3DTileset()`. It checks the `--key` argument first, then the `GOOGLE_MAPS_API_KEY` environment variable, and finally the `.env` file. Without a valid key, the tool aborts unless you modify [`tools/cesium-render.html`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/tools/cesium-render.html) to use a non-photorealistic basemap.

### How does the script handle camera positioning in look-at mode?

In look-at mode, the utility calculates a camera offset behind the target using the supplied heading and pitch. It computes horizontal distance as `height / tan(pitch)`, converts this Cartesian offset to geographic coordinates using `Cesium.EllipsoidGeodesic`, and positions the camera at `groundHeight + --height` for precise framing.

### Why does the tool use progressive screen-space error reduction?

The script initializes the tileset with a coarse SSE of 16 for rapid loading, then iteratively lowers it to the target value (default 2). This staged approach prevents timeout errors while ensuring the final screenshot captures the maximum available geometric detail once all tiles stabilize.

### Can I run this tool in a CI environment?

Yes. The architecture is designed for automated pipelines: it uses headless Chromium via Puppeteer, requires no display server, handles all Cesium asset serving internally, and exits with appropriate error codes on failure. Ensure the environment provides the `GOOGLE_MAPS_API_KEY` and sufficient memory for 3D tile caching.