What it is
Remotion makes videos by screenshotting a React page once per frame. A map is the worst possible thing to screenshot: tiles stream in asynchronously, labels settle over hundreds of milliseconds, and the camera animates on its own clock. Point Remotion at a map naively and you get blurry frames, missing labels, and a video that renders differently every time.@nika-js/onlymap-remotion makes a map safe to screenshot. Every frame is held
until the map reports it has genuinely finished drawing, and a frame is a pure
function of the frame number — the same frame renders byte-identically whether
it is produced alone, in sequence, or out of order.
Which tool for which job
If the video is just the map moving — no titles, no overlays, one output —
you do not need this package.
npx onlymapjs record map.html --out flyby.mp4
is one command and has none of the caveats below.Bring data, not coordinates
The camera should be derived from data you already have. The prop you pass to<OmMapVideo> selects the source, and omMapVideoMetadata derives the
composition’s duration from that same prop — so a longer route is a longer
video, and an extra stop is a longer tour.
useOmManifest
and useOmCamera hooks it is built from.
Hand-typed coordinate arrays are the escape hatch, not the starting point. If
you find yourself typing longitudes, you probably want a route file instead.
Overlays that track geography
This is the capability a recorded MP4 cannot offer. Because the map is live DOM inside your React tree,projectLngLat gives a coordinate’s screen position
each frame, so Remotion content — <Sequence>-timed, interpolate()-animated —
stays pinned to a place as the camera flies over it.
The rules that differ from ordinary Remotion
- ANGLE GL is required, not preferred. Remotion’s default software renderer
cannot create a WebGL context for the map — every render fails. Set
Config.setChromiumOpenGlRenderer("angle")inremotion.config.ts, or passchromiumOptions: { gl: "angle" }to programmatic@remotion/renderercalls, which do not read the config file. - Preview and render gate differently, on purpose. Studio releases each
frame as soon as the camera is written, so scrubbing is instant and tiles pop
in behind the playhead. Rendering always waits for the map to settle,
whatever the preview does. Set
previewGate="exact"to preview at final quality. - Studio cannot host interactive map UI. Its preview overlays consume
pointer events before a composition sees them, so buttons and map gestures
inside a composition receive nothing. Interactive shot composing runs as a
separate page (
npm run rig) that writes a JSON file a composition loads; tweaks belong in the props panel via a zodschema. - Manifests are prepared before mounting. Every
<om-map>gains the recording switch,<script>tags are removed (they never execute underinnerHTML, and a second OnlyMap runtime would fail deck.gl’s duplicate-version check), andloopis stripped. Each warns once. - Concurrency turns over. Measured on a basemap scene:
2→ 1.66×,4→ 1.90×,8→ slower than 4. Use--concurrency 2; every worker is a browser tab with a cold tile cache, so start at1on metered providers.
Verified, not asserted
The determinism claim is enforced by a render suite that boots a real browser: the same frame rendered twice in separate processes must be byte-identical, and a frame rendered out of order must equal the same frame rendered cold. A cross-check renders the same manifest throughonlymapjs record and through the
plugin and compares them — currently 0.9999 structural similarity at aligned
story time, across two different Chromium and GL stacks.
Reference
- The package ships
llms.txt(the complete agent-facing reference) and an installable agent skill — both inside the npm tarball. - Peers:
@nika-js/onlymapat an exact version (it bundles deck.gl, which fails on duplicate versions),remotion≥ 4.0.342, React ≥ 18.zodis optional, for props-panel schemas. - Licensing: free for non-commercial use provided the in-frame OnlyMap attribution survives into the output; commercial use needs a key. Remotion and OnlyMap JS carry their own separate terms — see Licensing & Telemetry.