dsh-remotion 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +7 -5
- package/README.md +3 -3
- package/lib/index.d.ts +2 -2
- package/lib/index.js +15 -2
- package/package.json +1 -1
- package/skills/remotion-best-practices/SKILL.md +59 -0
- package/skills/remotion-best-practices/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/assets/remotion-icon.png +0 -0
- package/skills/remotion-best-practices/remotion-captions/SKILL.md +36 -0
- package/skills/remotion-best-practices/remotion-captions/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-captions/assets/remotion-icon.png +0 -0
- package/skills/{remotion/rules → remotion-best-practices/remotion-captions}/display-captions.md +9 -3
- package/skills/{remotion/rules → remotion-best-practices/remotion-captions}/import-srt-captions.md +5 -1
- package/skills/remotion-best-practices/remotion-create/SKILL.md +68 -0
- package/skills/remotion-best-practices/remotion-create/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-create/assets/remotion-icon.png +0 -0
- package/skills/{remotion/rules → remotion-best-practices/remotion-create}/tailwind.md +1 -1
- package/skills/remotion-best-practices/remotion-create/video-layout.md +9 -0
- package/skills/remotion-best-practices/remotion-docs/SKILL.md +46 -0
- package/skills/remotion-best-practices/remotion-docs/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-docs/assets/remotion-icon.png +0 -0
- package/skills/remotion-best-practices/remotion-interactivity/SKILL.md +247 -0
- package/skills/remotion-best-practices/remotion-interactivity/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-interactivity/assets/remotion-icon.png +0 -0
- package/skills/remotion-best-practices/remotion-maps/SKILL.md +36 -0
- package/skills/remotion-best-practices/remotion-maps/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-maps/assets/remotion-icon.png +0 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/cesium/TECHNIQUE.md +89 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/cesium/assets/CesiumFlythrough.tsx +284 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/cesium/assets/cesium-path.json +299 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/cesium/assets/city-path.json +12 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/cesium/assets/example-Root.tsx +39 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/cesium/assets/flight-path.ts +35 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/cesium/assets/sample-river.geojson +215 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/cesium/references/3d-data-sources.md +43 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/cesium/references/3d-flyover-architecture.md +128 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/cesium/references/3d-troubleshooting.md +57 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/cesium/scripts/prep-cesium-path.mjs +161 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/mapbox/TECHNIQUE.md +449 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/mapbox/references/render-stability.md +64 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maplibre/TECHNIQUE.md +465 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maplibre/references/render-stability.md +64 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/TECHNIQUE.md +80 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/assets/CountryLabel.tsx +70 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/assets/MapTilerVectorElement.ts +80 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/assets/RiverReveal.tsx +363 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/assets/example-Root.tsx +22 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/assets/sample-data/country-meta.json +7535 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/assets/sample-data/yarlung-flow.json +605 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/assets/tokens.ts +34 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/references/map-data-sources.md +91 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/references/map-explainer-architecture.md +129 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/references/map-geo-prep.md +85 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/references/render-stability.md +64 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/maptiler/scripts/prep-geo.mjs +202 -0
- package/skills/remotion-best-practices/remotion-maps/techniques/static-map/TECHNIQUE.md +37 -0
- package/skills/remotion-best-practices/remotion-markup/SKILL.md +365 -0
- package/skills/remotion-best-practices/remotion-markup/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-markup/assets/remotion-icon.png +0 -0
- package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/audio.md +4 -2
- package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/calculate-metadata.md +4 -2
- package/skills/remotion-best-practices/remotion-markup/compositions.md +117 -0
- package/skills/remotion-best-practices/remotion-markup/cropping.md +34 -0
- package/skills/remotion-best-practices/remotion-markup/effects.md +251 -0
- package/skills/{remotion/rules/videos.md → remotion-best-practices/remotion-markup/embedding-videos.md} +6 -4
- package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/ffmpeg.md +5 -1
- package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/gifs.md +55 -9
- package/skills/remotion-best-practices/remotion-markup/google-fonts.md +76 -0
- package/skills/remotion-best-practices/remotion-markup/html-in-canvas.md +128 -0
- package/skills/remotion-best-practices/remotion-markup/images.md +73 -0
- package/skills/remotion-best-practices/remotion-markup/light-leaks.md +106 -0
- package/skills/remotion-best-practices/remotion-markup/local-fonts.md +69 -0
- package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/lottie.md +4 -1
- package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/measuring-dom-nodes.md +5 -1
- package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/measuring-text.md +8 -2
- package/skills/remotion-best-practices/remotion-markup/multi-scene-video.md +81 -0
- package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/parameters.md +3 -1
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/SKILL.md +36 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/assets/remotion-icon.png +0 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/cesium/TECHNIQUE.md +89 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/cesium/assets/CesiumFlythrough.tsx +284 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/cesium/assets/cesium-path.json +299 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/cesium/assets/city-path.json +12 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/cesium/assets/example-Root.tsx +39 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/cesium/assets/flight-path.ts +35 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/cesium/assets/sample-river.geojson +215 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/cesium/references/3d-data-sources.md +43 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/cesium/references/3d-flyover-architecture.md +128 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/cesium/references/3d-troubleshooting.md +57 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/cesium/scripts/prep-cesium-path.mjs +161 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/mapbox/TECHNIQUE.md +449 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/mapbox/references/render-stability.md +64 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maplibre/TECHNIQUE.md +465 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maplibre/references/render-stability.md +64 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/TECHNIQUE.md +80 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/assets/CountryLabel.tsx +70 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/assets/MapTilerVectorElement.ts +80 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/assets/RiverReveal.tsx +363 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/assets/example-Root.tsx +22 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/assets/sample-data/country-meta.json +7535 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/assets/sample-data/yarlung-flow.json +605 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/assets/tokens.ts +34 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/references/map-data-sources.md +91 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/references/map-explainer-architecture.md +129 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/references/map-geo-prep.md +85 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/references/render-stability.md +64 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/maptiler/scripts/prep-geo.mjs +202 -0
- package/skills/remotion-best-practices/remotion-markup/remotion-maps/techniques/static-map/TECHNIQUE.md +37 -0
- package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/sequencing.md +63 -12
- package/skills/remotion-best-practices/remotion-markup/sfx.md +51 -0
- package/skills/remotion-best-practices/remotion-markup/text-highlights.md +56 -0
- package/skills/remotion-best-practices/remotion-markup/timing.md +111 -0
- package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/transitions.md +49 -7
- package/skills/remotion-best-practices/remotion-markup/video-editing.md +90 -0
- package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/voiceover.md +2 -2
- package/skills/remotion-best-practices/remotion-multimedia/SKILL.md +20 -0
- package/skills/remotion-best-practices/remotion-multimedia/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-multimedia/assets/remotion-icon.png +0 -0
- package/skills/{remotion/rules → remotion-best-practices/remotion-multimedia}/get-audio-duration.md +1 -3
- package/skills/{remotion/rules → remotion-best-practices/remotion-multimedia}/get-video-dimensions.md +1 -3
- package/skills/{remotion/rules → remotion-best-practices/remotion-multimedia}/get-video-duration.md +1 -3
- package/skills/remotion-best-practices/remotion-render/SKILL.md +27 -0
- package/skills/remotion-best-practices/remotion-render/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-render/assets/remotion-icon.png +0 -0
- package/skills/remotion-best-practices/remotion-saas/SKILL.md +33 -0
- package/skills/remotion-best-practices/remotion-saas/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-saas/assets/remotion-icon.png +0 -0
- package/skills/remotion-best-practices/remotion-saas/framework.md +21 -0
- package/skills/remotion-best-practices/remotion-saas/player.md +33 -0
- package/skills/remotion-best-practices/remotion-saas/rendering.md +69 -0
- package/skills/remotion-best-practices/remotion-studio/SKILL.md +24 -0
- package/skills/remotion-best-practices/remotion-studio/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-studio/assets/remotion-icon.png +0 -0
- package/skills/remotion-best-practices/remotion-upgrade/SKILL.md +31 -0
- package/skills/remotion-best-practices/remotion-upgrade/agents/openai.yaml +7 -0
- package/skills/remotion-best-practices/remotion-upgrade/assets/remotion-icon.png +0 -0
- package/skills/remotion-captions/SKILL.md +36 -0
- package/skills/remotion-captions/agents/openai.yaml +7 -0
- package/skills/remotion-captions/assets/remotion-icon.png +0 -0
- package/skills/remotion-captions/display-captions.md +190 -0
- package/skills/remotion-captions/import-srt-captions.md +73 -0
- package/skills/remotion-captions/transcribe-captions.md +70 -0
- package/skills/remotion-create/SKILL.md +68 -0
- package/skills/remotion-create/agents/openai.yaml +7 -0
- package/skills/remotion-create/assets/remotion-icon.png +0 -0
- package/skills/remotion-create/tailwind.md +11 -0
- package/skills/remotion-create/video-layout.md +9 -0
- package/skills/remotion-docs/SKILL.md +46 -0
- package/skills/remotion-docs/agents/openai.yaml +7 -0
- package/skills/remotion-docs/assets/remotion-icon.png +0 -0
- package/skills/remotion-interactivity/SKILL.md +247 -0
- package/skills/remotion-interactivity/agents/openai.yaml +7 -0
- package/skills/remotion-interactivity/assets/remotion-icon.png +0 -0
- package/skills/remotion-maps/SKILL.md +36 -0
- package/skills/remotion-maps/agents/openai.yaml +7 -0
- package/skills/remotion-maps/assets/remotion-icon.png +0 -0
- package/skills/remotion-maps/techniques/cesium/TECHNIQUE.md +89 -0
- package/skills/remotion-maps/techniques/cesium/assets/CesiumFlythrough.tsx +284 -0
- package/skills/remotion-maps/techniques/cesium/assets/cesium-path.json +299 -0
- package/skills/remotion-maps/techniques/cesium/assets/city-path.json +12 -0
- package/skills/remotion-maps/techniques/cesium/assets/example-Root.tsx +39 -0
- package/skills/remotion-maps/techniques/cesium/assets/flight-path.ts +35 -0
- package/skills/remotion-maps/techniques/cesium/assets/sample-river.geojson +215 -0
- package/skills/remotion-maps/techniques/cesium/references/3d-data-sources.md +43 -0
- package/skills/remotion-maps/techniques/cesium/references/3d-flyover-architecture.md +128 -0
- package/skills/remotion-maps/techniques/cesium/references/3d-troubleshooting.md +57 -0
- package/skills/remotion-maps/techniques/cesium/scripts/prep-cesium-path.mjs +161 -0
- package/skills/remotion-maps/techniques/mapbox/TECHNIQUE.md +449 -0
- package/skills/remotion-maps/techniques/mapbox/references/render-stability.md +64 -0
- package/skills/remotion-maps/techniques/maplibre/TECHNIQUE.md +465 -0
- package/skills/remotion-maps/techniques/maplibre/references/render-stability.md +64 -0
- package/skills/remotion-maps/techniques/maptiler/TECHNIQUE.md +80 -0
- package/skills/remotion-maps/techniques/maptiler/assets/CountryLabel.tsx +70 -0
- package/skills/remotion-maps/techniques/maptiler/assets/MapTilerVectorElement.ts +80 -0
- package/skills/remotion-maps/techniques/maptiler/assets/RiverReveal.tsx +363 -0
- package/skills/remotion-maps/techniques/maptiler/assets/example-Root.tsx +22 -0
- package/skills/remotion-maps/techniques/maptiler/assets/sample-data/country-meta.json +7535 -0
- package/skills/remotion-maps/techniques/maptiler/assets/sample-data/yarlung-flow.json +605 -0
- package/skills/remotion-maps/techniques/maptiler/assets/tokens.ts +34 -0
- package/skills/remotion-maps/techniques/maptiler/references/map-data-sources.md +91 -0
- package/skills/remotion-maps/techniques/maptiler/references/map-explainer-architecture.md +129 -0
- package/skills/remotion-maps/techniques/maptiler/references/map-geo-prep.md +85 -0
- package/skills/remotion-maps/techniques/maptiler/references/render-stability.md +64 -0
- package/skills/remotion-maps/techniques/maptiler/scripts/prep-geo.mjs +202 -0
- package/skills/remotion-maps/techniques/static-map/TECHNIQUE.md +37 -0
- package/skills/remotion-markup/3d.md +86 -0
- package/skills/remotion-markup/SKILL.md +365 -0
- package/skills/remotion-markup/agents/openai.yaml +7 -0
- package/skills/remotion-markup/assets/remotion-icon.png +0 -0
- package/skills/remotion-markup/audio-visualization.md +198 -0
- package/skills/remotion-markup/audio.md +171 -0
- package/skills/remotion-markup/calculate-metadata.md +136 -0
- package/skills/remotion-markup/compositions.md +117 -0
- package/skills/remotion-markup/cropping.md +34 -0
- package/skills/remotion-markup/effects.md +251 -0
- package/skills/remotion-markup/embedding-videos.md +173 -0
- package/skills/remotion-markup/ffmpeg.md +38 -0
- package/skills/remotion-markup/gifs.md +187 -0
- package/skills/remotion-markup/google-fonts.md +76 -0
- package/skills/remotion-markup/html-in-canvas.md +128 -0
- package/skills/remotion-markup/images.md +73 -0
- package/skills/remotion-markup/light-leaks.md +106 -0
- package/skills/remotion-markup/local-fonts.md +69 -0
- package/skills/remotion-markup/lottie.md +73 -0
- package/skills/remotion-markup/measuring-dom-nodes.md +38 -0
- package/skills/remotion-markup/measuring-text.md +146 -0
- package/skills/remotion-markup/multi-scene-video.md +81 -0
- package/skills/remotion-markup/parameters.md +111 -0
- package/skills/remotion-markup/remotion-maps/SKILL.md +36 -0
- package/skills/remotion-markup/remotion-maps/agents/openai.yaml +7 -0
- package/skills/remotion-markup/remotion-maps/assets/remotion-icon.png +0 -0
- package/skills/remotion-markup/remotion-maps/techniques/cesium/TECHNIQUE.md +89 -0
- package/skills/remotion-markup/remotion-maps/techniques/cesium/assets/CesiumFlythrough.tsx +284 -0
- package/skills/remotion-markup/remotion-maps/techniques/cesium/assets/cesium-path.json +299 -0
- package/skills/remotion-markup/remotion-maps/techniques/cesium/assets/city-path.json +12 -0
- package/skills/remotion-markup/remotion-maps/techniques/cesium/assets/example-Root.tsx +39 -0
- package/skills/remotion-markup/remotion-maps/techniques/cesium/assets/flight-path.ts +35 -0
- package/skills/remotion-markup/remotion-maps/techniques/cesium/assets/sample-river.geojson +215 -0
- package/skills/remotion-markup/remotion-maps/techniques/cesium/references/3d-data-sources.md +43 -0
- package/skills/remotion-markup/remotion-maps/techniques/cesium/references/3d-flyover-architecture.md +128 -0
- package/skills/remotion-markup/remotion-maps/techniques/cesium/references/3d-troubleshooting.md +57 -0
- package/skills/remotion-markup/remotion-maps/techniques/cesium/scripts/prep-cesium-path.mjs +161 -0
- package/skills/remotion-markup/remotion-maps/techniques/mapbox/TECHNIQUE.md +449 -0
- package/skills/remotion-markup/remotion-maps/techniques/mapbox/references/render-stability.md +64 -0
- package/skills/remotion-markup/remotion-maps/techniques/maplibre/TECHNIQUE.md +465 -0
- package/skills/remotion-markup/remotion-maps/techniques/maplibre/references/render-stability.md +64 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/TECHNIQUE.md +80 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/assets/CountryLabel.tsx +70 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/assets/MapTilerVectorElement.ts +80 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/assets/RiverReveal.tsx +363 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/assets/example-Root.tsx +22 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/assets/sample-data/country-meta.json +7535 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/assets/sample-data/yarlung-flow.json +605 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/assets/tokens.ts +34 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/references/map-data-sources.md +91 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/references/map-explainer-architecture.md +129 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/references/map-geo-prep.md +85 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/references/render-stability.md +64 -0
- package/skills/remotion-markup/remotion-maps/techniques/maptiler/scripts/prep-geo.mjs +202 -0
- package/skills/remotion-markup/remotion-maps/techniques/static-map/TECHNIQUE.md +37 -0
- package/skills/remotion-markup/sequencing.md +169 -0
- package/skills/remotion-markup/sfx.md +51 -0
- package/skills/remotion-markup/silence-detection.md +71 -0
- package/skills/remotion-markup/text-highlights.md +56 -0
- package/skills/remotion-markup/timing.md +111 -0
- package/skills/remotion-markup/transitions.md +239 -0
- package/skills/remotion-markup/video-editing.md +90 -0
- package/skills/remotion-markup/voiceover.md +99 -0
- package/skills/remotion-multimedia/SKILL.md +20 -0
- package/skills/remotion-multimedia/agents/openai.yaml +7 -0
- package/skills/remotion-multimedia/assets/remotion-icon.png +0 -0
- package/skills/remotion-multimedia/get-audio-duration.md +56 -0
- package/skills/remotion-multimedia/get-video-dimensions.md +66 -0
- package/skills/remotion-multimedia/get-video-duration.md +58 -0
- package/skills/remotion-render/SKILL.md +27 -0
- package/skills/remotion-render/agents/openai.yaml +7 -0
- package/skills/remotion-render/assets/remotion-icon.png +0 -0
- package/skills/remotion-render/transparent-videos.md +106 -0
- package/skills/remotion-saas/SKILL.md +33 -0
- package/skills/remotion-saas/agents/openai.yaml +7 -0
- package/skills/remotion-saas/assets/remotion-icon.png +0 -0
- package/skills/remotion-saas/framework.md +21 -0
- package/skills/remotion-saas/player.md +33 -0
- package/skills/remotion-saas/rendering.md +69 -0
- package/skills/remotion-studio/SKILL.md +24 -0
- package/skills/remotion-studio/agents/openai.yaml +7 -0
- package/skills/remotion-studio/assets/remotion-icon.png +0 -0
- package/skills/remotion-upgrade/SKILL.md +31 -0
- package/skills/remotion-upgrade/agents/openai.yaml +7 -0
- package/skills/remotion-upgrade/assets/remotion-icon.png +0 -0
- package/skills/remotion/SKILL.md +0 -95
- package/skills/remotion/rules/animations.md +0 -31
- package/skills/remotion/rules/assets.md +0 -78
- package/skills/remotion/rules/can-decode.md +0 -81
- package/skills/remotion/rules/charts.md +0 -120
- package/skills/remotion/rules/compositions.md +0 -154
- package/skills/remotion/rules/extract-frames.md +0 -229
- package/skills/remotion/rules/fonts.md +0 -152
- package/skills/remotion/rules/images.md +0 -134
- package/skills/remotion/rules/light-leaks.md +0 -73
- package/skills/remotion/rules/maps.md +0 -412
- package/skills/remotion/rules/sfx.md +0 -30
- package/skills/remotion/rules/subtitles.md +0 -36
- package/skills/remotion/rules/text-animations.md +0 -20
- package/skills/remotion/rules/timing.md +0 -136
- package/skills/remotion/rules/trimming.md +0 -51
- /package/skills/{remotion/rules → remotion-best-practices/remotion-captions}/transcribe-captions.md +0 -0
- /package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/3d.md +0 -0
- /package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/audio-visualization.md +0 -0
- /package/skills/{remotion/rules → remotion-best-practices/remotion-markup}/silence-detection.md +0 -0
- /package/skills/{remotion/rules → remotion-best-practices/remotion-render}/transparent-videos.md +0 -0
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: maps-maplibre
|
|
3
|
+
description: Make deterministic Remotion 2D map animations with MapLibre GL JS and Turf. Use when the user chooses MapLibre for animated routes, map markers, labels, and camera movement.
|
|
4
|
+
metadata:
|
|
5
|
+
tags: map, map animation, maplibre, turf, geojson, route animation
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Use MapLibre GL JS for rendering maps in Remotion. Use Turf for geospatial operations such as great-circle routes, distances, slicing lines, and positions along routes.
|
|
9
|
+
|
|
10
|
+
## Core rules
|
|
11
|
+
|
|
12
|
+
- Prefer `@turf/turf` for geospatial work. Do not hand-roll distance, great-circle, route slicing, or coordinate interpolation unless the user explicitly needs a custom non-geodesic effect.
|
|
13
|
+
- Use GeoJSON sources and MapLibre layers for lines, markers, and labels. Avoid DOM `Marker` elements unless the user specifically asks for HTML markers.
|
|
14
|
+
- Keep the live map camera static by default. Before moving it on every frame, read [moving-map stability](references/render-stability.md). Prefer a fixed map plate for satellite imagery, hillshade, or a modest 2D reframe.
|
|
15
|
+
- Use a live per-frame camera only after rendering a short MP4 and checking for shimmer. This 2D technique does not provide genuine terrain, pitch, bearing, or banking.
|
|
16
|
+
- Disable non-deterministic map behavior: `interactive: false`, `fadeDuration: 0`.
|
|
17
|
+
- Drive animation from `useCurrentFrame()`; do not use CSS transitions or browser-timed animation.
|
|
18
|
+
- Use `delayRender()` / `continueRender()` around map loading and per-frame map updates.
|
|
19
|
+
- Set `preserveDrawingBuffer: true` and render WebGL with `bunx remotion ... --gl=angle`.
|
|
20
|
+
- Before continuing the initial render, add sources/layers, apply the frame-0 camera with `jumpTo()`, then wait for `idle`.
|
|
21
|
+
- Do not add a `mapInstance.remove()` cleanup function; it can interfere with Remotion's render lifecycle.
|
|
22
|
+
- Use standard MapLibre style JSON URLs and layer/source APIs.
|
|
23
|
+
- Do not install `@types/maplibre-gl`; MapLibre ships its own types.
|
|
24
|
+
- Keep required provider attribution visible and verify the current terms of the chosen style and tile providers before rendering.
|
|
25
|
+
- Record the source and effective date of custom or disputed geography.
|
|
26
|
+
- Inspect rendered pixels, not only Studio playback, at every required aspect ratio.
|
|
27
|
+
|
|
28
|
+
Coordinates in MapLibre, Turf, and GeoJSON are `[longitude, latitude]`.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
const zurich: [number, number] = [8.5417, 47.3769];
|
|
32
|
+
const newYork: [number, number] = [-74.006, 40.7128];
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Prerequisites
|
|
36
|
+
|
|
37
|
+
Install MapLibre and Turf with the project's package manager.
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm i maplibre-gl @turf/turf
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bun i maplibre-gl @turf/turf
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
yarn add maplibre-gl @turf/turf
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pnpm i maplibre-gl @turf/turf
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Import the MapLibre CSS once in the component or an app-level stylesheet:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Basic map example
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import {useEffect, useRef, useState} from 'react';
|
|
65
|
+
import {AbsoluteFill, useDelayRender, useVideoConfig} from 'remotion';
|
|
66
|
+
import maplibregl from 'maplibre-gl';
|
|
67
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
68
|
+
|
|
69
|
+
const zurich: [number, number] = [8.5417, 47.3769];
|
|
70
|
+
|
|
71
|
+
export const MyComposition = () => {
|
|
72
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
73
|
+
const {delayRender, continueRender} = useDelayRender();
|
|
74
|
+
const {width, height} = useVideoConfig();
|
|
75
|
+
const [loadingHandle] = useState(() => delayRender('Loading map'));
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
if (!containerRef.current) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const mapInstance = new maplibregl.Map({
|
|
83
|
+
container: containerRef.current,
|
|
84
|
+
style: 'https://demotiles.maplibre.org/style.json',
|
|
85
|
+
center: zurich,
|
|
86
|
+
zoom: 7,
|
|
87
|
+
interactive: false,
|
|
88
|
+
attributionControl: false,
|
|
89
|
+
fadeDuration: 0,
|
|
90
|
+
canvasContextAttributes: {
|
|
91
|
+
preserveDrawingBuffer: true,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
mapInstance.on('load', () => {
|
|
96
|
+
mapInstance.jumpTo({center: zurich, zoom: 7});
|
|
97
|
+
mapInstance.once('idle', () => {
|
|
98
|
+
continueRender(loadingHandle);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}, [continueRender, loadingHandle]);
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<AbsoluteFill>
|
|
105
|
+
<div ref={containerRef} style={{width, height, position: 'absolute'}} />
|
|
106
|
+
</AbsoluteFill>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Animated examples should keep the loaded map in React state and skip per-frame updates until that state is set.
|
|
112
|
+
|
|
113
|
+
## Animated flight route example
|
|
114
|
+
|
|
115
|
+
This example shows the recommended pattern for route animations:
|
|
116
|
+
|
|
117
|
+
- Turf creates the route and markers.
|
|
118
|
+
- Turf slices the route for line reveal animation.
|
|
119
|
+
- The camera has a separate route from the target route.
|
|
120
|
+
- MapLibre's `calculateCameraOptionsFromTo()` is used for camera movement.
|
|
121
|
+
- Frame 0 is prepared before `continueRender()`.
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
import * as turf from '@turf/turf';
|
|
125
|
+
import {useEffect, useRef, useState} from 'react';
|
|
126
|
+
import {
|
|
127
|
+
AbsoluteFill,
|
|
128
|
+
Easing,
|
|
129
|
+
interpolate,
|
|
130
|
+
useCurrentFrame,
|
|
131
|
+
useDelayRender,
|
|
132
|
+
useVideoConfig,
|
|
133
|
+
} from 'remotion';
|
|
134
|
+
import maplibregl, {type GeoJSONSource, type Map} from 'maplibre-gl';
|
|
135
|
+
import 'maplibre-gl/dist/maplibre-gl.css';
|
|
136
|
+
|
|
137
|
+
const zurich: [number, number] = [8.5417, 47.3769];
|
|
138
|
+
const newYork: [number, number] = [-74.006, 40.7128];
|
|
139
|
+
|
|
140
|
+
const greatCircleLine = (from: [number, number], to: [number, number]) => {
|
|
141
|
+
const route = turf.greatCircle(from, to, {npoints: 100});
|
|
142
|
+
|
|
143
|
+
if (route.geometry.type === 'LineString') {
|
|
144
|
+
return turf.lineString(route.geometry.coordinates);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Great-circle routes crossing the antimeridian can become MultiLineString.
|
|
148
|
+
// Keep the example valid by choosing the longest segment.
|
|
149
|
+
const longestSegment = route.geometry.coordinates.reduce((longest, segment) => {
|
|
150
|
+
return segment.length > longest.length ? segment : longest;
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return turf.lineString(longestSegment);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const targetRoute = greatCircleLine(zurich, newYork);
|
|
157
|
+
const targetRouteDistance = turf.length(targetRoute);
|
|
158
|
+
|
|
159
|
+
const cameraRoute = greatCircleLine(zurich, newYork);
|
|
160
|
+
const cameraRouteDistance = turf.length(cameraRoute);
|
|
161
|
+
|
|
162
|
+
const cityMarkers = turf.featureCollection([
|
|
163
|
+
turf.point(zurich, {name: 'Zurich'}),
|
|
164
|
+
turf.point(newYork, {name: 'New York'}),
|
|
165
|
+
]);
|
|
166
|
+
|
|
167
|
+
const clampProgress = (progress: number) => Math.min(1, Math.max(0, progress));
|
|
168
|
+
|
|
169
|
+
const distanceAlong = (totalDistance: number, progress: number) => {
|
|
170
|
+
// Keep the route non-empty at progress 0; Turf can error on zero-length slices.
|
|
171
|
+
return Math.max(0.001, totalDistance * clampProgress(progress));
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const getPartialTargetRoute = (progress: number) => {
|
|
175
|
+
return turf.lineSliceAlong(
|
|
176
|
+
targetRoute,
|
|
177
|
+
0,
|
|
178
|
+
distanceAlong(targetRouteDistance, progress),
|
|
179
|
+
);
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const getCameraOptions = (
|
|
183
|
+
map: Map,
|
|
184
|
+
progress: number,
|
|
185
|
+
cameraAltitudeMeters: number,
|
|
186
|
+
cameraLatitudeOffset: number,
|
|
187
|
+
) => {
|
|
188
|
+
const target = turf.along(
|
|
189
|
+
targetRoute,
|
|
190
|
+
distanceAlong(targetRouteDistance, progress),
|
|
191
|
+
).geometry.coordinates;
|
|
192
|
+
const camera = turf.along(
|
|
193
|
+
cameraRoute,
|
|
194
|
+
distanceAlong(cameraRouteDistance, progress),
|
|
195
|
+
).geometry.coordinates;
|
|
196
|
+
|
|
197
|
+
return map.calculateCameraOptionsFromTo(
|
|
198
|
+
new maplibregl.LngLat(camera[0], camera[1] - cameraLatitudeOffset),
|
|
199
|
+
cameraAltitudeMeters,
|
|
200
|
+
new maplibregl.LngLat(target[0], target[1]),
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export const MyComposition = () => {
|
|
205
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
206
|
+
const frame = useCurrentFrame();
|
|
207
|
+
const {delayRender, continueRender} = useDelayRender();
|
|
208
|
+
const {durationInFrames, height, width} = useVideoConfig();
|
|
209
|
+
const [map, setMap] = useState<Map | null>(null);
|
|
210
|
+
const [loadingHandle] = useState(() => delayRender('Loading MapLibre map'));
|
|
211
|
+
|
|
212
|
+
useEffect(() => {
|
|
213
|
+
if (!containerRef.current) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const mapInstance = new maplibregl.Map({
|
|
218
|
+
container: containerRef.current,
|
|
219
|
+
style: 'https://demotiles.maplibre.org/style.json',
|
|
220
|
+
center: zurich,
|
|
221
|
+
zoom: 7,
|
|
222
|
+
interactive: false,
|
|
223
|
+
attributionControl: false,
|
|
224
|
+
fadeDuration: 0,
|
|
225
|
+
canvasContextAttributes: {
|
|
226
|
+
preserveDrawingBuffer: true,
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
mapInstance.on('load', () => {
|
|
231
|
+
mapInstance.addSource('trace', {
|
|
232
|
+
type: 'geojson',
|
|
233
|
+
data: getPartialTargetRoute(0),
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
mapInstance.addLayer({
|
|
237
|
+
id: 'trace-line',
|
|
238
|
+
type: 'line',
|
|
239
|
+
source: 'trace',
|
|
240
|
+
layout: {
|
|
241
|
+
'line-cap': 'round',
|
|
242
|
+
'line-join': 'round',
|
|
243
|
+
},
|
|
244
|
+
paint: {
|
|
245
|
+
'line-color': '#111111',
|
|
246
|
+
'line-width': 7,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
mapInstance.addSource('city-markers', {
|
|
251
|
+
type: 'geojson',
|
|
252
|
+
data: cityMarkers,
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
mapInstance.addLayer({
|
|
256
|
+
id: 'city-marker-dots',
|
|
257
|
+
type: 'circle',
|
|
258
|
+
source: 'city-markers',
|
|
259
|
+
paint: {
|
|
260
|
+
'circle-color': '#f03b20',
|
|
261
|
+
'circle-radius': 12,
|
|
262
|
+
'circle-stroke-color': '#ffffff',
|
|
263
|
+
'circle-stroke-width': 4,
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
mapInstance.addLayer({
|
|
268
|
+
id: 'city-marker-labels',
|
|
269
|
+
type: 'symbol',
|
|
270
|
+
source: 'city-markers',
|
|
271
|
+
layout: {
|
|
272
|
+
'text-allow-overlap': true,
|
|
273
|
+
'text-anchor': 'top',
|
|
274
|
+
'text-field': ['get', 'name'],
|
|
275
|
+
'text-offset': [0, 0.9],
|
|
276
|
+
'text-size': 28,
|
|
277
|
+
},
|
|
278
|
+
paint: {
|
|
279
|
+
'text-color': '#111111',
|
|
280
|
+
'text-halo-color': '#ffffff',
|
|
281
|
+
'text-halo-width': 3,
|
|
282
|
+
},
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
mapInstance.jumpTo(getCameraOptions(mapInstance, 0, 180000, 1.1));
|
|
286
|
+
mapInstance.once('idle', () => {
|
|
287
|
+
setMap(mapInstance);
|
|
288
|
+
continueRender(loadingHandle);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
}, [continueRender, loadingHandle]);
|
|
292
|
+
|
|
293
|
+
useEffect(() => {
|
|
294
|
+
if (!map) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const handle = delayRender('Rendering MapLibre frame');
|
|
299
|
+
const timelineProgress = interpolate(frame, [0, durationInFrames - 1], [0, 1], {
|
|
300
|
+
extrapolateLeft: 'clamp',
|
|
301
|
+
extrapolateRight: 'clamp',
|
|
302
|
+
});
|
|
303
|
+
const travelProgress = interpolate(timelineProgress, [0.2, 0.82], [0, 1], {
|
|
304
|
+
extrapolateLeft: 'clamp',
|
|
305
|
+
extrapolateRight: 'clamp',
|
|
306
|
+
easing: Easing.bezier(0.645, 0.045, 0.355, 1),
|
|
307
|
+
});
|
|
308
|
+
const cameraAltitudeMeters = interpolate(
|
|
309
|
+
timelineProgress,
|
|
310
|
+
[0, 0.28, 0.74, 1],
|
|
311
|
+
[180000, 2200000, 2200000, 180000],
|
|
312
|
+
{
|
|
313
|
+
extrapolateLeft: 'clamp',
|
|
314
|
+
extrapolateRight: 'clamp',
|
|
315
|
+
easing: Easing.bezier(0.645, 0.045, 0.355, 1),
|
|
316
|
+
},
|
|
317
|
+
);
|
|
318
|
+
const cameraLatitudeOffset = interpolate(
|
|
319
|
+
timelineProgress,
|
|
320
|
+
[0, 0.28, 0.74, 1],
|
|
321
|
+
[1.1, 8, 8, 1.1],
|
|
322
|
+
{
|
|
323
|
+
extrapolateLeft: 'clamp',
|
|
324
|
+
extrapolateRight: 'clamp',
|
|
325
|
+
easing: Easing.bezier(0.645, 0.045, 0.355, 1),
|
|
326
|
+
},
|
|
327
|
+
);
|
|
328
|
+
const trace = map.getSource('trace') as GeoJSONSource | undefined;
|
|
329
|
+
|
|
330
|
+
trace?.setData(getPartialTargetRoute(travelProgress));
|
|
331
|
+
map.jumpTo(
|
|
332
|
+
getCameraOptions(
|
|
333
|
+
map,
|
|
334
|
+
travelProgress,
|
|
335
|
+
cameraAltitudeMeters,
|
|
336
|
+
cameraLatitudeOffset,
|
|
337
|
+
),
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
map.once('idle', () => continueRender(handle));
|
|
341
|
+
// Force an idle event even if the camera parameters are unchanged from the previous frame.
|
|
342
|
+
map.triggerRepaint();
|
|
343
|
+
}, [continueRender, delayRender, durationInFrames, frame, map]);
|
|
344
|
+
|
|
345
|
+
return (
|
|
346
|
+
<AbsoluteFill style={{backgroundColor: '#e8eef3'}}>
|
|
347
|
+
<div ref={containerRef} style={{height, position: 'absolute', width}} />
|
|
348
|
+
</AbsoluteFill>
|
|
349
|
+
);
|
|
350
|
+
};
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Camera guidance
|
|
354
|
+
|
|
355
|
+
Use MapLibre's camera helper for camera movement:
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
map.calculateCameraOptionsFromTo(cameraLngLat, cameraAltitudeMeters, targetLngLat);
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
A good pattern is to keep two concepts separate:
|
|
362
|
+
|
|
363
|
+
- `targetRoute`: where the animated line is and where the camera looks.
|
|
364
|
+
- `cameraRoute`: where the camera moves.
|
|
365
|
+
|
|
366
|
+
Then use Turf to read positions from both routes for the same progress value:
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
const target = turf.along(targetRoute, targetDistance * progress).geometry.coordinates;
|
|
370
|
+
const camera = turf.along(cameraRoute, cameraDistance * progress).geometry.coordinates;
|
|
371
|
+
|
|
372
|
+
map.jumpTo(
|
|
373
|
+
map.calculateCameraOptionsFromTo(
|
|
374
|
+
new maplibregl.LngLat(camera[0], camera[1]),
|
|
375
|
+
cameraAltitudeMeters,
|
|
376
|
+
new maplibregl.LngLat(target[0], target[1]),
|
|
377
|
+
),
|
|
378
|
+
);
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
For zoom-out / travel / zoom-in animations, animate travel progress separately from camera altitude. Camera altitude is measured in meters. This avoids heavy custom camera math.
|
|
382
|
+
|
|
383
|
+
## Lines
|
|
384
|
+
|
|
385
|
+
Use GeoJSON sources for lines. Unless the user asks, do not add glow effects or extra decorative points.
|
|
386
|
+
|
|
387
|
+
For geodesic flight routes, use Turf:
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
const line = greatCircleLine(start, end);
|
|
391
|
+
const distance = turf.length(line);
|
|
392
|
+
const partialLine = turf.lineSliceAlong(
|
|
393
|
+
line,
|
|
394
|
+
0,
|
|
395
|
+
// Keep the route non-empty at progress 0.
|
|
396
|
+
Math.max(0.001, distance * progress),
|
|
397
|
+
);
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
For a visually straight line on the map, use a simple GeoJSON `LineString` between the two points instead of `greatCircle()`.
|
|
401
|
+
|
|
402
|
+
## Markers and labels
|
|
403
|
+
|
|
404
|
+
Use map-native GeoJSON layers for markers and labels:
|
|
405
|
+
|
|
406
|
+
```tsx
|
|
407
|
+
mapInstance.addSource('markers', {
|
|
408
|
+
type: 'geojson',
|
|
409
|
+
data: turf.featureCollection([
|
|
410
|
+
turf.point([-118.2437, 34.0522], {name: 'Los Angeles'}),
|
|
411
|
+
]),
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
mapInstance.addLayer({
|
|
415
|
+
id: 'marker-dots',
|
|
416
|
+
type: 'circle',
|
|
417
|
+
source: 'markers',
|
|
418
|
+
paint: {
|
|
419
|
+
'circle-color': '#f03b20',
|
|
420
|
+
'circle-radius': 12,
|
|
421
|
+
'circle-stroke-color': '#ffffff',
|
|
422
|
+
'circle-stroke-width': 4,
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
mapInstance.addLayer({
|
|
427
|
+
id: 'marker-labels',
|
|
428
|
+
type: 'symbol',
|
|
429
|
+
source: 'markers',
|
|
430
|
+
layout: {
|
|
431
|
+
'text-allow-overlap': true,
|
|
432
|
+
'text-anchor': 'top',
|
|
433
|
+
'text-field': ['get', 'name'],
|
|
434
|
+
'text-offset': [0, 0.9],
|
|
435
|
+
'text-size': 28,
|
|
436
|
+
},
|
|
437
|
+
paint: {
|
|
438
|
+
'text-color': '#111111',
|
|
439
|
+
'text-halo-color': '#ffffff',
|
|
440
|
+
'text-halo-width': 3,
|
|
441
|
+
},
|
|
442
|
+
});
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Make marker sizes and label font sizes large enough for the composition resolution.
|
|
446
|
+
|
|
447
|
+
## Styles
|
|
448
|
+
|
|
449
|
+
Default to the stock MapLibre demo style:
|
|
450
|
+
|
|
451
|
+
```ts
|
|
452
|
+
style: 'https://demotiles.maplibre.org/style.json'
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
If the user requests another style, use any valid MapLibre style JSON URL.
|
|
456
|
+
|
|
457
|
+
## Rendering
|
|
458
|
+
|
|
459
|
+
For WebGL map renders, prefer single concurrency and ANGLE:
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
bunx remotion render [composition-id] out/video.mp4 --gl=angle --concurrency=1
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Use the equivalent package runner for the project. In npm projects, use `npx`; in Bun projects, use `bunx`.
|
package/skills/remotion-markup/remotion-maps/techniques/maplibre/references/render-stability.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Moving Map Render Stability
|
|
2
|
+
|
|
3
|
+
Read this reference before building a moving 2D MapTiler scene or diagnosing a wavering Remotion render.
|
|
4
|
+
|
|
5
|
+
## Symptom and cause
|
|
6
|
+
|
|
7
|
+
If basemap detail shimmers or jitters during a pan/zoom, the likely cause is per-frame `map.jumpTo()`.
|
|
8
|
+
Headless MapTiler capture can resample both vector hillshade and satellite imagery differently frame to
|
|
9
|
+
frame. Tile retries, easing changes, and label changes do not solve that renderer effect.
|
|
10
|
+
|
|
11
|
+
## Required pattern: fixed map plate
|
|
12
|
+
|
|
13
|
+
For any 2D pan/zoom:
|
|
14
|
+
|
|
15
|
+
1. Render the MapTiler canvas once at the largest required zoom in an oversized container. Size it from the camera route and keep each dimension below the browser's reliable WebGL render-buffer limit (commonly 4096 px). Do **not** blindly use 3×: a 1920×1080 composition becomes 5760 px wide and Chromium may silently downsample it, causing visible pixelation during the CSS zoom.
|
|
16
|
+
2. Keep the map's camera static.
|
|
17
|
+
3. For each frame, calculate the approved target centre/zoom, then move the canvas with CSS `translate` + `scale`.
|
|
18
|
+
4. Apply the same transform to every projected HTML overlay.
|
|
19
|
+
5. Continue to animate GeoJSON data and paint properties imperatively; only the renderer camera is frozen.
|
|
20
|
+
|
|
21
|
+
Keep pitch and bearing constant. Use a 3D engine such as Cesium for genuine changing pitch/bearing or a terrain flythrough.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const baseZoom = Math.max(start.zoom, end.zoom);
|
|
25
|
+
const map = new maptilersdk.Map({
|
|
26
|
+
container,
|
|
27
|
+
style,
|
|
28
|
+
center: end.center,
|
|
29
|
+
zoom: baseZoom,
|
|
30
|
+
pitch: end.pitch ?? 0,
|
|
31
|
+
bearing: end.bearing ?? 0,
|
|
32
|
+
interactive: false,
|
|
33
|
+
fadeDuration: 0,
|
|
34
|
+
canvasContextAttributes: {preserveDrawingBuffer: true},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// Per Remotion frame. `camera` is the approved centre/zoom interpolation.
|
|
38
|
+
const projected = map.project(camera.center);
|
|
39
|
+
const scale = 2 ** (camera.zoom - baseZoom);
|
|
40
|
+
const plate = {
|
|
41
|
+
transform: `translate(${width / 2 - projected.x * scale}px, ${height / 2 - projected.y * scale}px) scale(${scale})`,
|
|
42
|
+
transformOrigin: "0 0",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Convert label projection with exactly the same plate transform.
|
|
46
|
+
const labelX = labelPoint.x * scale + width / 2 - projected.x * scale;
|
|
47
|
+
const labelY = labelPoint.y * scale + height / 2 - projected.y * scale;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Plate sizing and sharpness
|
|
51
|
+
|
|
52
|
+
- Render at the maximum zoom reached by **any** camera waypoint, including intermediate or hold cameras. The CSS scale should never exceed `1`; otherwise the plate is being enlarged.
|
|
53
|
+
- Centre the frozen map on the midpoint of the camera route's geographic extent, not automatically on the final camera. This minimizes required overscan.
|
|
54
|
+
- Keep the largest canvas dimension at or below 4096 px unless the actual render environment has been tested with a larger `MAX_RENDERBUFFER_SIZE`.
|
|
55
|
+
- For 1920×1080, a 3840×2160 plate is a safe default. For 1080×1920, use approximately 2700×3840 when the route needs extra horizontal pan room.
|
|
56
|
+
- If the route cannot fit within that plate at the required zoom, split the shot into two fixed plates with a deliberate editorial transition. Do not trade sharpness for one enormous canvas.
|
|
57
|
+
- Distinguish failure modes: repeating shimmer means the live renderer is moving; steadily soft tiles during a CSS push means the fixed plate is underspecified, internally downsampled, or being scaled above `1`.
|
|
58
|
+
|
|
59
|
+
## Verification
|
|
60
|
+
|
|
61
|
+
- Render a short MP4, not only a Studio preview.
|
|
62
|
+
- Inspect static terrain texture and satellite detail while the camera moves.
|
|
63
|
+
- If any underlying map detail wavers, use the fixed map plate. Do not approve it as a minor preview artefact.
|
|
64
|
+
- Render WebGL with `--gl=angle`, `preserveDrawingBuffer:true`, and conservative concurrency (`1`) while validating.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# MapTiler maps in Remotion
|
|
2
|
+
|
|
3
|
+
MapTiler is a good solution for map animations where geographics features should be drawn as annotations on top of the map: Country borders, rivers, labels for POIs.
|
|
4
|
+
|
|
5
|
+
## MapTiler SDK (`@maptiler/sdk`)
|
|
6
|
+
|
|
7
|
+
Draw the basemap plus MapTiler Planet vector layers and custom GeoJSON into a WebGL canvas. Default styled-vector starting point: `MapStyle.BASIC`; satellite is an equally valid choice.
|
|
8
|
+
|
|
9
|
+
## Remotion
|
|
10
|
+
|
|
11
|
+
Imperatively update `setData`/`setPaintProperty`.
|
|
12
|
+
|
|
13
|
+
Use `jumpTo` only for a static shot, or a fixed map plate for any pan/zoom.
|
|
14
|
+
Gate with [`delayRender`](https://www.remotion.dev/docs/delay-render.md) until `map.once('idle')`.
|
|
15
|
+
|
|
16
|
+
Use `preserveDrawingBuffer:true`.
|
|
17
|
+
|
|
18
|
+
Render labels as positioned [`<Interactive.Div>`](https://www.remotion.dev/docs/interactive.md) elements.
|
|
19
|
+
|
|
20
|
+
Env `REMOTION_MAPTILER_KEY` (unrestricted). Init the map once (ref guard); update imperatively per frame.
|
|
21
|
+
|
|
22
|
+
When constructing MapLibre/MapTiler layer objects, omit optional properties that are absent.
|
|
23
|
+
In particular, use `...(layer.filter ? {filter: layer.filter} : {})`; do not pass `filter: undefined`. An undefined filter can suppress the layer while separately created halo or border layers continue rendering, producing missing country fills and dark marker halos with no coloured cores.
|
|
24
|
+
|
|
25
|
+
Drive animation from `useCurrentFrame()` rather than CSS transitions or browser timers.
|
|
26
|
+
|
|
27
|
+
## Choose the source for each map element
|
|
28
|
+
|
|
29
|
+
Do not begin by manufacturing GeoJSON. First check whether MapTiler Planet already exposes the element as filtered vector data.
|
|
30
|
+
|
|
31
|
+
### MapTiler vector
|
|
32
|
+
|
|
33
|
+
Use it when the feature exists in a provider `source-layer`, its attributes support an exact filter, and provider geometry is editorially acceptable.
|
|
34
|
+
|
|
35
|
+
### Hybrid
|
|
36
|
+
|
|
37
|
+
Use it when ordinary geographic context can come from MapTiler while the claim depends on custom
|
|
38
|
+
evidence.
|
|
39
|
+
|
|
40
|
+
Animate each layer according to its source and meaning.
|
|
41
|
+
|
|
42
|
+
MapTiler vector features remain split across tiles. Do not use them for a semantic start-to-end line draw; extract, verify, order, and bake that element to GeoJSON first. Read **`references/map-data-sources.md`** and reuse **`assets/MapTilerVectorElement.ts`** for provider-layer setup and per-frame paint updates.
|
|
43
|
+
|
|
44
|
+
## Motion stability
|
|
45
|
+
|
|
46
|
+
**Do not call `map.jumpTo()` on every Remotion frame when the camera moves.** In headless capture it can make both MapTiler hillshade **and satellite imagery** shimmer/jitter, even when the source tiles load correctly. This is renderer resampling, not a data, network, or label problem.
|
|
47
|
+
|
|
48
|
+
For the implementation, read **`references/render-stability.md`** before building or debugging any moving map. It contains the fixed-map-plate recipe, diagnostics, and render checks.
|
|
49
|
+
|
|
50
|
+
- Use the live MapTiler camera only for a static shot.
|
|
51
|
+
- Keep pitch and bearing constant for a fixed plate. This technique does not implement a genuine changing 3D camera.
|
|
52
|
+
- Verify the moving preview and a short rendered MP4 before approving a beat. If any basemap detail wavers, switch to the fixed-plate pattern; do not try to solve it with tile retries or camera easing.
|
|
53
|
+
|
|
54
|
+
## Drawing rivers
|
|
55
|
+
|
|
56
|
+
Use `turf.lineSliceAlong(line, 0, lineKm*reveal)` to draw rivers.
|
|
57
|
+
|
|
58
|
+
## Source selection
|
|
59
|
+
|
|
60
|
+
Use MapTiler vector layers for suitable provider features and custom GeoJSON for story-specific or ordered geometry. If the beat needs country-entry triggers or a progressive line draw, run `scripts/prep-geo.mjs` to bake `country-meta.json`, `borders.geojson`, and the ordered line. Details → `references/map-data-sources.md` and `references/map-geo-prep.md`.
|
|
61
|
+
|
|
62
|
+
## Keep it minmal
|
|
63
|
+
|
|
64
|
+
Strip clutter on `load`: remove `symbol` layers (place labels) and `/other border/i` (admin-1 inner borders); hide the logo via CSS. Keep country + disputed borders.
|
|
65
|
+
|
|
66
|
+
## Files
|
|
67
|
+
|
|
68
|
+
Use as reference:
|
|
69
|
+
|
|
70
|
+
- `assets/RiverReveal.tsx` — the main component.
|
|
71
|
+
- `assets/MapTilerVectorElement.ts` — filtered MapTiler Planet elements.
|
|
72
|
+
- `assets/CountryLabel.tsx` — reusable example label.
|
|
73
|
+
- `assets/tokens.ts` — example palette and durations.
|
|
74
|
+
- `assets/example-Root.tsx` — minimal composition scaffold.
|
|
75
|
+
- `assets/sample-data/` — example route and generated country metadata.
|
|
76
|
+
- `scripts/prep-geo.mjs` — geo pipeline.
|
|
77
|
+
- `references/map-explainer-architecture.md` — timing model and implementation.
|
|
78
|
+
- `references/map-data-sources.md` — provider vector versus custom GeoJSON selection.
|
|
79
|
+
- `references/map-geo-prep.md` — basemap stripping and geo preparation.
|
|
80
|
+
- `references/render-stability.md` — camera motion and stable headless renders.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {Easing, interpolate} from 'remotion';
|
|
3
|
+
|
|
4
|
+
// Reusable country label. Supply typography and final values from the consuming project; the CSS custom
|
|
5
|
+
// properties below provide neutral fallbacks. Positioned by its centre (x,y in screen px).
|
|
6
|
+
export const CountryLabel: React.FC<{
|
|
7
|
+
name: string;
|
|
8
|
+
color: string;
|
|
9
|
+
reveal: number;
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
}> = ({name, color, reveal, x, y}) => {
|
|
13
|
+
const e = interpolate(reveal, [0, 1], [0, 1], {
|
|
14
|
+
extrapolateLeft: 'clamp',
|
|
15
|
+
extrapolateRight: 'clamp',
|
|
16
|
+
easing: Easing.bezier(0.3333333333333333, 1, 0.6666666666666666, 1),
|
|
17
|
+
});
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
style={{
|
|
21
|
+
position: 'absolute',
|
|
22
|
+
left: x,
|
|
23
|
+
top: y,
|
|
24
|
+
transform: 'translate(-50%, -50%)',
|
|
25
|
+
pointerEvents: 'none',
|
|
26
|
+
display: 'flex',
|
|
27
|
+
flexDirection: 'column',
|
|
28
|
+
alignItems: 'center',
|
|
29
|
+
opacity: e,
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
<div
|
|
33
|
+
style={{
|
|
34
|
+
transform: `translateY(${(1 - e) * 16}px)`,
|
|
35
|
+
display: 'flex',
|
|
36
|
+
flexDirection: 'column',
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
{/* accent rule — draws out from the centre in the country's colour */}
|
|
41
|
+
<div
|
|
42
|
+
style={{
|
|
43
|
+
width: 64,
|
|
44
|
+
height: 3,
|
|
45
|
+
borderRadius: 2,
|
|
46
|
+
background: color,
|
|
47
|
+
transform: `scaleX(${e})`,
|
|
48
|
+
boxShadow: `0 0 10px ${color}`,
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
<div
|
|
52
|
+
style={{
|
|
53
|
+
fontFamily: 'var(--map-label-font, system-ui, sans-serif)',
|
|
54
|
+
fontWeight: 'var(--map-label-weight, 600)',
|
|
55
|
+
fontSize: 'var(--map-label-size, 34px)',
|
|
56
|
+
letterSpacing: 'var(--map-label-tracking, 0.16em)',
|
|
57
|
+
textTransform: 'uppercase',
|
|
58
|
+
color: 'var(--map-label-color, #ffffff)',
|
|
59
|
+
textShadow: 'var(--map-label-shadow, 0 2px 18px rgba(0,0,0,0.9))',
|
|
60
|
+
marginTop: 13,
|
|
61
|
+
paddingLeft: 'var(--map-label-tracking, 0.16em)',
|
|
62
|
+
whiteSpace: 'nowrap',
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
{name}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
};
|