Technique 01 — Cesium Flyover
A helicopter glide through real terrain
Google-Earth-grade 3D mountains — quantized-mesh terrain draped in satellite imagery — with a camera that flows along a river, banks into the bends, and never jitters. For the beat where the viewer needs to feel the landscape: a canyon, a gorge, a coastline, a route.
/ Fit
What it's for
Reach for it when
- The story needs a cinematic reveal of a real place — fly through a canyon, over a mountain range, along a border or supply route.
- You want true 3D geometry with sharp ridgelines, not a flat map tilted back.
- The shot must be reproducible: re-render after a script change and get the identical move.
Not the tool for
- Flat "where is this?" maps, choropleths, or data overlays — switch to the 2D Map Animation.
- Street-level or city photorealism (that's a 3D-tiles job, a paid tier above this stack).
/ Architecture
Three parts, one job each
Neither engine can do this alone — Cesium won't render reliably headless by itself, and Remotion has no 3D globe. Stacked, they cover each other exactly.
Draws the world
CesiumJS
A true free camera in a 3D globe that renders to the horizon. Loaded from CDN at runtime, driven one frame at a time — its default render loop is switched off.
Feeds the world
MapTiler
Quantized-mesh terrain (real 3D geometry, crisp silhouettes) plus satellite imagery draped on top. One API key covers both — no separate Cesium ion account.
Runs the clock
Remotion
Steps the timeline frame by frame and holds each frame until every terrain tile is loaded before capturing it. Deterministic in, deterministic out.
/ Pipeline
From a line on a map to a finished shot
Prepare the camera path
Start from a river or route centerline (OpenStreetMap). The prep script clips a window, resamples it to even spacing, smooths it into continuously flowing curves, and dampens the swerve toward a straight chord — one knob controls how much the camera weaves.
Fly the path
Each frame, the camera walks the path by distance and aims at a real point further along it. That far aim is what makes the heading turn gently with the terrain — and the rate of turn drives a subtle roll, so the camera banks into bends like a helicopter.
Render
One headless Chrome, one frame at a time: npx remotion render … --gl=angle.
Every frame waits for tilesLoaded before capture, so there are no half-loaded
tiles in the final video — ever.
/ Live demo
The camera path, interactive
This is the actual path algorithm running in your browser, top-down. The thin cyan line is the raw river; the amber line is the smoothed, dampened camera path; the marker aims at a point ahead of itself and banks with the turn. Drag the sliders.
Swerve at 0% flies dead straight; 100% follows every meander. The proven water-wars values: swerve 45%, look-ahead 1.5 km, 13 km travelled over 24 s (≈0.54 km/s — slower reads calmer, and renders faster because tiles stay cached).
/ Tuning
Every feel decision is one number
| You want | Turn this | Proven value |
|---|---|---|
| Slower, more peaceful | Composition duration (speed = distance ÷ seconds) | 24 s for 13 km |
| More / less swerve | DAMP in the path script | 0.45 |
| Higher / lower camera | ALT_START / ALT_END (absolute, in metres) | 4600 → 4300 m |
| More horizon in frame | PITCH_FROM_NADIR | 76° |
| Stronger / gentler bank | MAX_BANK / BANK_GAIN | ±7.5° / 0.6 |
| More dramatic relief | verticalExaggeration | 1.1 |
The gotcha this skill exists to spare you
Cesium's globe silently refuses to draw in a plain headless browser — WebGL works, the sky
renders, but the terrain produces zero draw commands. The fix that works: render through
Remotion, disable Cesium's own render loop, drive every frame with viewer.render()
(never scene.render()), and gate each frame on tilesLoaded. The skill's
troubleshooting file maps every failure symptom to its fix.
/ Contents
What ships in the skill
SKILL.mdthe entry point — when to use it, architecture, quick startassets/CesiumFlythrough.tsxthe runnable Remotion componentassets/example-Root.tsxminimal composition scaffold (duration = speed knob)assets/sample-river.geojsonan OSM gorge centerline — works out of the boxassets/cesium-path.jsonthe pre-generated camera path for the samplescripts/prep-cesium-path.mjscamera-path generator (dependency-free Node)references/architecture.mdfull camera math + proven feel valuesreferences/troubleshooting.mdevery symptom → its fix/ Quick start
Four steps to a first render
# 1 — copy the component + scaffold into a Remotion project
cp assets/CesiumFlythrough.tsx your-project/src/components/
cp assets/example-Root.tsx your-project/src/Root.tsx
# 2 — generate a camera path (runs against the shipped sample as-is)
node scripts/prep-cesium-path.mjs # or: … my-river.geojson src/geo/cesium-path.json
# 3 — set the key (must be an UNRESTRICTED MapTiler key; domain-locked keys 403 headless)
echo "REMOTION_MAPTILER_KEY=…" > .env
# 4 — validate one still, then render
npx remotion still src/index.ts CesiumFlyover out.png --frame=360 --gl=angle --timeout=180000
npx remotion render src/index.ts CesiumFlyover out.mp4 --gl=angle --concurrency=1 --timeout=180000