From 79e6186b238e494218522fcebc8769f3d61f0911 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 12 Jun 2024 16:33:48 +0200
Subject: [PATCH 01/36] Start working on captions

---
 packages/core/src/CompositionManager.tsx      |  9 ++++-
 packages/core/src/index.ts                    |  1 +
 packages/core/src/no-react.ts                 |  6 +++-
 .../src/assets/calculate-asset-positions.ts   | 17 +++++++---
 .../src/assets/convert-assets-to-file-urls.ts |  6 ++--
 .../assets/download-and-map-assets-to-file.ts |  6 ++--
 packages/renderer/src/assets/types.ts         | 10 +++---
 packages/renderer/src/compress-assets.ts      |  8 ++---
 packages/renderer/src/filter-asset-types.ts   | 17 ++++++++++
 packages/renderer/src/render-frames.ts        | 33 +++++++++++--------
 .../src/test/asset-calculation.test.tsx       |  7 +++-
 .../src/test/asset-compression.test.ts        |  7 ++--
 .../src/test/dont-skip-assets.test.ts         |  7 +++-
 13 files changed, 94 insertions(+), 40 deletions(-)
 create mode 100644 packages/renderer/src/filter-asset-types.ts

diff --git a/packages/core/src/CompositionManager.tsx b/packages/core/src/CompositionManager.tsx
index bc83c93337a..1381d103b34 100644
--- a/packages/core/src/CompositionManager.tsx
+++ b/packages/core/src/CompositionManager.tsx
@@ -126,7 +126,7 @@ export type TSequence = {
 	premountDisplay: number | null;
 } & EnhancedTSequenceData;
 
-export type TRenderAsset = {
+export type AudioOrVideoAsset = {
 	type: 'audio' | 'video';
 	src: string;
 	id: string;
@@ -139,6 +139,13 @@ export type TRenderAsset = {
 	audioStartFrame: number;
 };
 
+export type ArtifactAsset = {
+	type: 'artifact';
+	id: string;
+};
+
+export type TRenderAsset = AudioOrVideoAsset | ArtifactAsset;
+
 export const compositionsRef = React.createRef<{
 	getCompositions: () => AnyComposition[];
 }>();
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index a083bc3af17..461e2711673 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -103,6 +103,7 @@ export {
 export {
 	AnyCompMetadata,
 	AnyComposition,
+	AudioOrVideoAsset,
 	SmallTCompMetadata,
 	TCompMetadata,
 	TRenderAsset,
diff --git a/packages/core/src/no-react.ts b/packages/core/src/no-react.ts
index 6a81eed547d..4559b5f4f72 100644
--- a/packages/core/src/no-react.ts
+++ b/packages/core/src/no-react.ts
@@ -1,4 +1,8 @@
-export type {TRenderAsset} from './CompositionManager';
+export type {
+	ArtifactAsset,
+	AudioOrVideoAsset,
+	TRenderAsset,
+} from './CompositionManager';
 export type {ClipRegion} from './NativeLayers';
 export {
 	EasingFunction,
diff --git a/packages/renderer/src/assets/calculate-asset-positions.ts b/packages/renderer/src/assets/calculate-asset-positions.ts
index b8f7fa1d3d4..ecadc15a266 100644
--- a/packages/renderer/src/assets/calculate-asset-positions.ts
+++ b/packages/renderer/src/assets/calculate-asset-positions.ts
@@ -1,4 +1,5 @@
-import type {TRenderAsset} from 'remotion/no-react';
+import type {AudioOrVideoAsset, TRenderAsset} from 'remotion/no-react';
+import {onlyAudioAndVideoAssets} from '../filter-asset-types';
 import {resolveAssetSrc} from '../resolve-asset-src';
 import {convertAssetToFlattenedVolume} from './flatten-volume-array';
 import type {Assets, MediaAsset, UnsafeAsset} from './types';
@@ -18,9 +19,13 @@ const findFrom = (target: TRenderAsset[], renderAsset: TRenderAsset) => {
 	return true;
 };
 
-const copyAndDeduplicateAssets = (renderAssets: TRenderAsset[]) => {
-	const deduplicated: TRenderAsset[] = [];
-	for (const renderAsset of renderAssets) {
+const copyAndDeduplicateAssets = (
+	renderAssets: TRenderAsset[],
+): AudioOrVideoAsset[] => {
+	const onlyAudioAndVideo = onlyAudioAndVideoAssets(renderAssets);
+	const deduplicated: AudioOrVideoAsset[] = [];
+
+	for (const renderAsset of onlyAudioAndVideo) {
 		if (!deduplicated.find((d) => d.id === renderAsset.id)) {
 			deduplicated.push(renderAsset);
 		}
@@ -29,7 +34,9 @@ const copyAndDeduplicateAssets = (renderAssets: TRenderAsset[]) => {
 	return deduplicated;
 };
 
-export const calculateAssetPositions = (frames: TRenderAsset[][]): Assets => {
+export const calculateAssetPositions = (
+	frames: AudioOrVideoAsset[][],
+): Assets => {
 	const assets: UnsafeAsset[] = [];
 
 	for (let frame = 0; frame < frames.length; frame++) {
diff --git a/packages/renderer/src/assets/convert-assets-to-file-urls.ts b/packages/renderer/src/assets/convert-assets-to-file-urls.ts
index 1d75a95bec1..3f90b22ac82 100644
--- a/packages/renderer/src/assets/convert-assets-to-file-urls.ts
+++ b/packages/renderer/src/assets/convert-assets-to-file-urls.ts
@@ -1,4 +1,4 @@
-import type {TRenderAsset} from 'remotion/no-react';
+import type {AudioOrVideoAsset} from 'remotion/no-react';
 import type {LogLevel} from '../log-level';
 import type {FrameAndAssets} from '../render-frames';
 import type {RenderMediaOnDownload} from './download-and-map-assets-to-file';
@@ -25,9 +25,9 @@ export const convertAssetsToFileUrls = async ({
 	downloadMap: DownloadMap;
 	indent: boolean;
 	logLevel: LogLevel;
-}): Promise<TRenderAsset[][]> => {
+}): Promise<AudioOrVideoAsset[][]> => {
 	const chunks = chunk(assets, 1000);
-	const results: TRenderAsset[][][] = [];
+	const results: AudioOrVideoAsset[][][] = [];
 
 	for (const ch of chunks) {
 		const assetPromises = ch.map((frame) => {
diff --git a/packages/renderer/src/assets/download-and-map-assets-to-file.ts b/packages/renderer/src/assets/download-and-map-assets-to-file.ts
index a34fa32217a..db8c3ebaa11 100644
--- a/packages/renderer/src/assets/download-and-map-assets-to-file.ts
+++ b/packages/renderer/src/assets/download-and-map-assets-to-file.ts
@@ -1,6 +1,6 @@
 import fs from 'node:fs';
 import path, {extname} from 'node:path';
-import type {TRenderAsset} from 'remotion/no-react';
+import type {AudioOrVideoAsset} from 'remotion/no-react';
 import {random} from 'remotion/no-react';
 import {isAssetCompressed} from '../compress-assets';
 import {ensureOutputDirectory} from '../ensure-output-directory';
@@ -350,12 +350,12 @@ export const downloadAndMapAssetsToFileUrl = async ({
 	logLevel,
 	indent,
 }: {
-	renderAsset: TRenderAsset;
+	renderAsset: AudioOrVideoAsset;
 	onDownload: RenderMediaOnDownload | null;
 	downloadMap: DownloadMap;
 	logLevel: LogLevel;
 	indent: boolean;
-}): Promise<TRenderAsset> => {
+}): Promise<AudioOrVideoAsset> => {
 	const cleanup = attachDownloadListenerToEmitter(downloadMap, onDownload);
 	const newSrc = await downloadAsset({
 		src: renderAsset.src,
diff --git a/packages/renderer/src/assets/types.ts b/packages/renderer/src/assets/types.ts
index 22624f43918..f22289f6aa6 100644
--- a/packages/renderer/src/assets/types.ts
+++ b/packages/renderer/src/assets/types.ts
@@ -1,10 +1,10 @@
-import type {TRenderAsset} from 'remotion/no-react';
+import type {AudioOrVideoAsset} from 'remotion/no-react';
 
 // An unsafe asset is an asset with looser types, which occurs
 // during construction of the asset list. Prefer the MediaAsset
 // type instead.
 export type UnsafeAsset = Omit<
-	TRenderAsset,
+	AudioOrVideoAsset,
 	'frame' | 'id' | 'volume' | 'mediaFrame' | 'audioStartFrom'
 > & {
 	startInVideo: number;
@@ -27,9 +27,9 @@ export type MediaAsset = Omit<UnsafeAsset, 'duration' | 'volume'> & {
 };
 
 export const uncompressMediaAsset = (
-	allRenderAssets: TRenderAsset[],
-	assetToUncompress: TRenderAsset,
-): TRenderAsset => {
+	allRenderAssets: AudioOrVideoAsset[],
+	assetToUncompress: AudioOrVideoAsset,
+): AudioOrVideoAsset => {
 	const isCompressed = assetToUncompress.src.match(/same-as-(.*)-([0-9]+)$/);
 	if (!isCompressed) {
 		return assetToUncompress;
diff --git a/packages/renderer/src/compress-assets.ts b/packages/renderer/src/compress-assets.ts
index b0d6c28aaf0..d2ce0fe50d3 100644
--- a/packages/renderer/src/compress-assets.ts
+++ b/packages/renderer/src/compress-assets.ts
@@ -3,12 +3,12 @@
  * Since we track the `src` property for every frame, Node.JS can run out of memory easily. Instead of duplicating the src for every frame, we save memory by replacing the full base 64 encoded data with a string `same-as-[asset-id]-[frame]` referencing a previous asset with the same src.
  */
 
-import type {TRenderAsset} from 'remotion/no-react';
+import type {AudioOrVideoAsset} from 'remotion/no-react';
 
 export const compressAsset = (
-	previousRenderAssets: TRenderAsset[],
-	newRenderAsset: TRenderAsset,
-): TRenderAsset => {
+	previousRenderAssets: AudioOrVideoAsset[],
+	newRenderAsset: AudioOrVideoAsset,
+): AudioOrVideoAsset => {
 	if (newRenderAsset.src.length < 400) {
 		return newRenderAsset;
 	}
diff --git a/packages/renderer/src/filter-asset-types.ts b/packages/renderer/src/filter-asset-types.ts
new file mode 100644
index 00000000000..b7fe68b32e8
--- /dev/null
+++ b/packages/renderer/src/filter-asset-types.ts
@@ -0,0 +1,17 @@
+import type {
+	ArtifactAsset,
+	AudioOrVideoAsset,
+	TRenderAsset,
+} from 'remotion/no-react';
+
+export const onlyAudioAndVideoAssets = (
+	assets: TRenderAsset[],
+): AudioOrVideoAsset[] => {
+	return assets.filter(
+		(asset) => asset.type === 'audio' || asset.type === 'video',
+	) as AudioOrVideoAsset[];
+};
+
+export const onlyArtifact = (assets: TRenderAsset[]): ArtifactAsset[] => {
+	return assets.filter((asset) => asset.type === 'artifact') as ArtifactAsset[];
+};
diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts
index b1885eee45b..d29f1f06664 100644
--- a/packages/renderer/src/render-frames.ts
+++ b/packages/renderer/src/render-frames.ts
@@ -2,8 +2,7 @@ import fs from 'node:fs';
 import path from 'node:path';
 import {performance} from 'perf_hooks';
 // eslint-disable-next-line no-restricted-imports
-import type {TAsset} from 'remotion';
-import type {VideoConfig} from 'remotion/no-react';
+import type {AudioOrVideoAsset, VideoConfig} from 'remotion/no-react';
 import {NoReactInternals} from 'remotion/no-react';
 import type {RenderMediaOnDownload} from './assets/download-and-map-assets-to-file';
 import {downloadAndMapAssetsToFileUrl} from './assets/download-and-map-assets-to-file';
@@ -23,6 +22,7 @@ import type {Compositor} from './compositor/compositor';
 import {compressAsset} from './compress-assets';
 import {cycleBrowserTabs} from './cycle-browser-tabs';
 import {handleJavascriptException} from './error-handling/handle-javascript-exception';
+import {onlyArtifact, onlyAudioAndVideoAssets} from './filter-asset-types';
 import {findRemotionRoot} from './find-closest-package-json';
 import type {FrameRange} from './frame-range';
 import {getActualConcurrency} from './get-concurrency';
@@ -144,7 +144,7 @@ type InnerRenderFramesOptions = {
 
 export type FrameAndAssets = {
 	frame: number;
-	assets: TAsset[];
+	assets: AudioOrVideoAsset[];
 };
 
 export type RenderFramesOptions = {
@@ -488,20 +488,24 @@ const innerRenderFrames = async ({
 
 		stopPerfMeasure(id);
 
-		const compressedAssets = collectedAssets.map((asset) =>
-			compressAsset(
-				assets
-					.filter(truthy)
-					.map((a) => a.assets)
-					.flat(2),
-				asset,
-			),
-		);
+		const previousRenderAssets = assets
+			.filter(truthy)
+			.map((a) => a.assets)
+			.flat(2);
+
+		const audioAndVideoAssets = onlyAudioAndVideoAssets(collectedAssets);
+		const artifactAssets = onlyArtifact(collectedAssets);
+		console.log(artifactAssets);
+
+		const compressedAssets = audioAndVideoAssets.map((asset) => {
+			return compressAsset(previousRenderAssets, asset);
+		});
+
 		assets.push({
 			assets: compressedAssets,
 			frame,
 		});
-		compressedAssets.forEach((renderAsset) => {
+		for (const renderAsset of compressedAssets) {
 			downloadAndMapAssetsToFileUrl({
 				renderAsset,
 				onDownload,
@@ -518,7 +522,8 @@ const innerRenderFrames = async ({
 					),
 				);
 			});
-		});
+		}
+
 		if (!assetsOnly) {
 			framesRendered++;
 			onFrameUpdate?.(framesRendered, frame, performance.now() - startTime);
diff --git a/packages/renderer/src/test/asset-calculation.test.tsx b/packages/renderer/src/test/asset-calculation.test.tsx
index d1bfc76a42d..1f40f9f33cb 100644
--- a/packages/renderer/src/test/asset-calculation.test.tsx
+++ b/packages/renderer/src/test/asset-calculation.test.tsx
@@ -7,6 +7,7 @@ import {Audio, interpolate, Sequence, useCurrentFrame, Video} from 'remotion';
 import {expect, test} from 'vitest';
 import {calculateAssetPositions} from '../assets/calculate-asset-positions';
 import type {MediaAsset} from '../assets/types';
+import {onlyAudioAndVideoAssets} from '../filter-asset-types';
 import {getAssetsForMarkup} from './get-assets-for-markup';
 
 const basicConfig = {
@@ -19,7 +20,11 @@ const basicConfig = {
 
 const getPositions = async (Markup: React.FC) => {
 	const assets = await getAssetsForMarkup(Markup, basicConfig);
-	return calculateAssetPositions(assets);
+	const onlyAudioAndVideo = assets.map((ass) => {
+		return onlyAudioAndVideoAssets(ass);
+	});
+
+	return calculateAssetPositions(onlyAudioAndVideo);
 };
 
 const withoutId = (asset: MediaAsset) => {
diff --git a/packages/renderer/src/test/asset-compression.test.ts b/packages/renderer/src/test/asset-compression.test.ts
index 36ee4d08fc6..0e22d40d577 100644
--- a/packages/renderer/src/test/asset-compression.test.ts
+++ b/packages/renderer/src/test/asset-compression.test.ts
@@ -2,6 +2,7 @@ import type {TRenderAsset} from 'remotion';
 import {expect, test} from 'vitest';
 import {calculateAssetPositions} from '../assets/calculate-asset-positions';
 import {compressAsset} from '../compress-assets';
+import {onlyAudioAndVideoAssets} from '../filter-asset-types';
 
 test('Should compress and uncompress assets', () => {
 	const uncompressed: TRenderAsset[] = [
@@ -35,8 +36,10 @@ test('Should compress and uncompress assets', () => {
 		],
 	].flat(1);
 
-	const compressedAssets = uncompressed.map((asset, i) => {
-		return compressAsset(uncompressed.slice(0, i), asset);
+	const onlyAudioAndVideo = onlyAudioAndVideoAssets(uncompressed);
+
+	const compressedAssets = onlyAudioAndVideo.map((asset, i) => {
+		return compressAsset(onlyAudioAndVideo.slice(0, i), asset);
 	});
 
 	expect(compressedAssets[0].src).toBe(String('x').repeat(1000));
diff --git a/packages/renderer/src/test/dont-skip-assets.test.ts b/packages/renderer/src/test/dont-skip-assets.test.ts
index 84ac0290bab..3f12d1cbae6 100644
--- a/packages/renderer/src/test/dont-skip-assets.test.ts
+++ b/packages/renderer/src/test/dont-skip-assets.test.ts
@@ -1,6 +1,7 @@
 import type {TRenderAsset} from 'remotion';
 import {expect, test} from 'vitest';
 import {calculateAssetPositions} from '../assets/calculate-asset-positions';
+import {onlyAudioAndVideoAssets} from '../filter-asset-types';
 
 type Truthy<T> = T extends false | '' | 0 | null | undefined ? never : T; // from lodash
 
@@ -9,7 +10,11 @@ function truthy<T>(value: T): value is Truthy<T> {
 }
 
 test('Dont skip assets', () => {
-	const assetPositions = calculateAssetPositions(mock);
+	const onlyAudioAndVideo = mock.map((m) => {
+		return onlyAudioAndVideoAssets(m);
+	});
+
+	const assetPositions = calculateAssetPositions(onlyAudioAndVideo);
 	expect(assetPositions).toEqual([
 		{
 			src: 'http://localhost:3000/4793bac32f610ffba8197b8a3422456f.mp3',

From e769c8204fb4a3798e167891fd368ce0957ac179 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 12 Jun 2024 16:38:37 +0200
Subject: [PATCH 02/36] hmm

---
 .../src/assets/convert-assets-to-file-urls.ts |  2 +-
 packages/renderer/src/render-frames.ts        | 19 ++++++++++++-------
 2 files changed, 13 insertions(+), 8 deletions(-)

diff --git a/packages/renderer/src/assets/convert-assets-to-file-urls.ts b/packages/renderer/src/assets/convert-assets-to-file-urls.ts
index 3f90b22ac82..ef3b6ad1b4a 100644
--- a/packages/renderer/src/assets/convert-assets-to-file-urls.ts
+++ b/packages/renderer/src/assets/convert-assets-to-file-urls.ts
@@ -31,7 +31,7 @@ export const convertAssetsToFileUrls = async ({
 
 	for (const ch of chunks) {
 		const assetPromises = ch.map((frame) => {
-			const frameAssetPromises = frame.assets.map((a) => {
+			const frameAssetPromises = frame.audioAndVideoAssets.map((a) => {
 				return downloadAndMapAssetsToFileUrl({
 					renderAsset: a,
 					onDownload,
diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts
index d29f1f06664..e587592ecb9 100644
--- a/packages/renderer/src/render-frames.ts
+++ b/packages/renderer/src/render-frames.ts
@@ -2,7 +2,11 @@ import fs from 'node:fs';
 import path from 'node:path';
 import {performance} from 'perf_hooks';
 // eslint-disable-next-line no-restricted-imports
-import type {AudioOrVideoAsset, VideoConfig} from 'remotion/no-react';
+import type {
+	ArtifactAsset,
+	AudioOrVideoAsset,
+	VideoConfig,
+} from 'remotion/no-react';
 import {NoReactInternals} from 'remotion/no-react';
 import type {RenderMediaOnDownload} from './assets/download-and-map-assets-to-file';
 import {downloadAndMapAssetsToFileUrl} from './assets/download-and-map-assets-to-file';
@@ -144,7 +148,8 @@ type InnerRenderFramesOptions = {
 
 export type FrameAndAssets = {
 	frame: number;
-	assets: AudioOrVideoAsset[];
+	audioAndVideoAssets: AudioOrVideoAsset[];
+	artifactAssets: ArtifactAsset[];
 };
 
 export type RenderFramesOptions = {
@@ -488,22 +493,22 @@ const innerRenderFrames = async ({
 
 		stopPerfMeasure(id);
 
-		const previousRenderAssets = assets
+		const previousAudioRenderAssets = assets
 			.filter(truthy)
-			.map((a) => a.assets)
+			.map((a) => a.audioAndVideoAssets)
 			.flat(2);
 
 		const audioAndVideoAssets = onlyAudioAndVideoAssets(collectedAssets);
 		const artifactAssets = onlyArtifact(collectedAssets);
-		console.log(artifactAssets);
 
 		const compressedAssets = audioAndVideoAssets.map((asset) => {
-			return compressAsset(previousRenderAssets, asset);
+			return compressAsset(previousAudioRenderAssets, asset);
 		});
 
 		assets.push({
-			assets: compressedAssets,
+			audioAndVideoAssets: compressedAssets,
 			frame,
+			artifactAssets,
 		});
 		for (const renderAsset of compressedAssets) {
 			downloadAndMapAssetsToFileUrl({

From ef5552d490e65916035e73727d95329139da0855 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 12 Jun 2024 16:47:22 +0200
Subject: [PATCH 03/36] Artifact API

---
 packages/core/src/Artifact.tsx           | 32 ++++++++++++++++++++++++
 packages/core/src/CompositionManager.tsx |  2 ++
 packages/core/src/index.ts               |  2 ++
 3 files changed, 36 insertions(+)
 create mode 100644 packages/core/src/Artifact.tsx

diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx
new file mode 100644
index 00000000000..9c38bf60d7f
--- /dev/null
+++ b/packages/core/src/Artifact.tsx
@@ -0,0 +1,32 @@
+import type React from 'react';
+import {useContext, useEffect, useState} from 'react';
+import {RenderAssetManager} from './RenderAssetManager';
+
+// TODO: Not register in development?
+export const Artifact: React.FC<{
+	filename: string;
+	content: string;
+}> = ({filename, content}) => {
+	// TODO: Validate filename and content
+	const {registerRenderAsset, unregisterRenderAsset} =
+		useContext(RenderAssetManager);
+
+	const [id] = useState(() => {
+		return String(Math.random());
+	});
+
+	useEffect(() => {
+		registerRenderAsset({
+			type: 'artifact',
+			id,
+			content,
+			filename,
+		});
+
+		return () => {
+			return unregisterRenderAsset(id);
+		};
+	}, [content, filename, id, registerRenderAsset, unregisterRenderAsset]);
+
+	return null;
+};
diff --git a/packages/core/src/CompositionManager.tsx b/packages/core/src/CompositionManager.tsx
index 1381d103b34..b21e617883a 100644
--- a/packages/core/src/CompositionManager.tsx
+++ b/packages/core/src/CompositionManager.tsx
@@ -142,6 +142,8 @@ export type AudioOrVideoAsset = {
 export type ArtifactAsset = {
 	type: 'artifact';
 	id: string;
+	filename: string;
+	content: string;
 };
 
 export type TRenderAsset = AudioOrVideoAsset | ArtifactAsset;
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index 461e2711673..f52ac894315 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -1,3 +1,4 @@
+import {Artifact} from './Artifact.js';
 import './asset-types.js';
 import {Clipper} from './Clipper.js';
 import type {Codec} from './codec.js';
@@ -170,6 +171,7 @@ export const Experimental = {
 	 * @see [Documentation](https://www.remotion.dev/docs/null)
 	 */
 	Null,
+	Artifact,
 	useIsPlayer,
 };
 

From b95253e2a5f1e1f7091301a3fa3adeeb8989a62c Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 12 Jun 2024 17:07:45 +0200
Subject: [PATCH 04/36] artifact

---
 packages/core/src/Artifact.tsx                | 16 +++++++++++++---
 packages/core/src/CompositionManager.tsx      |  1 +
 packages/example/src/Root.tsx                 | 19 +++++++++++++++----
 .../src/SubtitleArtifact/SubtitleArtifact.tsx |  6 ++++++
 packages/renderer/src/render-frames.ts        | 18 ++++++++++++++++++
 5 files changed, 53 insertions(+), 7 deletions(-)
 create mode 100644 packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx

diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx
index 9c38bf60d7f..b2fd295a435 100644
--- a/packages/core/src/Artifact.tsx
+++ b/packages/core/src/Artifact.tsx
@@ -1,15 +1,17 @@
 import type React from 'react';
 import {useContext, useEffect, useState} from 'react';
 import {RenderAssetManager} from './RenderAssetManager';
+import {useCurrentFrame} from './use-current-frame';
 
 // TODO: Not register in development?
 export const Artifact: React.FC<{
-	filename: string;
-	content: string;
+	readonly filename: string;
+	readonly content: string;
 }> = ({filename, content}) => {
 	// TODO: Validate filename and content
 	const {registerRenderAsset, unregisterRenderAsset} =
 		useContext(RenderAssetManager);
+	const frame = useCurrentFrame();
 
 	const [id] = useState(() => {
 		return String(Math.random());
@@ -21,12 +23,20 @@ export const Artifact: React.FC<{
 			id,
 			content,
 			filename,
+			frame,
 		});
 
 		return () => {
 			return unregisterRenderAsset(id);
 		};
-	}, [content, filename, id, registerRenderAsset, unregisterRenderAsset]);
+	}, [
+		content,
+		filename,
+		frame,
+		id,
+		registerRenderAsset,
+		unregisterRenderAsset,
+	]);
 
 	return null;
 };
diff --git a/packages/core/src/CompositionManager.tsx b/packages/core/src/CompositionManager.tsx
index b21e617883a..7bed4510d9d 100644
--- a/packages/core/src/CompositionManager.tsx
+++ b/packages/core/src/CompositionManager.tsx
@@ -144,6 +144,7 @@ export type ArtifactAsset = {
 	id: string;
 	filename: string;
 	content: string;
+	frame: number;
 };
 
 export type TRenderAsset = AudioOrVideoAsset | ArtifactAsset;
diff --git a/packages/example/src/Root.tsx b/packages/example/src/Root.tsx
index 20e943ef7f0..ee5ed32611f 100644
--- a/packages/example/src/Root.tsx
+++ b/packages/example/src/Root.tsx
@@ -4,9 +4,9 @@ import {
 	CalculateMetadataFunction,
 	Composition,
 	Folder,
-	Still,
 	getInputProps,
 	staticFile,
+	Still,
 } from 'remotion';
 import {z} from 'zod';
 import {TwentyTwoKHzAudio} from './22KhzAudio';
@@ -32,8 +32,8 @@ import {Layers} from './Layers';
 import {ManyAudio} from './ManyAudio';
 import {MissingImg} from './MissingImg';
 import {
-	OffthreadRemoteVideo,
 	calculateMetadataFn,
+	OffthreadRemoteVideo,
 } from './OffthreadRemoteVideo/OffthreadRemoteVideo';
 import {OrbScene} from './Orb';
 import {ShapesMorph} from './Paths/ShapesMorph';
@@ -47,8 +47,8 @@ import RiveVehicle from './Rive/RiveExample';
 import {ScalePath} from './ScalePath';
 import {
 	ArrayTest,
-	SchemaTest,
 	schemaArrayTestSchema,
+	SchemaTest,
 	schemaTestSchema,
 } from './SchemaTest';
 import {Scripts} from './Scripts';
@@ -74,6 +74,7 @@ import {
 } from './StudioApis/SaveDefaultProps';
 import {TriggerCalculateMetadata} from './StudioApis/TriggerCalculateMetadata';
 import {WriteStaticFile} from './StudioApis/WriteStaticFile';
+import './style.css';
 import {Tailwind} from './Tailwind';
 import {TenFrameTester} from './TenFrameTester';
 import {TextStroke} from './TextStroke';
@@ -90,7 +91,6 @@ import {VideoSpeed} from './VideoSpeed';
 import {VideoTesting} from './VideoTesting';
 import {WarpDemoOuter} from './WarpText';
 import {WarpDemo2} from './WarpText/demo2';
-import './style.css';
 import {WatchStaticDemo} from './watch-static';
 if (alias !== 'alias') {
 	throw new Error('should support TS aliases');
@@ -98,6 +98,7 @@ if (alias !== 'alias') {
 
 // @ts-expect-error no types
 import styles from './styles.module.scss';
+import {SubtitleArtifact} from './SubtitleArtifact/SubtitleArtifact';
 
 if (!styles.hithere) {
 	throw new Error('should support SCSS modules');
@@ -1309,6 +1310,16 @@ export const Index: React.FC = () => {
 					defaultProps={{color: 'green'}}
 				/>
 			</Folder>
+			<Folder name="Artifacts">
+				<Composition
+					id="subtitle"
+					component={SubtitleArtifact}
+					fps={30}
+					height={1000}
+					width={1000}
+					durationInFrames={10}
+				/>
+			</Folder>
 		</>
 	);
 };
diff --git a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx
new file mode 100644
index 00000000000..56e892fdcfb
--- /dev/null
+++ b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx
@@ -0,0 +1,6 @@
+import React from 'react';
+import {Experimental} from 'remotion';
+
+export const SubtitleArtifact: React.FC = () => {
+	return <Experimental.Artifact content="Hello World!" filename="hello.txt" />;
+};
diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts
index e587592ecb9..34c5746f487 100644
--- a/packages/renderer/src/render-frames.ts
+++ b/packages/renderer/src/render-frames.ts
@@ -498,9 +498,27 @@ const innerRenderFrames = async ({
 			.map((a) => a.audioAndVideoAssets)
 			.flat(2);
 
+		const previousArtifactAssets = assets
+			.filter(truthy)
+			.map((a) => a.artifactAssets)
+			.flat(2);
+
 		const audioAndVideoAssets = onlyAudioAndVideoAssets(collectedAssets);
 		const artifactAssets = onlyArtifact(collectedAssets);
 
+		for (const artifact of artifactAssets) {
+			for (const previousArtifact of previousArtifactAssets) {
+				if (artifact.filename === previousArtifact.filename) {
+					reject(
+						new Error(
+							`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}.`,
+						),
+					);
+					return;
+				}
+			}
+		}
+
 		const compressedAssets = audioAndVideoAssets.map((asset) => {
 			return compressAsset(previousAudioRenderAssets, asset);
 		});

From 8b0550b1d3d82158f3a9ee9a3bbecc3b3d8e5ca8 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 12 Jun 2024 17:10:13 +0200
Subject: [PATCH 05/36] Update SubtitleArtifact.tsx

---
 .../example/src/SubtitleArtifact/SubtitleArtifact.tsx     | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx
index 56e892fdcfb..fa36b1dc18a 100644
--- a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx
+++ b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx
@@ -1,6 +1,10 @@
 import React from 'react';
-import {Experimental} from 'remotion';
+import {Experimental, useCurrentFrame} from 'remotion';
 
 export const SubtitleArtifact: React.FC = () => {
-	return <Experimental.Artifact content="Hello World!" filename="hello.txt" />;
+	const frame = useCurrentFrame();
+
+	return frame === 0 ? (
+		<Experimental.Artifact content="Hello World!" filename="hello.txt" />
+	) : null;
 };

From 367224479d83a5031173bbf51e3da3bbe48df5ab Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 12 Jun 2024 17:55:44 +0200
Subject: [PATCH 06/36] scaffold our artifact

---
 packages/cli/src/benchmark.ts                 |  1 +
 packages/cli/src/render-flows/render.ts       |  7 ++++
 .../functions/render-media-single-thread.ts   | 12 +++++-
 packages/lambda/src/functions/renderer.ts     | 12 +++++-
 packages/renderer/src/index.ts                |  2 +-
 packages/renderer/src/render-frames.ts        | 39 ++++++++++++++-----
 packages/renderer/src/render-media.ts         |  7 ++++
 7 files changed, 68 insertions(+), 12 deletions(-)

diff --git a/packages/cli/src/benchmark.ts b/packages/cli/src/benchmark.ts
index 83967681f41..2db1ea5cca4 100644
--- a/packages/cli/src/benchmark.ts
+++ b/packages/cli/src/benchmark.ts
@@ -489,6 +489,7 @@ export const benchmarkCommand = async (
 						}).value,
 					compositionStart: 0,
 					onBrowserDownload,
+					onArtifact: () => undefined,
 				},
 				(run, progress) => {
 					benchmarkProgress.update(
diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts
index f3493511577..2e61f875f1d 100644
--- a/packages/cli/src/render-flows/render.ts
+++ b/packages/cli/src/render-flows/render.ts
@@ -11,6 +11,7 @@ import type {
 	FrameRange,
 	LogLevel,
 	NumberOfGifLoops,
+	OnArtifact,
 	PixelFormat,
 	ProResProfile,
 	RenderMediaOnDownload,
@@ -402,6 +403,10 @@ export const renderVideoFlow = async ({
 		logLevel,
 	);
 
+	const onArtifact: OnArtifact = (artifact) => {
+		console.log('artifact', artifact);
+	};
+
 	const absoluteSeparateAudioTo =
 		separateAudioTo === null ? null : path.resolve(separateAudioTo);
 	const exists = existsSync(absoluteOutputFile);
@@ -491,6 +496,7 @@ export const renderVideoFlow = async ({
 			compositionStart: 0,
 			forSeamlessAacConcatenation,
 			onBrowserDownload,
+			onArtifact,
 		});
 
 		Log.info({indent, logLevel}, chalk.blue(`▶ ${absoluteOutputFile}`));
@@ -580,6 +586,7 @@ export const renderVideoFlow = async ({
 		forSeamlessAacConcatenation,
 		compositionStart: 0,
 		onBrowserDownload,
+		onArtifact,
 	});
 	if (!updatesDontOverwrite) {
 		updateRenderProgress({newline: true, printToConsole: true});
diff --git a/packages/cloudrun/src/functions/render-media-single-thread.ts b/packages/cloudrun/src/functions/render-media-single-thread.ts
index cd6b677f066..fcdc80d1531 100644
--- a/packages/cloudrun/src/functions/render-media-single-thread.ts
+++ b/packages/cloudrun/src/functions/render-media-single-thread.ts
@@ -1,6 +1,10 @@
 import type * as ff from '@google-cloud/functions-framework';
 import {Storage} from '@google-cloud/storage';
-import type {ChromiumOptions, RenderMediaOnProgress} from '@remotion/renderer';
+import type {
+	ChromiumOptions,
+	OnArtifact,
+	RenderMediaOnProgress,
+} from '@remotion/renderer';
 import {RenderInternals} from '@remotion/renderer';
 import {NoReactInternals} from 'remotion/no-react';
 import {VERSION} from 'remotion/version';
@@ -59,6 +63,11 @@ export const renderMediaSingleThread = async (
 			enableMultiProcessOnLinux: true,
 		};
 
+		const onArtifact: OnArtifact = (artifact) => {
+			// TODO: Do something with the artifact
+			console.log(artifact);
+		};
+
 		await RenderInternals.internalRenderMedia({
 			composition: {
 				...composition,
@@ -126,6 +135,7 @@ export const renderMediaSingleThread = async (
 			onBrowserDownload: () => {
 				throw new Error('Should not download a browser in Cloud Run');
 			},
+			onArtifact,
 		});
 
 		const storage = new Storage();
diff --git a/packages/lambda/src/functions/renderer.ts b/packages/lambda/src/functions/renderer.ts
index 8e15291dfa3..ac45bec1aa2 100644
--- a/packages/lambda/src/functions/renderer.ts
+++ b/packages/lambda/src/functions/renderer.ts
@@ -1,4 +1,9 @@
-import type {AudioCodec, BrowserLog, Codec} from '@remotion/renderer';
+import type {
+	AudioCodec,
+	BrowserLog,
+	Codec,
+	OnArtifact,
+} from '@remotion/renderer';
 import {RenderInternals} from '@remotion/renderer';
 import fs from 'node:fs';
 import path from 'node:path';
@@ -167,6 +172,10 @@ const renderHandler = async ({
 		params.everyNthFrame,
 	);
 
+	const onArtifact: OnArtifact = (artifact) => {
+		console.log(artifact);
+	};
+
 	await new Promise<void>((resolve, reject) => {
 		RenderInternals.internalRenderMedia({
 			repro: false,
@@ -271,6 +280,7 @@ const renderHandler = async ({
 			onBrowserDownload: () => {
 				throw new Error('Should not download a browser in Lambda');
 			},
+			onArtifact,
 		})
 			.then(({slowestFrames}) => {
 				RenderInternals.Log.verbose(
diff --git a/packages/renderer/src/index.ts b/packages/renderer/src/index.ts
index d30ac4b6e7b..ac072fa0339 100644
--- a/packages/renderer/src/index.ts
+++ b/packages/renderer/src/index.ts
@@ -108,7 +108,7 @@ export {X264Preset} from './options/x264-preset';
 export {PixelFormat} from './pixel-format';
 export {RemotionServer} from './prepare-server';
 export {ProResProfile} from './prores-profile';
-export {RenderFramesOptions, renderFrames} from './render-frames';
+export {OnArtifact, RenderFramesOptions, renderFrames} from './render-frames';
 export {
 	InternalRenderMediaOptions,
 	RenderMediaOnProgress,
diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts
index 34c5746f487..8ac752ef349 100644
--- a/packages/renderer/src/render-frames.ts
+++ b/packages/renderer/src/render-frames.ts
@@ -2,11 +2,7 @@ import fs from 'node:fs';
 import path from 'node:path';
 import {performance} from 'perf_hooks';
 // eslint-disable-next-line no-restricted-imports
-import type {
-	ArtifactAsset,
-	AudioOrVideoAsset,
-	VideoConfig,
-} from 'remotion/no-react';
+import type {AudioOrVideoAsset, VideoConfig} from 'remotion/no-react';
 import {NoReactInternals} from 'remotion/no-react';
 import type {RenderMediaOnDownload} from './assets/download-and-map-assets-to-file';
 import {downloadAndMapAssetsToFileUrl} from './assets/download-and-map-assets-to-file';
@@ -71,7 +67,9 @@ import {wrapWithErrorHandling} from './wrap-with-error-handling';
 
 const MAX_RETRIES_PER_FRAME = 1;
 
-export type InternalRenderFramesOptions = {
+export type OnArtifact = (asset: EmittedAsset) => void;
+
+type InternalRenderFramesOptions = {
 	onStart: null | ((data: OnStartData) => void);
 	onFrameUpdate:
 		| null
@@ -105,8 +103,15 @@ export type InternalRenderFramesOptions = {
 	serializedResolvedPropsWithCustomSchema: string;
 	parallelEncodingEnabled: boolean;
 	compositionStart: number;
+	onArtifact: OnArtifact | null;
 } & ToOptions<typeof optionsMap.renderFrames>;
 
+type EmittedAsset = {
+	filename: string;
+	content: string | Buffer;
+	frame: number;
+};
+
 type InnerRenderFramesOptions = {
 	onStart: null | ((data: OnStartData) => void);
 	onFrameUpdate:
@@ -123,6 +128,7 @@ type InnerRenderFramesOptions = {
 	everyNthFrame: number;
 	onBrowserLog: null | ((log: BrowserLog) => void);
 	onFrameBuffer: null | ((buffer: Buffer, frame: number) => void);
+	onArtifact: OnArtifact | null;
 	onDownload: RenderMediaOnDownload | null;
 	timeoutInMilliseconds: number;
 	scale: number;
@@ -146,10 +152,15 @@ type InnerRenderFramesOptions = {
 	compositionStart: number;
 } & ToOptions<typeof optionsMap.renderFrames>;
 
+type ArtifactWithoutContent = {
+	frame: number;
+	filename: string;
+};
+
 export type FrameAndAssets = {
 	frame: number;
 	audioAndVideoAssets: AudioOrVideoAsset[];
-	artifactAssets: ArtifactAsset[];
+	artifactAssets: ArtifactWithoutContent[];
 };
 
 export type RenderFramesOptions = {
@@ -190,6 +201,7 @@ export type RenderFramesOptions = {
 	composition: VideoConfig;
 	muted?: boolean;
 	concurrency?: number | string | null;
+	onArtifact?: OnArtifact | null;
 	serveUrl: string;
 } & Partial<ToOptions<typeof optionsMap.renderFrames>>;
 
@@ -511,7 +523,7 @@ const innerRenderFrames = async ({
 				if (artifact.filename === previousArtifact.filename) {
 					reject(
 						new Error(
-							`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}.`,
+							`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. An artifact`,
 						),
 					);
 					return;
@@ -526,7 +538,12 @@ const innerRenderFrames = async ({
 		assets.push({
 			audioAndVideoAssets: compressedAssets,
 			frame,
-			artifactAssets,
+			artifactAssets: artifactAssets.map((a) => {
+				return {
+					frame: a.frame,
+					filename: a.filename,
+				};
+			}),
 		});
 		for (const renderAsset of compressedAssets) {
 			downloadAndMapAssetsToFileUrl({
@@ -784,6 +801,7 @@ const internalRenderFramesRaw = ({
 	forSeamlessAacConcatenation,
 	compositionStart,
 	onBrowserDownload,
+	onArtifact,
 }: InternalRenderFramesOptions): Promise<RenderFramesOutput> => {
 	validateDimension(
 		composition.height,
@@ -913,6 +931,7 @@ const internalRenderFramesRaw = ({
 					forSeamlessAacConcatenation,
 					compositionStart,
 					onBrowserDownload,
+					onArtifact,
 				});
 			}),
 		])
@@ -1007,6 +1026,7 @@ export const renderFrames = (
 		offthreadVideoCacheSizeInBytes,
 		binariesDirectory,
 		onBrowserDownload,
+		onArtifact,
 	} = options;
 
 	if (!composition) {
@@ -1078,5 +1098,6 @@ export const renderFrames = (
 		onBrowserDownload:
 			onBrowserDownload ??
 			defaultBrowserDownloadProgress({indent, logLevel, api: 'renderFrames()'}),
+		onArtifact: onArtifact ?? null,
 	});
 };
diff --git a/packages/renderer/src/render-media.ts b/packages/renderer/src/render-media.ts
index fce7039b5a7..191035cdc7f 100644
--- a/packages/renderer/src/render-media.ts
+++ b/packages/renderer/src/render-media.ts
@@ -53,6 +53,7 @@ import {prespawnFfmpeg} from './prespawn-ffmpeg';
 import {shouldUseParallelEncoding} from './prestitcher-memory-usage';
 import type {ProResProfile} from './prores-profile';
 import {validateSelectedCodecAndProResCombination} from './prores-profile';
+import type {OnArtifact} from './render-frames';
 import {internalRenderFrames} from './render-frames';
 import {
 	disableRepro,
@@ -125,6 +126,7 @@ export type InternalRenderMediaOptions = {
 	concurrency: number | string | null;
 	binariesDirectory: string | null;
 	compositionStart: number;
+	onArtifact: OnArtifact | null;
 } & MoreRenderMediaOptions;
 
 type Prettify<T> = {
@@ -179,6 +181,7 @@ export type RenderMediaOptions = Prettify<{
 	colorSpace?: ColorSpace;
 	repro?: boolean;
 	binariesDirectory?: string | null;
+	onArtifact?: OnArtifact;
 }> &
 	Partial<MoreRenderMediaOptions>;
 
@@ -241,6 +244,7 @@ const internalRenderMediaRaw = ({
 	forSeamlessAacConcatenation,
 	compositionStart,
 	onBrowserDownload,
+	onArtifact,
 }: InternalRenderMediaOptions): Promise<RenderMediaResult> => {
 	if (repro) {
 		enableRepro({
@@ -649,6 +653,7 @@ const internalRenderMediaRaw = ({
 					compositionStart,
 					forSeamlessAacConcatenation,
 					onBrowserDownload,
+					onArtifact,
 				});
 
 				return renderFramesProc;
@@ -863,6 +868,7 @@ export const renderMedia = ({
 	separateAudioTo,
 	forSeamlessAacConcatenation,
 	onBrowserDownload,
+	onArtifact,
 }: RenderMediaOptions): Promise<RenderMediaResult> => {
 	if (quality !== undefined) {
 		console.warn(
@@ -937,6 +943,7 @@ export const renderMedia = ({
 		onBrowserDownload:
 			onBrowserDownload ??
 			defaultBrowserDownloadProgress({indent, logLevel, api: 'renderMedia()'}),
+		onArtifact: onArtifact ?? null,
 		// TODO: In the future, introduce this as a public API when launching the distributed rendering API
 		compositionStart: 0,
 	});

From d654062a8d6d3b1184b7b9a6b6d5e79c8c9c6ac2 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 12 Jun 2024 17:58:45 +0200
Subject: [PATCH 07/36] do something with the artifact

---
 packages/cli/src/render-flows/render.ts | 4 ++--
 packages/renderer/src/render-frames.ts  | 3 +++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts
index 2e61f875f1d..6fbb8faa795 100644
--- a/packages/cli/src/render-flows/render.ts
+++ b/packages/cli/src/render-flows/render.ts
@@ -29,7 +29,7 @@ import type {
 	RenderingProgressInput,
 	StitchingProgressInput,
 } from '@remotion/studio-server';
-import fs, {existsSync} from 'node:fs';
+import fs, {existsSync, writeFileSync} from 'node:fs';
 import os from 'node:os';
 import path from 'node:path';
 import {NoReactInternals} from 'remotion/no-react';
@@ -404,7 +404,7 @@ export const renderVideoFlow = async ({
 	);
 
 	const onArtifact: OnArtifact = (artifact) => {
-		console.log('artifact', artifact);
+		writeFileSync(artifact.filename, artifact.content);
 	};
 
 	const absoluteSeparateAudioTo =
diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts
index 8ac752ef349..15ef6b4ea3a 100644
--- a/packages/renderer/src/render-frames.ts
+++ b/packages/renderer/src/render-frames.ts
@@ -239,6 +239,7 @@ const innerRenderFrames = async ({
 	parallelEncodingEnabled,
 	compositionStart,
 	forSeamlessAacConcatenation,
+	onArtifact,
 }: Omit<
 	InnerRenderFramesOptions,
 	'offthreadVideoCacheSizeInBytes'
@@ -529,6 +530,8 @@ const innerRenderFrames = async ({
 					return;
 				}
 			}
+
+			onArtifact?.(artifact);
 		}
 
 		const compressedAssets = audioAndVideoAssets.map((asset) => {

From c2bb5e99535a2f999e397601aaf7ec7b0d4b8346 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 13 Jun 2024 10:50:04 +0200
Subject: [PATCH 08/36] general CLI handler

---
 packages/cli/src/on-artifact.ts                      |  6 ++++++
 packages/cli/src/render-flows/render.ts              | 12 ++++--------
 packages/core/src/index.ts                           |  3 +--
 .../src/SubtitleArtifact/SubtitleArtifact.tsx        |  4 ++--
 packages/renderer/src/render-frames.ts               |  2 +-
 5 files changed, 14 insertions(+), 13 deletions(-)
 create mode 100644 packages/cli/src/on-artifact.ts

diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts
new file mode 100644
index 00000000000..869d7d40f35
--- /dev/null
+++ b/packages/cli/src/on-artifact.ts
@@ -0,0 +1,6 @@
+import type {OnArtifact} from '@remotion/renderer';
+import {writeFileSync} from 'fs';
+
+export const onArtifactOnCli: OnArtifact = (artifact) => {
+	writeFileSync(artifact.filename, artifact.content);
+};
diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts
index 6fbb8faa795..bbb22fbe550 100644
--- a/packages/cli/src/render-flows/render.ts
+++ b/packages/cli/src/render-flows/render.ts
@@ -11,7 +11,6 @@ import type {
 	FrameRange,
 	LogLevel,
 	NumberOfGifLoops,
-	OnArtifact,
 	PixelFormat,
 	ProResProfile,
 	RenderMediaOnDownload,
@@ -29,7 +28,7 @@ import type {
 	RenderingProgressInput,
 	StitchingProgressInput,
 } from '@remotion/studio-server';
-import fs, {existsSync, writeFileSync} from 'node:fs';
+import fs, {existsSync} from 'node:fs';
 import os from 'node:os';
 import path from 'node:path';
 import {NoReactInternals} from 'remotion/no-react';
@@ -43,6 +42,7 @@ import {makeHyperlink} from '../hyperlinks/make-link';
 import {getVideoImageFormat} from '../image-formats';
 import {Log} from '../log';
 import {makeOnDownload} from '../make-on-download';
+import {onArtifactOnCli} from '../on-artifact';
 import {parsedCli, quietFlagProvided} from '../parsed-cli';
 import {
 	LABEL_WIDTH,
@@ -403,10 +403,6 @@ export const renderVideoFlow = async ({
 		logLevel,
 	);
 
-	const onArtifact: OnArtifact = (artifact) => {
-		writeFileSync(artifact.filename, artifact.content);
-	};
-
 	const absoluteSeparateAudioTo =
 		separateAudioTo === null ? null : path.resolve(separateAudioTo);
 	const exists = existsSync(absoluteOutputFile);
@@ -496,7 +492,7 @@ export const renderVideoFlow = async ({
 			compositionStart: 0,
 			forSeamlessAacConcatenation,
 			onBrowserDownload,
-			onArtifact,
+			onArtifact: onArtifactOnCli,
 		});
 
 		Log.info({indent, logLevel}, chalk.blue(`▶ ${absoluteOutputFile}`));
@@ -586,7 +582,7 @@ export const renderVideoFlow = async ({
 		forSeamlessAacConcatenation,
 		compositionStart: 0,
 		onBrowserDownload,
-		onArtifact,
+		onArtifact: onArtifactOnCli,
 	});
 	if (!updatesDontOverwrite) {
 		updateRenderProgress({newline: true, printToConsole: true});
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index f52ac894315..88ed8ff801b 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -1,4 +1,3 @@
-import {Artifact} from './Artifact.js';
 import './asset-types.js';
 import {Clipper} from './Clipper.js';
 import type {Codec} from './codec.js';
@@ -92,6 +91,7 @@ export type BundleState =
 
 checkMultipleRemotionVersions();
 export * from './AbsoluteFill.js';
+export {Artifact} from './Artifact.js';
 export * from './audio/index.js';
 export {cancelRender} from './cancel-render.js';
 export {
@@ -171,7 +171,6 @@ export const Experimental = {
 	 * @see [Documentation](https://www.remotion.dev/docs/null)
 	 */
 	Null,
-	Artifact,
 	useIsPlayer,
 };
 
diff --git a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx
index fa36b1dc18a..14ca160c160 100644
--- a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx
+++ b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx
@@ -1,10 +1,10 @@
 import React from 'react';
-import {Experimental, useCurrentFrame} from 'remotion';
+import {Artifact, useCurrentFrame} from 'remotion';
 
 export const SubtitleArtifact: React.FC = () => {
 	const frame = useCurrentFrame();
 
 	return frame === 0 ? (
-		<Experimental.Artifact content="Hello World!" filename="hello.txt" />
+		<Artifact content="Hello World!" filename="hello.txt" />
 	) : null;
 };
diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts
index 15ef6b4ea3a..6e753eddffc 100644
--- a/packages/renderer/src/render-frames.ts
+++ b/packages/renderer/src/render-frames.ts
@@ -108,7 +108,7 @@ type InternalRenderFramesOptions = {
 
 type EmittedAsset = {
 	filename: string;
-	content: string | Buffer;
+	content: string | Uint8Array;
 	frame: number;
 };
 

From fcbcac71bd289c77474aceacfa011d1d4c4b56b9 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 13 Jun 2024 12:59:47 +0200
Subject: [PATCH 09/36] artifact state

---
 packages/cli/src/on-artifact.ts          | 30 ++++++++++++++++++++++--
 packages/cli/src/progress-types.ts       |  3 +++
 packages/cli/src/render-flows/render.ts  | 15 +++++++++---
 packages/studio-shared/src/index.ts      |  2 ++
 packages/studio-shared/src/render-job.ts | 11 +++++++++
 5 files changed, 56 insertions(+), 5 deletions(-)

diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts
index 869d7d40f35..715d025cd55 100644
--- a/packages/cli/src/on-artifact.ts
+++ b/packages/cli/src/on-artifact.ts
@@ -1,6 +1,32 @@
 import type {OnArtifact} from '@remotion/renderer';
+import type {ArtifactProgress} from '@remotion/studio-shared';
 import {writeFileSync} from 'fs';
+import path from 'path';
 
-export const onArtifactOnCli: OnArtifact = (artifact) => {
-	writeFileSync(artifact.filename, artifact.content);
+export const handleOnArtifact = (
+	onProgress: (artifact: ArtifactProgress) => void,
+) => {
+	const progress: ArtifactProgress = {received: []};
+
+	const onArtifact: OnArtifact = (artifact) => {
+		// TODO: Normalize backslashes on Windows
+		const absoluteOutputDestination = path.join(
+			process.cwd(),
+			artifact.filename,
+		);
+		writeFileSync(artifact.filename, artifact.content);
+
+		progress.received.push({
+			absoluteOutputDestination,
+			filename: artifact.filename,
+			sizeInBytes: artifact.content.length,
+		});
+	};
+
+	onProgress(progress);
+
+	return {
+		onArtifact,
+		initialProgress: progress,
+	};
 };
diff --git a/packages/cli/src/progress-types.ts b/packages/cli/src/progress-types.ts
index 58fe3bb4a5d..497f4d07662 100644
--- a/packages/cli/src/progress-types.ts
+++ b/packages/cli/src/progress-types.ts
@@ -12,4 +12,7 @@ export const initialAggregateRenderProgress = (): AggregateRenderProgress => ({
 		bytes: 0,
 		doneIn: null,
 	},
+	artifactState: {
+		received: [],
+	},
 });
diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts
index bbb22fbe550..82c82600850 100644
--- a/packages/cli/src/render-flows/render.ts
+++ b/packages/cli/src/render-flows/render.ts
@@ -42,7 +42,7 @@ import {makeHyperlink} from '../hyperlinks/make-link';
 import {getVideoImageFormat} from '../image-formats';
 import {Log} from '../log';
 import {makeOnDownload} from '../make-on-download';
-import {onArtifactOnCli} from '../on-artifact';
+import {handleOnArtifact} from '../on-artifact';
 import {parsedCli, quietFlagProvided} from '../parsed-cli';
 import {
 	LABEL_WIDTH,
@@ -220,6 +220,13 @@ export const renderVideoFlow = async ({
 		doneIn: null,
 	};
 
+	let {onArtifact, initialProgress: artifactState} = handleOnArtifact(
+		(progress) => {
+			artifactState = progress;
+			updateRenderProgress({newline: false, printToConsole: true});
+		},
+	);
+
 	const updateRenderProgress = ({
 		newline,
 		printToConsole,
@@ -233,6 +240,7 @@ export const renderVideoFlow = async ({
 			downloads,
 			bundling: bundlingProgress,
 			copyingState,
+			artifactState,
 		};
 
 		const {output, message, progress} = makeRenderingAndStitchingProgress({
@@ -430,6 +438,7 @@ export const renderVideoFlow = async ({
 		codec: shouldOutputImageSequence ? undefined : codec,
 		uiImageFormat,
 	});
+
 	if (shouldOutputImageSequence) {
 		fs.mkdirSync(absoluteOutputFile, {
 			recursive: true,
@@ -492,7 +501,7 @@ export const renderVideoFlow = async ({
 			compositionStart: 0,
 			forSeamlessAacConcatenation,
 			onBrowserDownload,
-			onArtifact: onArtifactOnCli,
+			onArtifact,
 		});
 
 		Log.info({indent, logLevel}, chalk.blue(`▶ ${absoluteOutputFile}`));
@@ -582,7 +591,7 @@ export const renderVideoFlow = async ({
 		forSeamlessAacConcatenation,
 		compositionStart: 0,
 		onBrowserDownload,
-		onArtifact: onArtifactOnCli,
+		onArtifact,
 	});
 	if (!updatesDontOverwrite) {
 		updateRenderProgress({newline: true, printToConsole: true});
diff --git a/packages/studio-shared/src/index.ts b/packages/studio-shared/src/index.ts
index 11bf724e0d7..7e66eac1070 100644
--- a/packages/studio-shared/src/index.ts
+++ b/packages/studio-shared/src/index.ts
@@ -49,10 +49,12 @@ export {ProjectInfo} from './project-info';
 export type {RenderDefaults} from './render-defaults';
 export {
 	AggregateRenderProgress,
+	ArtifactProgress,
 	BundlingState,
 	CopyingState,
 	DownloadProgress,
 	JobProgressCallback,
+	ReceivedAsset,
 	RenderJob,
 	RenderJobWithCleanup,
 	RenderingProgressInput,
diff --git a/packages/studio-shared/src/render-job.ts b/packages/studio-shared/src/render-job.ts
index 6ff3b0c1f8b..86eefd9e7e2 100644
--- a/packages/studio-shared/src/render-job.ts
+++ b/packages/studio-shared/src/render-job.ts
@@ -56,6 +56,17 @@ export type AggregateRenderProgress = {
 	downloads: DownloadProgress[];
 	bundling: BundlingState;
 	copyingState: CopyingState;
+	artifactState: ArtifactProgress;
+};
+
+export type ReceivedAsset = {
+	filename: string;
+	absoluteOutputDestination: string;
+	sizeInBytes: number;
+};
+
+export type ArtifactProgress = {
+	received: ReceivedAsset[];
 };
 
 export type JobProgressCallback = (

From dd1710fede5b099d0442d4cb6a6223f04e79bf4f Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Fri, 14 Jun 2024 17:25:46 +0200
Subject: [PATCH 10/36] add some tests to it

---
 packages/core/src/Artifact.tsx                |  1 +
 packages/core/src/RenderAssetManager.tsx      |  2 +
 packages/core/src/internals.ts                |  2 +
 .../core/src/validation/validate-artifact.ts  | 41 +++++++++
 packages/renderer/src/test/artifacts.test.tsx | 88 +++++++++++++++++++
 .../src/test/get-assets-for-markup.tsx        |  1 +
 6 files changed, 135 insertions(+)
 create mode 100644 packages/core/src/validation/validate-artifact.ts
 create mode 100644 packages/renderer/src/test/artifacts.test.tsx

diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx
index b2fd295a435..489e47685d4 100644
--- a/packages/core/src/Artifact.tsx
+++ b/packages/core/src/Artifact.tsx
@@ -11,6 +11,7 @@ export const Artifact: React.FC<{
 	// TODO: Validate filename and content
 	const {registerRenderAsset, unregisterRenderAsset} =
 		useContext(RenderAssetManager);
+
 	const frame = useCurrentFrame();
 
 	const [id] = useState(() => {
diff --git a/packages/core/src/RenderAssetManager.tsx b/packages/core/src/RenderAssetManager.tsx
index 43588b15e6a..87b41d67dad 100644
--- a/packages/core/src/RenderAssetManager.tsx
+++ b/packages/core/src/RenderAssetManager.tsx
@@ -6,6 +6,7 @@ import {
 	useState,
 } from 'react';
 import type {TRenderAsset} from './CompositionManager.js';
+import {validateRenderAsset} from './validation/validate-artifact.js';
 
 export type RenderAssetManagerContext = {
 	registerRenderAsset: (renderAsset: TRenderAsset) => void;
@@ -26,6 +27,7 @@ export const RenderAssetManagerProvider: React.FC<{
 	const [renderAssets, setRenderAssets] = useState<TRenderAsset[]>([]);
 
 	const registerRenderAsset = useCallback((renderAsset: TRenderAsset) => {
+		validateRenderAsset(renderAsset);
 		setRenderAssets((assets) => {
 			return [...assets, renderAsset];
 		});
diff --git a/packages/core/src/internals.ts b/packages/core/src/internals.ts
index 656c475d3f8..3a9342b779d 100644
--- a/packages/core/src/internals.ts
+++ b/packages/core/src/internals.ts
@@ -75,6 +75,7 @@ import {
 import {useLazyComponent} from './use-lazy-component.js';
 import {useUnsafeVideoConfig} from './use-unsafe-video-config.js';
 import {useVideo} from './use-video.js';
+import {validateRenderAsset} from './validation/validate-artifact.js';
 import {
 	invalidCompositionErrorMessage,
 	isCompositionIdValid,
@@ -161,6 +162,7 @@ export const Internals = {
 	calculateScale,
 	editorPropsProviderRef,
 	PROPS_UPDATED_EXTERNALLY,
+	validateRenderAsset,
 } as const;
 
 export type {
diff --git a/packages/core/src/validation/validate-artifact.ts b/packages/core/src/validation/validate-artifact.ts
new file mode 100644
index 00000000000..162e177b093
--- /dev/null
+++ b/packages/core/src/validation/validate-artifact.ts
@@ -0,0 +1,41 @@
+import type {TRenderAsset} from '../CompositionManager';
+
+export const validateArtifactFilename = (filename: unknown) => {
+	if (typeof filename !== 'string') {
+		throw new TypeError(
+			`The "filename" must be a string, but you passed a value of type ${typeof filename}`,
+		);
+	}
+
+	if (filename.trim() === '') {
+		throw new Error('The `filename` must not be empty');
+	}
+
+	if (!filename.match(/^([0-9a-zA-Z-!_.*'()/:&$@=;+,?]+)/g)) {
+		throw new Error(
+			'The `filename` must match "/^([0-9a-zA-Z-!_.*\'()/:&$@=;+,?]+)/g". Use forward slashes only, even on Windows.',
+		);
+	}
+};
+
+const validateContent = (content: unknown) => {
+	if (typeof content !== 'string') {
+		throw new TypeError(
+			`The "content" must be a string, but you passed a value of type ${typeof content}`,
+		);
+	}
+
+	if (content.trim() === '') {
+		throw new Error('The `content` must not be empty');
+	}
+};
+
+export const validateRenderAsset = (artifact: TRenderAsset) => {
+	// We don't have validation for it yet
+	if (artifact.type !== 'artifact') {
+		return;
+	}
+
+	validateArtifactFilename(artifact.filename);
+	validateContent(artifact.content);
+};
diff --git a/packages/renderer/src/test/artifacts.test.tsx b/packages/renderer/src/test/artifacts.test.tsx
new file mode 100644
index 00000000000..879787c735c
--- /dev/null
+++ b/packages/renderer/src/test/artifacts.test.tsx
@@ -0,0 +1,88 @@
+/* eslint-disable no-restricted-imports */
+/**
+ * @vitest-environment jsdom
+ */
+import React from 'react';
+import {Artifact, useCurrentFrame} from 'remotion';
+import {expect, test} from 'vitest';
+import {getAssetsForMarkup} from './get-assets-for-markup';
+
+const basicConfig = {
+	width: 1080,
+	height: 1080,
+	fps: 30,
+	durationInFrames: 60,
+	id: 'hithere',
+};
+
+test('Should be able to collect artifacts', async () => {
+	const Markup: React.FC = () => {
+		const frame = useCurrentFrame();
+
+		return (
+			<div>
+				{frame === 1 ? (
+					<Artifact filename="hi.txt" content="hi there"></Artifact>
+				) : null}
+			</div>
+		);
+	};
+
+	const [first, second] = await getAssetsForMarkup(Markup, basicConfig);
+	expect(first).toEqual([]);
+	expect(second.length).toEqual(1);
+	const sec = second[0];
+	if (sec.type !== 'artifact') {
+		throw new Error('Expected artifact');
+	}
+
+	expect(sec.filename).toEqual('hi.txt');
+	expect(sec.content).toEqual('hi there');
+});
+
+test('Should throw on invalid artifact', async () => {
+	const Markup: React.FC = () => {
+		const frame = useCurrentFrame();
+
+		return (
+			<div>
+				{frame === 1 ? (
+					<Artifact
+						filename="backslash\\hithere.com"
+						content="hi there"
+					></Artifact>
+				) : null}
+			</div>
+		);
+	};
+
+	try {
+		await getAssetsForMarkup(Markup, basicConfig);
+	} catch (err) {
+		expect(err).toMatch(/The "filename" must match/);
+	}
+});
+
+test('Should throw on when missing content', async () => {
+	const Markup: React.FC = () => {
+		const frame = useCurrentFrame();
+
+		return (
+			<div>
+				{frame === 1 ? (
+					<Artifact
+						filename="backslash\\hithere.com"
+						// @ts-expect-error
+						content={undefined}
+					></Artifact>
+				) : null}
+			</div>
+		);
+	};
+
+	try {
+		await getAssetsForMarkup(Markup, basicConfig);
+	} catch (err) {
+		expect(err).toMatch(/The "content" must be a string/);
+	}
+});
diff --git a/packages/renderer/src/test/get-assets-for-markup.tsx b/packages/renderer/src/test/get-assets-for-markup.tsx
index 93000ae7195..6b935fbecd5 100644
--- a/packages/renderer/src/test/get-assets-for-markup.tsx
+++ b/packages/renderer/src/test/get-assets-for-markup.tsx
@@ -52,6 +52,7 @@ export const getAssetsForMarkup = async (
 		const [renderAssets, setAssets] = useState<TRenderAsset[]>([]);
 
 		const registerRenderAsset = useCallback((renderAsset: TRenderAsset) => {
+			Internals.validateRenderAsset(renderAsset);
 			setAssets((assts) => {
 				return [...assts, renderAsset];
 			});

From 2fc2eb9ab431e6d1905e3000ef19133088faa161 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Fri, 14 Jun 2024 17:42:41 +0200
Subject: [PATCH 11/36] cool artifacts

---
 packages/cli/src/on-artifact.ts         |  8 +++----
 packages/cli/src/progress-bar.ts        | 28 ++++++++++++++++++++++++-
 packages/cli/src/render-flows/render.ts | 13 ++++++------
 3 files changed, 38 insertions(+), 11 deletions(-)

diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts
index 715d025cd55..c93f287faa2 100644
--- a/packages/cli/src/on-artifact.ts
+++ b/packages/cli/src/on-artifact.ts
@@ -4,9 +4,10 @@ import {writeFileSync} from 'fs';
 import path from 'path';
 
 export const handleOnArtifact = (
+	artifactState: ArtifactProgress,
 	onProgress: (artifact: ArtifactProgress) => void,
 ) => {
-	const progress: ArtifactProgress = {received: []};
+	const initialProgress = {...artifactState};
 
 	const onArtifact: OnArtifact = (artifact) => {
 		// TODO: Normalize backslashes on Windows
@@ -16,17 +17,16 @@ export const handleOnArtifact = (
 		);
 		writeFileSync(artifact.filename, artifact.content);
 
-		progress.received.push({
+		initialProgress.received.push({
 			absoluteOutputDestination,
 			filename: artifact.filename,
 			sizeInBytes: artifact.content.length,
 		});
 	};
 
-	onProgress(progress);
+	onProgress(initialProgress);
 
 	return {
 		onArtifact,
-		initialProgress: progress,
 	};
 };
diff --git a/packages/cli/src/progress-bar.ts b/packages/cli/src/progress-bar.ts
index cc57ecd88ea..20cc2ca1b18 100644
--- a/packages/cli/src/progress-bar.ts
+++ b/packages/cli/src/progress-bar.ts
@@ -8,6 +8,7 @@ import type {
 	StitchingProgressInput,
 } from '@remotion/studio-server';
 import {StudioServerInternals} from '@remotion/studio-server';
+import {formatBytes, type ArtifactProgress} from '@remotion/studio-shared';
 import {chalk} from './chalk';
 import {
 	getFileSizeDownloadBar,
@@ -192,6 +193,30 @@ const makeRenderingProgress = ({
 		.join(' ');
 };
 
+const makeArtifactProgress = (artifactState: ArtifactProgress) => {
+	const {received} = artifactState;
+	if (received.length === 0) {
+		return null;
+	}
+
+	return received
+		.map((artifact) => {
+			return [
+				chalk.blue('+'.padEnd(LABEL_WIDTH)),
+				chalk.blue(
+					makeHyperlink({
+						url: 'file://' + artifact.absoluteOutputDestination,
+						fallback: artifact.filename,
+						text: artifact.filename,
+					}),
+				),
+				chalk.gray(`${formatBytes(artifact.sizeInBytes)}`),
+			].join(' ');
+		})
+		.filter(truthy)
+		.join('\n');
+};
+
 export const getRightLabelWidth = (totalFrames: number) => {
 	return `${totalFrames}/${totalFrames}`.length;
 };
@@ -237,7 +262,7 @@ export const makeRenderingAndStitchingProgress = ({
 	progress: number;
 	message: string;
 } => {
-	const {rendering, stitching, downloads, bundling} = prog;
+	const {rendering, stitching, downloads, bundling, artifactState} = prog;
 	const output = [
 		rendering ? makeRenderingProgress(rendering) : null,
 		makeMultiDownloadProgress(downloads, rendering?.totalFrames ?? 0),
@@ -247,6 +272,7 @@ export const makeRenderingAndStitchingProgress = ({
 					stitchingProgress: stitching,
 					isUsingParallelEncoding,
 				}),
+		makeArtifactProgress(artifactState),
 	]
 		.filter(truthy)
 		.join('\n');
diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts
index 82c82600850..89983aeb578 100644
--- a/packages/cli/src/render-flows/render.ts
+++ b/packages/cli/src/render-flows/render.ts
@@ -28,6 +28,7 @@ import type {
 	RenderingProgressInput,
 	StitchingProgressInput,
 } from '@remotion/studio-server';
+import type {ArtifactProgress} from '@remotion/studio-shared';
 import fs, {existsSync} from 'node:fs';
 import os from 'node:os';
 import path from 'node:path';
@@ -220,12 +221,7 @@ export const renderVideoFlow = async ({
 		doneIn: null,
 	};
 
-	let {onArtifact, initialProgress: artifactState} = handleOnArtifact(
-		(progress) => {
-			artifactState = progress;
-			updateRenderProgress({newline: false, printToConsole: true});
-		},
-	);
+	let artifactState: ArtifactProgress = {received: []};
 
 	const updateRenderProgress = ({
 		newline,
@@ -254,6 +250,11 @@ export const renderVideoFlow = async ({
 		}
 	};
 
+	const {onArtifact} = handleOnArtifact(artifactState, (progress) => {
+		artifactState = progress;
+		updateRenderProgress({newline: false, printToConsole: true});
+	});
+
 	const {urlOrBundle, cleanup: cleanupBundle} = await bundleOnCliOrTakeServeUrl(
 		{
 			fullPath: fullEntryPoint,

From d7a75ec463dd3dfb17fe7e5f5e071e5c736b0949 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Mon, 17 Jun 2024 17:08:11 +0200
Subject: [PATCH 12/36] artifact name

---
 packages/cli/src/on-artifact.ts               |  3 +-
 packages/lambda/src/functions/helpers/io.ts   |  2 +-
 .../src/functions/helpers/stream-renderer.ts  | 27 ++++++++-
 packages/lambda/src/functions/launch.ts       | 56 ++++++++++++++++++-
 packages/lambda/src/functions/renderer.ts     | 27 ++++++++-
 .../src/functions/streaming/streaming.ts      | 10 ++++
 packages/lambda/src/shared/constants.ts       |  2 +
 packages/renderer/src/index.ts                |  7 ++-
 packages/renderer/src/render-frames.ts        |  4 +-
 9 files changed, 128 insertions(+), 10 deletions(-)

diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts
index c93f287faa2..7fa19e4c811 100644
--- a/packages/cli/src/on-artifact.ts
+++ b/packages/cli/src/on-artifact.ts
@@ -10,10 +10,9 @@ export const handleOnArtifact = (
 	const initialProgress = {...artifactState};
 
 	const onArtifact: OnArtifact = (artifact) => {
-		// TODO: Normalize backslashes on Windows
 		const absoluteOutputDestination = path.join(
 			process.cwd(),
-			artifact.filename,
+			artifact.filename.replace('/', path.sep),
 		);
 		writeFileSync(artifact.filename, artifact.content);
 
diff --git a/packages/lambda/src/functions/helpers/io.ts b/packages/lambda/src/functions/helpers/io.ts
index 09efc03e7a5..adc8d2f1909 100644
--- a/packages/lambda/src/functions/helpers/io.ts
+++ b/packages/lambda/src/functions/helpers/io.ts
@@ -99,7 +99,7 @@ export const lambdaDeleteFile = async ({
 type LambdaWriteFileInput = {
 	bucketName: string;
 	key: string;
-	body: ReadStream | string;
+	body: ReadStream | string | Uint8Array;
 	region: AwsRegion;
 	privacy: Privacy;
 	expectedBucketOwner: string | null;
diff --git a/packages/lambda/src/functions/helpers/stream-renderer.ts b/packages/lambda/src/functions/helpers/stream-renderer.ts
index 94fe98cada5..e7a041aaaa3 100644
--- a/packages/lambda/src/functions/helpers/stream-renderer.ts
+++ b/packages/lambda/src/functions/helpers/stream-renderer.ts
@@ -1,4 +1,4 @@
-import type {LogLevel} from '@remotion/renderer';
+import type {EmittedAsset, LogLevel} from '@remotion/renderer';
 import {RenderInternals} from '@remotion/renderer';
 import {writeFileSync} from 'fs';
 import {join} from 'path';
@@ -26,6 +26,7 @@ const streamRenderer = ({
 	overallProgress,
 	files,
 	logLevel,
+	onArtifact,
 }: {
 	payload: LambdaPayload;
 	functionName: string;
@@ -33,6 +34,7 @@ const streamRenderer = ({
 	overallProgress: OverallProgressHelper;
 	files: string[];
 	logLevel: LogLevel;
+	onArtifact: (asset: EmittedAsset) => {alreadyExisted: boolean};
 }) => {
 	if (payload.type !== LambdaRoutines.renderer) {
 		throw new Error('Expected renderer type');
@@ -97,6 +99,25 @@ const streamRenderer = ({
 				return;
 			}
 
+			if (message.type === 'artifact-emitted') {
+				RenderInternals.Log.info(
+					{indent: false, logLevel},
+					`Received artifact on frame ${message.payload.artifact.frame}:`,
+					message.payload.artifact.filename,
+					message.payload.artifact.content.length + 'bytes.',
+				);
+				const {alreadyExisted} = onArtifact(message.payload.artifact);
+				if (alreadyExisted) {
+					return resolve({
+						type: 'error',
+						error: `Chunk ${payload.chunk} emitted an asset filename ${message.payload.artifact.filename} at frame ${message.payload.artifact.frame} but there is already another artifact with the same name.`,
+						shouldRetry: false,
+					});
+				}
+
+				return;
+			}
+
 			if (message.type === 'error-occurred') {
 				overallProgress.addErrorWithoutUpload(message.payload.errorInfo);
 				overallProgress.setFrames({
@@ -162,6 +183,7 @@ export const streamRendererFunctionWithRetry = async ({
 	outdir,
 	overallProgress,
 	logLevel,
+	onArtifact,
 }: {
 	payload: LambdaPayload;
 	functionName: string;
@@ -169,6 +191,7 @@ export const streamRendererFunctionWithRetry = async ({
 	overallProgress: OverallProgressHelper;
 	files: string[];
 	logLevel: LogLevel;
+	onArtifact: (asset: EmittedAsset) => {alreadyExisted: boolean};
 }): Promise<unknown> => {
 	if (payload.type !== LambdaRoutines.renderer) {
 		throw new Error('Expected renderer type');
@@ -181,6 +204,7 @@ export const streamRendererFunctionWithRetry = async ({
 		overallProgress,
 		payload,
 		logLevel,
+		onArtifact,
 	});
 
 	if (result.type === 'error') {
@@ -205,6 +229,7 @@ export const streamRendererFunctionWithRetry = async ({
 				retriesLeft: payload.retriesLeft - 1,
 			},
 			logLevel,
+			onArtifact,
 		});
 	}
 };
diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts
index 78c8d2e58ae..078d0def3db 100644
--- a/packages/lambda/src/functions/launch.ts
+++ b/packages/lambda/src/functions/launch.ts
@@ -1,10 +1,10 @@
 /* eslint-disable @typescript-eslint/no-use-before-define */
-import type {LogOptions} from '@remotion/renderer';
+import type {EmittedAsset, LogOptions} from '@remotion/renderer';
 import {RenderInternals} from '@remotion/renderer';
 import {existsSync, mkdirSync, rmSync} from 'fs';
 import {join} from 'path';
 import {VERSION} from 'remotion/version';
-import type {EventEmitter} from 'stream';
+import {type EventEmitter} from 'stream';
 import {
 	compressInputProps,
 	decompressInputProps,
@@ -20,6 +20,7 @@ import {
 	CONCAT_FOLDER_TOKEN,
 	LambdaRoutines,
 	MAX_FUNCTIONS_PER_RENDER,
+	artifactName,
 } from '../shared/constants';
 import {DOCS_URL} from '../shared/docs-url';
 import {invokeWebhook} from '../shared/invoke-webhook';
@@ -41,6 +42,7 @@ import {
 	getBrowserInstance,
 } from './helpers/get-browser-instance';
 import {getCurrentRegionInFunction} from './helpers/get-current-region';
+import {lambdaWriteFile} from './helpers/io';
 import {mergeChunksAndFinishRender} from './helpers/merge-chunks';
 import type {OverallProgressHelper} from './helpers/overall-render-progress';
 import {makeOverallRenderProgress} from './helpers/overall-render-progress';
@@ -54,6 +56,11 @@ type Options = {
 	getRemainingTimeInMillis: () => number;
 };
 
+type ReceivedAsset = {
+	filename: string;
+	sizeInBytes: number;
+};
+
 const innerLaunchHandler = async ({
 	functionName,
 	params,
@@ -354,6 +361,50 @@ const innerLaunchHandler = async ({
 
 	const files: string[] = [];
 
+	const artifacts: ReceivedAsset[] = [];
+
+	const onArtifact = (artifact: EmittedAsset): {alreadyExisted: boolean} => {
+		if (artifacts.find((a) => a.filename === artifact.filename)) {
+			return {alreadyExisted: true};
+		}
+
+		artifacts.push({
+			filename: artifact.filename,
+			sizeInBytes: artifact.content.length,
+		});
+
+		const start = Date.now();
+		RenderInternals.Log.info(
+			{indent: false, logLevel: params.logLevel},
+			'Writing artifact ' + artifact.filename + ' to S3',
+		);
+		lambdaWriteFile({
+			bucketName: renderBucketName,
+			key: artifactName(renderMetadata.renderId, artifact.filename),
+			body: artifact.content,
+			region: getCurrentRegionInFunction(),
+			privacy: params.privacy,
+			expectedBucketOwner: options.expectedBucketOwner,
+			downloadBehavior: params.downloadBehavior,
+			customCredentials,
+		})
+			.then(() => {
+				RenderInternals.Log.info(
+					{indent: false, logLevel: params.logLevel},
+					`Wrote artifact to S3 in ${Date.now() - start}ms`,
+				);
+			})
+			.catch((err) => {
+				// TODO: Handle error
+				RenderInternals.Log.error(
+					{indent: false, logLevel: params.logLevel},
+					'Failed to write artifact to S3',
+					err,
+				);
+			});
+		return {alreadyExisted: false};
+	};
+
 	await Promise.all(
 		lambdaPayloads.map(async (payload) => {
 			await streamRendererFunctionWithRetry({
@@ -363,6 +414,7 @@ const innerLaunchHandler = async ({
 				overallProgress,
 				payload,
 				logLevel: params.logLevel,
+				onArtifact,
 			});
 		}),
 	);
diff --git a/packages/lambda/src/functions/renderer.ts b/packages/lambda/src/functions/renderer.ts
index 8bae9be4b9b..439c63136d5 100644
--- a/packages/lambda/src/functions/renderer.ts
+++ b/packages/lambda/src/functions/renderer.ts
@@ -173,7 +173,32 @@ const renderHandler = async ({
 	);
 
 	const onArtifact: OnArtifact = (artifact) => {
-		console.log(artifact);
+		RenderInternals.Log.info(
+			{indent: false, logLevel: params.logLevel},
+			`Received artifact on frame ${artifact.frame}:`,
+			artifact.filename,
+			artifact.content.length + 'bytes. Streaming to main function',
+		);
+		const startTimestamp = Date.now();
+		onStream({
+			type: 'artifact-emitted',
+			payload: {
+				artifact,
+			},
+		})
+			.then(() => {
+				RenderInternals.Log.info(
+					{indent: false, logLevel: params.logLevel},
+					`Streaming artifact ${artifact.filename} to main function took ${Date.now() - startTimestamp}ms`,
+				);
+			})
+			.catch((e) => {
+				RenderInternals.Log.error(
+					{indent: false, logLevel: params.logLevel},
+					`Error streaming artifact ${artifact.filename} to main function`,
+					e,
+				);
+			});
 	};
 
 	await new Promise<void>((resolve, reject) => {
diff --git a/packages/lambda/src/functions/streaming/streaming.ts b/packages/lambda/src/functions/streaming/streaming.ts
index f7d8eea4e13..71966376ef3 100644
--- a/packages/lambda/src/functions/streaming/streaming.ts
+++ b/packages/lambda/src/functions/streaming/streaming.ts
@@ -1,3 +1,4 @@
+import type {EmittedAsset} from '@remotion/renderer';
 import {makeStreamPayloadMessage} from '@remotion/streaming';
 import type {LambdaErrorInfo} from '../helpers/write-lambda-error';
 import type {RenderStillLambdaResponsePayload} from '../still';
@@ -10,6 +11,7 @@ const audioChunkRendered = 'audio-chunk-rendered' as const;
 const chunkComplete = 'chunk-complete' as const;
 const stillRendered = 'still-rendered' as const;
 const lambdaInvoked = 'lambda-invoked' as const;
+const artifactEmitted = 'artifact-emitted' as const;
 
 const messageTypes = {
 	'1': {type: framesRendered},
@@ -20,6 +22,7 @@ const messageTypes = {
 	'6': {type: stillRendered},
 	'7': {type: chunkComplete},
 	'8': {type: lambdaInvoked},
+	'9': {type: artifactEmitted},
 } as const;
 
 export type MessageTypeId = keyof typeof messageTypes;
@@ -34,6 +37,7 @@ export const formatMap: {[key in MessageType]: 'json' | 'binary'} = {
 	[stillRendered]: 'json',
 	[chunkComplete]: 'json',
 	[lambdaInvoked]: 'json',
+	[artifactEmitted]: 'json',
 };
 
 export type StreamingPayload =
@@ -82,6 +86,12 @@ export type StreamingPayload =
 			payload: {
 				attempt: number;
 			};
+	  }
+	| {
+			type: typeof artifactEmitted;
+			payload: {
+				artifact: EmittedAsset;
+			};
 	  };
 
 export const messageTypeIdToMessageType = (
diff --git a/packages/lambda/src/shared/constants.ts b/packages/lambda/src/shared/constants.ts
index 8abd7bd221d..3591297884c 100644
--- a/packages/lambda/src/shared/constants.ts
+++ b/packages/lambda/src/shared/constants.ts
@@ -85,6 +85,8 @@ export const outName = (renderId: string, extension: string) =>
 	`${rendersPrefix(renderId)}/out.${extension}`;
 export const outStillName = (renderId: string, imageFormat: StillImageFormat) =>
 	`${rendersPrefix(renderId)}/out.${imageFormat}`;
+export const artifactName = (renderId: string, name: string) =>
+	`${rendersPrefix(renderId)}/artifacts/${name}`;
 export const customOutName = (
 	renderId: string,
 	bucketName: string,
diff --git a/packages/renderer/src/index.ts b/packages/renderer/src/index.ts
index ac072fa0339..9b3973414ee 100644
--- a/packages/renderer/src/index.ts
+++ b/packages/renderer/src/index.ts
@@ -108,7 +108,12 @@ export {X264Preset} from './options/x264-preset';
 export {PixelFormat} from './pixel-format';
 export {RemotionServer} from './prepare-server';
 export {ProResProfile} from './prores-profile';
-export {OnArtifact, RenderFramesOptions, renderFrames} from './render-frames';
+export {
+	EmittedArtifact as EmittedAsset,
+	OnArtifact,
+	RenderFramesOptions,
+	renderFrames,
+} from './render-frames';
 export {
 	InternalRenderMediaOptions,
 	RenderMediaOnProgress,
diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts
index 6e753eddffc..5b8dc820233 100644
--- a/packages/renderer/src/render-frames.ts
+++ b/packages/renderer/src/render-frames.ts
@@ -67,7 +67,7 @@ import {wrapWithErrorHandling} from './wrap-with-error-handling';
 
 const MAX_RETRIES_PER_FRAME = 1;
 
-export type OnArtifact = (asset: EmittedAsset) => void;
+export type OnArtifact = (asset: EmittedArtifact) => void;
 
 type InternalRenderFramesOptions = {
 	onStart: null | ((data: OnStartData) => void);
@@ -106,7 +106,7 @@ type InternalRenderFramesOptions = {
 	onArtifact: OnArtifact | null;
 } & ToOptions<typeof optionsMap.renderFrames>;
 
-type EmittedAsset = {
+export type EmittedArtifact = {
 	filename: string;
 	content: string | Uint8Array;
 	frame: number;

From e6f27c263d35b7a161e4ed75cd3903e7a2df3c12 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Mon, 17 Jun 2024 17:29:20 +0200
Subject: [PATCH 13/36] let's go

---
 packages/example/runlambda.sh                 |  2 +-
 .../src/cli/commands/render/progress.ts       | 27 +++++++++++++++++++
 .../helpers/create-post-render-data.ts        |  1 +
 .../src/functions/helpers/get-progress.ts     |  2 ++
 .../helpers/overall-render-progress.ts        | 17 ++++++++++++
 packages/lambda/src/functions/launch.ts       | 22 +++++++--------
 packages/lambda/src/shared/constants.ts       |  3 +++
 7 files changed, 62 insertions(+), 12 deletions(-)

diff --git a/packages/example/runlambda.sh b/packages/example/runlambda.sh
index 43c2eb94300..935c37f18bd 100644
--- a/packages/example/runlambda.sh
+++ b/packages/example/runlambda.sh
@@ -7,4 +7,4 @@ cd example
 bunx remotion lambda functions rmall -f
 bunx remotion lambda functions deploy --memory=3000 --disk=10000
 bunx remotion lambda sites create --site-name=testbed-v6 --log=verbose --enable-folder-expiry
-bunx remotion lambda render testbed-v6 OffthreadRemoteVideo --log=verbose --delete-after="1-day"
+bunx remotion lambda render testbed-v6 subtitle --log=verbose --delete-after="1-day"
diff --git a/packages/lambda/src/cli/commands/render/progress.ts b/packages/lambda/src/cli/commands/render/progress.ts
index 2ee2a50c77f..316a6a14fe4 100644
--- a/packages/lambda/src/cli/commands/render/progress.ts
+++ b/packages/lambda/src/cli/commands/render/progress.ts
@@ -200,6 +200,32 @@ const makeTopRow = (overall: RenderProgress) => {
 	return CliInternals.chalk.gray(str);
 };
 
+const makeArtifactProgress = (progress: RenderProgress) => {
+	const {artifactProgress} = progress;
+	if (artifactProgress.length === 0) {
+		return null;
+	}
+
+	return artifactProgress
+		.map((artifact) => {
+			return [
+				CliInternals.chalk.blue('+'.padEnd(CliInternals.LABEL_WIDTH)),
+				CliInternals.chalk.blue(
+					CliInternals.makeHyperlink({
+						url: artifact.s3Url,
+						fallback: artifact.filename,
+						text: artifact.filename,
+					}),
+				),
+				CliInternals.chalk.gray(
+					`${CliInternals.formatBytes(artifact.sizeInBytes)}`,
+				),
+			].join(' ');
+		})
+		.filter(truthy)
+		.join('\n');
+};
+
 export const makeProgressString = ({
 	downloadInfo,
 	overall,
@@ -213,6 +239,7 @@ export const makeProgressString = ({
 		...makeInvokeProgress(overall),
 		...makeRenderProgress(overall),
 		makeCombinationProgress(overall),
+		makeArtifactProgress(overall),
 		downloadInfo ? makeDownloadProgress(downloadInfo) : null,
 	]
 		.filter(NoReactInternals.truthy)
diff --git a/packages/lambda/src/functions/helpers/create-post-render-data.ts b/packages/lambda/src/functions/helpers/create-post-render-data.ts
index ba9ffd508d0..f5a550063ed 100644
--- a/packages/lambda/src/functions/helpers/create-post-render-data.ts
+++ b/packages/lambda/src/functions/helpers/create-post-render-data.ts
@@ -103,5 +103,6 @@ export const createPostRenderData = ({
 		deleteAfter: renderMetadata.deleteAfter,
 		estimatedBillingDurationInMilliseconds,
 		timeToCombine: timeToCombine ?? null,
+		artifactProgress: overallProgress.receivedAssets,
 	};
 };
diff --git a/packages/lambda/src/functions/helpers/get-progress.ts b/packages/lambda/src/functions/helpers/get-progress.ts
index 0f3928027ce..0a77693adc6 100644
--- a/packages/lambda/src/functions/helpers/get-progress.ts
+++ b/packages/lambda/src/functions/helpers/get-progress.ts
@@ -110,6 +110,7 @@ export const getProgress = async ({
 			compositionValidated: overallProgress.compositionValidated,
 			functionLaunched: overallProgress.functionLaunched,
 			serveUrlOpened: overallProgress.serveUrlOpened,
+			artifactProgress: overallProgress.receivedAssets,
 		};
 	}
 
@@ -257,5 +258,6 @@ export const getProgress = async ({
 		compositionValidated: overallProgress.compositionValidated,
 		functionLaunched: overallProgress.functionLaunched,
 		serveUrlOpened: overallProgress.serveUrlOpened,
+		artifactProgress: overallProgress.receivedAssets,
 	};
 };
diff --git a/packages/lambda/src/functions/helpers/overall-render-progress.ts b/packages/lambda/src/functions/helpers/overall-render-progress.ts
index ecc6557e89c..68a793d98c2 100644
--- a/packages/lambda/src/functions/helpers/overall-render-progress.ts
+++ b/packages/lambda/src/functions/helpers/overall-render-progress.ts
@@ -26,6 +26,13 @@ export type OverallRenderProgress = {
 	functionLaunched: number;
 	serveUrlOpened: number | null;
 	compositionValidated: number | null;
+	receivedAssets: ReceivedAsset[];
+};
+
+export type ReceivedAsset = {
+	filename: string;
+	sizeInBytes: number;
+	s3Url: string;
 };
 
 export type OverallProgressHelper = {
@@ -55,6 +62,8 @@ export type OverallProgressHelper = {
 	get: () => OverallRenderProgress;
 	setServeUrlOpened: (timestamp: number) => void;
 	setCompositionValidated: (timestamp: number) => void;
+	addReceivedAsset: (asset: ReceivedAsset) => void;
+	getReceivedAssets: () => ReceivedAsset[];
 };
 
 export const makeInitialOverallRenderProgress = (
@@ -78,6 +87,7 @@ export const makeInitialOverallRenderProgress = (
 		functionLaunched: Date.now(),
 		serveUrlOpened: null,
 		compositionValidated: null,
+		receivedAssets: [],
 	};
 };
 
@@ -274,6 +284,13 @@ export const makeOverallRenderProgress = ({
 			renderProgress.retries.push(retry);
 			upload();
 		},
+		addReceivedAsset(asset) {
+			renderProgress.receivedAssets.push(asset);
+			upload();
+		},
+		getReceivedAssets() {
+			return renderProgress.receivedAssets;
+		},
 		get: () => renderProgress,
 	};
 };
diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts
index 078d0def3db..f42a3873c29 100644
--- a/packages/lambda/src/functions/launch.ts
+++ b/packages/lambda/src/functions/launch.ts
@@ -56,11 +56,6 @@ type Options = {
 	getRemainingTimeInMillis: () => number;
 };
 
-type ReceivedAsset = {
-	filename: string;
-	sizeInBytes: number;
-};
-
 const innerLaunchHandler = async ({
 	functionName,
 	params,
@@ -361,16 +356,21 @@ const innerLaunchHandler = async ({
 
 	const files: string[] = [];
 
-	const artifacts: ReceivedAsset[] = [];
-
 	const onArtifact = (artifact: EmittedAsset): {alreadyExisted: boolean} => {
-		if (artifacts.find((a) => a.filename === artifact.filename)) {
+		if (
+			overallProgress
+				.getReceivedAssets()
+				.find((a) => a.filename === artifact.filename)
+		) {
 			return {alreadyExisted: true};
 		}
 
-		artifacts.push({
+		const region = getCurrentRegionInFunction();
+		const s3Key = artifactName(renderMetadata.renderId, artifact.filename);
+		overallProgress.addReceivedAsset({
 			filename: artifact.filename,
 			sizeInBytes: artifact.content.length,
+			s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`,
 		});
 
 		const start = Date.now();
@@ -380,9 +380,9 @@ const innerLaunchHandler = async ({
 		);
 		lambdaWriteFile({
 			bucketName: renderBucketName,
-			key: artifactName(renderMetadata.renderId, artifact.filename),
+			key: s3Key,
 			body: artifact.content,
-			region: getCurrentRegionInFunction(),
+			region,
 			privacy: params.privacy,
 			expectedBucketOwner: options.expectedBucketOwner,
 			downloadBehavior: params.downloadBehavior,
diff --git a/packages/lambda/src/shared/constants.ts b/packages/lambda/src/shared/constants.ts
index 3591297884c..6418b7b2979 100644
--- a/packages/lambda/src/shared/constants.ts
+++ b/packages/lambda/src/shared/constants.ts
@@ -14,6 +14,7 @@ import type {
 import type {BrowserSafeApis} from '@remotion/renderer/client';
 import type {ChunkRetry} from '../functions/helpers/get-retry-stats';
 import type {DeleteAfter} from '../functions/helpers/lifecycle';
+import type {ReceivedAsset} from '../functions/helpers/overall-render-progress';
 import type {EnhancedErrorInfo} from '../functions/helpers/write-lambda-error';
 import type {AwsRegion} from '../pricing/aws-regions';
 import type {
@@ -414,6 +415,7 @@ export type PostRenderData = {
 	estimatedBillingDurationInMilliseconds: number;
 	deleteAfter: DeleteAfter | null;
 	timeToCombine: number | null;
+	artifactProgress: ReceivedAsset[];
 };
 
 export type CostsInfo = {
@@ -469,6 +471,7 @@ export type RenderProgress = {
 	functionLaunched: number;
 	serveUrlOpened: number | null;
 	compositionValidated: number | null;
+	artifactProgress: ReceivedAsset[];
 };
 
 export type Privacy = 'public' | 'private' | 'no-acl';

From fb2e525d44a0d42183c0f2727483f1aa7f6f87e2 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Tue, 18 Jun 2024 17:41:52 +0200
Subject: [PATCH 14/36] artifact

---
 packages/core/src/Artifact.tsx                |   10 +-
 packages/docs/docs/artifact.mdx               |   62 +
 .../docs/miscellaneous/linux-dependencies.mdx |    1 +
 packages/docs/sidebars.js                     |    1 +
 packages/docs/src/data/articles.ts            | 5046 +++++++++--------
 .../generated/articles-docs-artifact.png      |  Bin 0 -> 32218 bytes
 ...-docs-miscellaneous-linux-dependencies.png |  Bin 0 -> 37868 bytes
 7 files changed, 2602 insertions(+), 2518 deletions(-)
 create mode 100644 packages/docs/docs/artifact.mdx
 create mode 100644 packages/docs/static/generated/articles-docs-artifact.png
 create mode 100644 packages/docs/static/generated/articles-docs-miscellaneous-linux-dependencies.png

diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx
index 489e47685d4..cca65033b33 100644
--- a/packages/core/src/Artifact.tsx
+++ b/packages/core/src/Artifact.tsx
@@ -1,17 +1,18 @@
 import type React from 'react';
 import {useContext, useEffect, useState} from 'react';
 import {RenderAssetManager} from './RenderAssetManager';
+import {getRemotionEnvironment} from './get-remotion-environment';
 import {useCurrentFrame} from './use-current-frame';
 
-// TODO: Not register in development?
 export const Artifact: React.FC<{
 	readonly filename: string;
 	readonly content: string;
 }> = ({filename, content}) => {
-	// TODO: Validate filename and content
 	const {registerRenderAsset, unregisterRenderAsset} =
 		useContext(RenderAssetManager);
 
+	const [env] = useState(() => getRemotionEnvironment());
+
 	const frame = useCurrentFrame();
 
 	const [id] = useState(() => {
@@ -19,6 +20,10 @@ export const Artifact: React.FC<{
 	});
 
 	useEffect(() => {
+		if (!env.isRendering) {
+			return;
+		}
+
 		registerRenderAsset({
 			type: 'artifact',
 			id,
@@ -32,6 +37,7 @@ export const Artifact: React.FC<{
 		};
 	}, [
 		content,
+		env.isRendering,
 		filename,
 		frame,
 		id,
diff --git a/packages/docs/docs/artifact.mdx b/packages/docs/docs/artifact.mdx
new file mode 100644
index 00000000000..146c33e5068
--- /dev/null
+++ b/packages/docs/docs/artifact.mdx
@@ -0,0 +1,62 @@
+---
+image: /generated/articles-docs-artifact.png
+title: "<Artifact>"
+crumb: API
+---
+
+# `<Artifact>`<AvailableFrom v="4.0.176"/>
+
+By rendering an `<Artifact>` tag in your Remotion markup, an extra file will get emitted during rendering.
+
+```tsx twoslash title="MyComp.tsx"
+import { Artifact, useCurrentFrame } from "remotion";
+
+export const MyComp: React.FC = () => {
+  const frame = useCurrentFrame();
+
+  return frame === 0 ? (
+    <Artifact filename="my-file.txt" content="Hello World!" />
+  ) : null;
+};
+```
+
+If rendered on the CLI or via the Studio, this will emit an additional file:
+
+```
+$ npx remotion render MyComp
++ out/MyComp.mp4
++ my-file.txt (12B)
+```
+
+It is allowed for a composition to emit multiple files.  
+However, the file names must be unique.
+
+The component will get evaluated on every frame, which means if you want to emit just one file, only render it on one frame.
+
+```tsx twoslash title="❌ Will generate an asset on every frame and throw an error because the file name is not unique"
+import { Artifact, useCurrentFrame } from "remotion";
+
+export const MyComp: React.FC = () => {
+  const frame = useCurrentFrame();
+
+  return frame === 0 ? (
+    <Artifact filename="my-file.txt" content="Hello World!" />
+  ) : null;
+};
+```
+
+## API
+
+### `filename`
+
+A string that is the name of the file that will be emitted.  
+Use forward slashes only, even on Windows.  
+Must match the regex `/^([0-9a-zA-Z-!_.*'()/:&$@=;+,?]+)/g`.
+
+### `content`
+
+A string or `UInt8Array` that is the content of the file that will be emitted.
+
+## See also
+
+- [Source code for this component](https://github.com/remotion-dev/remotion/blob/main/packages/core/src/components/Artifact.tsx)
diff --git a/packages/docs/docs/miscellaneous/linux-dependencies.mdx b/packages/docs/docs/miscellaneous/linux-dependencies.mdx
index 16721224e49..91dd7421e7e 100644
--- a/packages/docs/docs/miscellaneous/linux-dependencies.mdx
+++ b/packages/docs/docs/miscellaneous/linux-dependencies.mdx
@@ -1,4 +1,5 @@
 ---
+image: /generated/articles-docs-miscellaneous-linux-dependencies.png
 sidebar_label: Linux Dependencies
 title: Linux Dependencies
 crumb: "FAQ"
diff --git a/packages/docs/sidebars.js b/packages/docs/sidebars.js
index bd1c4e1aca2..3f56d1f2350 100644
--- a/packages/docs/sidebars.js
+++ b/packages/docs/sidebars.js
@@ -84,6 +84,7 @@ module.exports = {
       items: [
         "absolute-fill",
         "audio",
+        "artifact",
         "calculate-metadata",
         "cancel-render",
         "composition",
diff --git a/packages/docs/src/data/articles.ts b/packages/docs/src/data/articles.ts
index ceb3701af32..42e185e56a3 100644
--- a/packages/docs/src/data/articles.ts
+++ b/packages/docs/src/data/articles.ts
@@ -1,73 +1,80 @@
 export const articles = [
   {
-    id: "2-0-migration",
-    title: "v2.0 Migration",
-    relativePath: "docs/2-0-migration.mdx",
-    compId: "articles-docs-2-0-migration",
-    crumb: "Version Upgrade",
+    id: "figma",
+    title: "Import from Figma",
+    relativePath: "docs/figma.mdx",
+    compId: "articles-docs-figma",
+    crumb: "The best of both",
   },
   {
-    id: "3-0-migration",
-    title: "v3.0 Migration",
-    relativePath: "docs/3-0-migration.mdx",
-    compId: "articles-docs-3-0-migration",
-    crumb: "Version Upgrade",
+    id: "env-variables",
+    title: "Environment variables",
+    relativePath: "docs/env-variables.mdx",
+    compId: "articles-docs-env-variables",
+    crumb: "How To",
   },
   {
-    id: "4-0-alpha",
-    title: "v4.0 Alpha",
-    relativePath: "docs/4-0-alpha.mdx",
-    compId: "articles-docs-4-0-alpha",
-    crumb: "Version Upgrade",
+    id: "skia",
+    title: "@remotion/skia",
+    relativePath: "docs/skia/skia.mdx",
+    compId: "articles-docs-skia-skia",
+    crumb: null,
   },
   {
-    id: "4-0-migration",
-    title: "v4.0 Migration",
-    relativePath: "docs/4-0-migration.mdx",
-    compId: "articles-docs-4-0-migration",
-    crumb: "Version Upgrade",
+    id: "skia/enable-skia",
+    title: "enableSkia()",
+    relativePath: "docs/skia/enable-skia.mdx",
+    compId: "articles-docs-skia-enable-skia",
+    crumb: "@remotion/skia",
   },
   {
-    id: "5-0-migration",
-    title: "v5.0 Migration",
-    relativePath: "docs/5-0-migration.mdx",
-    compId: "articles-docs-5-0-migration",
-    crumb: "Version Upgrade",
+    id: "skia/skia-canvas",
+    title: "<SkiaCanvas />",
+    relativePath: "docs/skia/skia-canvas.mdx",
+    compId: "articles-docs-skia-skia-canvas",
+    crumb: "@remotion/skia",
   },
   {
-    id: "absolute-fill",
-    title: "<AbsoluteFill>",
-    relativePath: "docs/absolute-fill.mdx",
-    compId: "articles-docs-absolute-fill",
+    id: "spring",
+    title: "spring()",
+    relativePath: "docs/spring.mdx",
+    compId: "articles-docs-spring",
     crumb: "API",
   },
   {
-    id: "acknowledgements",
-    title: "Acknowledgements",
-    relativePath: "docs/acknowledgements.mdx",
-    compId: "articles-docs-acknowledgements",
-    crumb: "Credits",
+    id: "audio-visualization",
+    title: "Audio visualization",
+    relativePath: "docs/audio-visualization.mdx",
+    compId: "articles-docs-audio-visualization",
+    crumb: "Techniques",
   },
   {
-    id: "after-effects",
-    title: "Import from After Effects",
-    relativePath: "docs/after-effects.mdx",
-    compId: "articles-docs-after-effects",
-    crumb: "Integrations",
+    id: "react-native",
+    title: "React Native",
+    relativePath: "docs/react-native.mdx",
+    compId: "articles-docs-react-native",
+    crumb: null,
   },
   {
-    id: "animating-properties",
-    title: "Animating properties",
-    relativePath: "docs/animating-properties.mdx",
-    compId: "articles-docs-animating-properties",
-    crumb: "The basics",
+    id: "use-current-scale",
+    title: "useCurrentScale()",
+    relativePath: "docs/use-current-scale.mdx",
+    compId: "articles-docs-use-current-scale",
+    crumb: "API",
   },
   {
-    id: "animation-math",
-    title: "Animation math",
-    relativePath: "docs/animation-math.mdx",
-    compId: "articles-docs-animation-math",
-    crumb: "Techniques",
+    id: "config",
+    title: "Configuration file",
+    relativePath: "docs/config.mdx",
+    compId: "articles-docs-config",
+    crumb: "remotion.config.ts",
+  },
+  {
+    id: "make-transform",
+    title: "makeTransform()",
+    relativePath: "docs/animation-utils/make-transform.mdx",
+    compId: "articles-docs-animation-utils-make-transform",
+    crumb: "@remotion/animation-utils",
   },
   {
     id: "animation-utils/index",
@@ -84,46 +91,25 @@ export const articles = [
     crumb: "@remotion/animation-utils",
   },
   {
-    id: "make-transform",
-    title: "makeTransform()",
-    relativePath: "docs/animation-utils/make-transform.mdx",
-    compId: "articles-docs-animation-utils-make-transform",
-    crumb: "@remotion/animation-utils",
-  },
-  {
-    id: "api",
-    title: "API overview",
-    relativePath: "docs/api.mdx",
-    compId: "articles-docs-api",
-    crumb: null,
-  },
-  {
-    id: "get-help",
-    title: "Get help",
-    relativePath: "docs/ask-for-help.mdx",
-    compId: "articles-docs-ask-for-help",
-    crumb: null,
-  },
-  {
-    id: "audio-visualization",
-    title: "Audio visualization",
-    relativePath: "docs/audio-visualization.mdx",
-    compId: "articles-docs-audio-visualization",
-    crumb: "Techniques",
+    id: "4-0-alpha",
+    title: "v4.0 Alpha",
+    relativePath: "docs/4-0-alpha.mdx",
+    compId: "articles-docs-4-0-alpha",
+    crumb: "Version Upgrade",
   },
   {
-    id: "audio",
-    title: "<Audio>",
-    relativePath: "docs/audio.mdx",
-    compId: "articles-docs-audio",
-    crumb: "How to",
+    id: "use-video-config",
+    title: "useVideoConfig()",
+    relativePath: "docs/use-video-config.mdx",
+    compId: "articles-docs-use-video-config",
+    crumb: "API",
   },
   {
-    id: "audio-buffer-to-data-url",
-    title: "audioBufferToDataUrl()",
-    relativePath: "docs/audiobuffertodataurl.mdx",
-    compId: "articles-docs-audiobuffertodataurl",
-    crumb: "@remotion/media-utils",
+    id: "legacy-babel",
+    title: "Using legacy Babel transpilation",
+    relativePath: "docs/legacy-babel-loader.mdx",
+    compId: "articles-docs-legacy-babel-loader",
+    crumb: "How To",
   },
   {
     id: "brownfield",
@@ -133,508 +119,487 @@ export const articles = [
     crumb: "Brownfield integration",
   },
   {
-    id: "buffer-state",
-    title: "Display a buffer state",
-    relativePath: "docs/buffer-state.mdx",
-    compId: "articles-docs-buffer-state",
-    crumb: "Building video apps",
+    id: "studio/studio",
+    title: "Starting the Studio",
+    relativePath: "docs/studio/studio.mdx",
+    compId: "articles-docs-studio-studio",
+    crumb: "Timeline basics",
   },
   {
-    id: "bun",
-    title: "Bun support",
-    relativePath: "docs/bun.mdx",
-    compId: "articles-docs-bun",
-    crumb: "bun bun bun bun bun",
+    id: "studio/quick-switcher",
+    title: "Quick Switcher",
+    relativePath: "docs/studio/quick-switcher.mdx",
+    compId: "articles-docs-studio-quick-switcher",
+    crumb: "Remotion Studio",
   },
   {
-    id: "bundle",
-    title: "bundle()",
-    relativePath: "docs/bundle.mdx",
-    compId: "articles-docs-bundle",
-    crumb: "@remotion/bundler",
+    id: "studio/restart-studio",
+    title: "restartStudio()",
+    relativePath: "docs/studio/restart-studio.mdx",
+    compId: "articles-docs-studio-restart-studio",
+    crumb: "@remotion/studio",
   },
   {
-    id: "bundler",
-    title: "@remotion/bundler",
-    relativePath: "docs/bundler.mdx",
-    compId: "articles-docs-bundler",
-    crumb: null,
+    id: "studio/focus-default-props-path",
+    title: "focusDefaultPropsPath()",
+    relativePath: "docs/studio/focus-default-props-path.mdx",
+    compId: "articles-docs-studio-focus-default-props-path",
+    crumb: "@remotion/studio",
   },
   {
-    id: "calculate-metadata",
-    title: "calculateMetadata()",
-    relativePath: "docs/calculate-metadata.mdx",
-    compId: "articles-docs-calculate-metadata",
-    crumb: "API",
+    id: "studio/save-default-props",
+    title: "saveDefaultProps()",
+    relativePath: "docs/studio/save-default-props.mdx",
+    compId: "articles-docs-studio-save-default-props",
+    crumb: "@remotion/studio",
   },
   {
-    id: "cancel-render",
-    title: "cancelRender()",
-    relativePath: "docs/cancel-render.mdx",
-    compId: "articles-docs-cancel-render",
-    crumb: "How to",
+    id: "studio/deploy-static",
+    title: "Deploy Remotion Studio as static site",
+    relativePath: "docs/studio/deploy-static.mdx",
+    compId: "articles-docs-studio-deploy-static",
+    crumb: "Remotion Studio",
   },
   {
-    id: "chromium-flags",
-    title: "Chromium flags",
-    relativePath: "docs/chromium-flags.mdx",
-    compId: "articles-docs-chromium-flags",
-    crumb: "Tweaks",
+    id: "studio/watch-public-folder",
+    title: "watchPublicFolder()",
+    relativePath: "docs/studio/watch-public-folder.mdx",
+    compId: "articles-docs-studio-watch-public-folder",
+    crumb: "@remotion/studio",
   },
   {
-    id: "cli/benchmark",
-    title: "npx remotion benchmark",
-    relativePath: "docs/cli/benchmark.mdx",
-    compId: "articles-docs-cli-benchmark",
-    crumb: "CLI Reference",
+    id: "studio/deploy-server",
+    title: "Deploy the Remotion Studio on a VPS",
+    relativePath: "docs/studio/deploy-server.mdx",
+    compId: "articles-docs-studio-deploy-server",
+    crumb: "Remotion Studio",
   },
   {
-    id: "cli/browser/ensure",
-    title: "npx remotion browser ensure",
-    relativePath: "docs/cli/browser/ensure.mdx",
-    compId: "articles-docs-cli-browser-ensure",
-    crumb: "@remotion/cli",
+    id: "studio/get-static-files",
+    title: "getStaticFiles()",
+    relativePath: "docs/studio/get-static-files.mdx",
+    compId: "articles-docs-studio-get-static-files",
+    crumb: "@remotion/studio",
   },
   {
-    id: "cli/browser/index",
-    title: "npx remotion browser",
-    relativePath: "docs/cli/browser/index.mdx",
-    compId: "articles-docs-cli-browser-index",
-    crumb: "@remotion/cli",
+    id: "studio/watch-static-file",
+    title: "watchStaticFile()",
+    relativePath: "docs/studio/watch-static-file.mdx",
+    compId: "articles-docs-studio-watch-static-file",
+    crumb: "@remotion/studio",
   },
   {
-    id: "cli/bundle",
-    title: "npx remotion bundle",
-    relativePath: "docs/cli/bundle.mdx",
-    compId: "articles-docs-cli-bundle",
-    crumb: "CLI Reference",
+    id: "studio/shortcuts",
+    title: "Keyboard navigation",
+    relativePath: "docs/studio/shortcuts.mdx",
+    compId: "articles-docs-studio-shortcuts",
+    crumb: "Remotion Studio",
   },
   {
-    id: "cli",
-    title: "Command line reference",
-    relativePath: "docs/cli/cli.mdx",
-    compId: "articles-docs-cli-cli",
+    id: "studio/api",
+    title: "@remotion/studio",
+    relativePath: "docs/studio/api.mdx",
+    compId: "articles-docs-studio-api",
     crumb: null,
   },
   {
-    id: "cli/compositions",
-    title: "npx remotion compositions",
-    relativePath: "docs/cli/compositions.mdx",
-    compId: "articles-docs-cli-compositions",
-    crumb: "CLI Reference",
-  },
-  {
-    id: "ffmpeg",
-    title: "npx remotion ffmpeg",
-    relativePath: "docs/cli/ffmpeg.mdx",
-    compId: "articles-docs-cli-ffmpeg",
-    crumb: "@remotion/cli",
+    id: "studio/reevaluate-composition",
+    title: "reevaluateComposition()",
+    relativePath: "docs/studio/reevaluate-composition.mdx",
+    compId: "articles-docs-studio-reevaluate-composition",
+    crumb: "@remotion/studio",
   },
   {
-    id: "ffprobe",
-    title: "npx remotion ffprobe",
-    relativePath: "docs/cli/ffprobe.mdx",
-    compId: "articles-docs-cli-ffprobe",
-    crumb: "@remotion/cli",
+    id: "studio/write-static-file",
+    title: "writeStaticFile()",
+    relativePath: "docs/studio/write-static-file.mdx",
+    compId: "articles-docs-studio-write-static-file",
+    crumb: "@remotion/studio",
   },
   {
-    id: "cli/gpu",
-    title: "npx remotion gpu",
-    relativePath: "docs/cli/gpu.mdx",
-    compId: "articles-docs-cli-gpu",
-    crumb: "CLI Reference",
+    id: "studio/update-default-props",
+    title: "updateDefaultProps()",
+    relativePath: "docs/studio/update-default-props.mdx",
+    compId: "articles-docs-studio-update-default-props",
+    crumb: "@remotion/studio",
   },
   {
-    id: "cli/help",
-    title: "npx remotion help",
-    relativePath: "docs/cli/help.mdx",
-    compId: "articles-docs-cli-help",
-    crumb: "CLI Reference",
+    id: "studio/delete-static-file",
+    title: "deleteStaticFile()",
+    relativePath: "docs/studio/delete-static-file.mdx",
+    compId: "articles-docs-studio-delete-static-file",
+    crumb: "@remotion/studio",
   },
   {
-    id: "cli/install",
-    title: "npx remotion install",
-    relativePath: "docs/cli/install.mdx",
-    compId: "articles-docs-cli-install",
-    crumb: "CLI Reference",
+    id: "video-uploads",
+    title: "Handling user video uploads",
+    relativePath: "docs/video-uploads.mdx",
+    compId: "articles-docs-video-uploads",
+    crumb: "Building video apps",
   },
   {
-    id: "cli/render",
-    title: "npx remotion render",
-    relativePath: "docs/cli/render.mdx",
-    compId: "articles-docs-cli-render",
-    crumb: "CLI Reference",
+    id: "bundler",
+    title: "@remotion/bundler",
+    relativePath: "docs/bundler.mdx",
+    compId: "articles-docs-bundler",
+    crumb: null,
   },
   {
-    id: "cli/still",
-    title: "npx remotion still",
-    relativePath: "docs/cli/still.mdx",
-    compId: "articles-docs-cli-still",
-    crumb: "CLI Reference",
+    id: "using-audio",
+    title: "Using audio",
+    relativePath: "docs/using-audio.mdx",
+    compId: "articles-docs-using-audio",
+    crumb: "Techniques",
   },
   {
-    id: "cli/studio",
-    title: "npx remotion studio",
-    relativePath: "docs/cli/studio.mdx",
-    compId: "articles-docs-cli-studio",
-    crumb: "CLI Reference",
+    id: "get-compositions",
+    title: "getCompositions()",
+    relativePath: "docs/renderer/get-compositions.mdx",
+    compId: "articles-docs-renderer-get-compositions",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "cli/upgrade",
-    title: "npx remotion upgrade",
-    relativePath: "docs/cli/upgrade.mdx",
-    compId: "articles-docs-cli-upgrade",
-    crumb: "CLI Reference",
+    id: "make-cancel-signal",
+    title: "makeCancelSignal()",
+    relativePath: "docs/renderer/make-cancel-signal.mdx",
+    compId: "articles-docs-renderer-make-cancel-signal",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "cli/versions",
-    title: "npx remotion versions",
-    relativePath: "docs/cli/versions.mdx",
-    compId: "articles-docs-cli-versions",
-    crumb: "CLI Reference",
+    id: "extract-audio",
+    title: "extractAudio()",
+    relativePath: "docs/renderer/extract-audio.mdx",
+    compId: "articles-docs-renderer-extract-audio",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "clipper",
-    title: "<Experimental.Clipper>",
-    relativePath: "docs/clipper.mdx",
-    compId: "articles-docs-clipper",
-    crumb: "Experimental API",
+    id: "get-video-metadata",
+    title: "getVideoMetadata()",
+    relativePath: "docs/renderer/get-video-metadata.mdx",
+    compId: "articles-docs-renderer-get-video-metadata",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "cloudrun/api",
-    title: "@remotion/cloudrun",
-    relativePath: "docs/cloudrun/api.mdx",
-    compId: "articles-docs-cloudrun-api",
-    crumb: "Render videos without servers on GCP",
+    id: "select-composition",
+    title: "selectComposition()",
+    relativePath: "docs/renderer/select-composition.mdx",
+    compId: "articles-docs-renderer-select-composition",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "checklist",
-    title: "Production Checklist",
-    relativePath: "docs/cloudrun/checklist.mdx",
-    compId: "articles-docs-cloudrun-checklist",
-    crumb: "Cloud Run",
+    id: "ensure-ffmpeg",
+    title: "ensureFfmpeg()",
+    relativePath: "docs/renderer/ensure-ffmpeg.mdx",
+    compId: "articles-docs-renderer-ensure-ffmpeg",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "permissions",
-    title: "npx remotion cloudrun permissions",
-    relativePath: "docs/cloudrun/cli/permissions.mdx",
-    compId: "articles-docs-cloudrun-cli-permissions",
-    crumb: "Cloud Run CLI Reference",
+    id: "get-can-extract-frames-fast",
+    title: "getCanExtractFramesFast()",
+    relativePath: "docs/renderer/get-can-extract-frames-fast.mdx",
+    compId: "articles-docs-renderer-get-can-extract-frames-fast",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "regions",
-    title: "npx remotion cloudrun regions",
-    relativePath: "docs/cloudrun/cli/regions.mdx",
-    compId: "articles-docs-cloudrun-cli-regions",
-    crumb: "Cloud Run CLI Reference",
+    id: "get-silent-parts",
+    title: "getSilentParts()",
+    relativePath: "docs/renderer/get-silent-parts.mdx",
+    compId: "articles-docs-renderer-get-silent-parts",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "render",
-    title: "npx remotion cloudrun render",
-    relativePath: "docs/cloudrun/cli/render.mdx",
-    compId: "articles-docs-cloudrun-cli-render",
-    crumb: "Cloud Run CLI Reference",
+    id: "open-browser",
+    title: "openBrowser()",
+    relativePath: "docs/renderer/open-browser.mdx",
+    compId: "articles-docs-renderer-open-browser",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "services",
-    title: "npx remotion cloudrun services",
-    relativePath: "docs/cloudrun/cli/services.mdx",
-    compId: "articles-docs-cloudrun-cli-services",
-    crumb: "Cloud Run CLI Reference",
+    id: "stitch-frames-to-video",
+    title: "stitchFramesToVideo()",
+    relativePath: "docs/renderer/stitch-frames-to-video.mdx",
+    compId: "articles-docs-renderer-stitch-frames-to-video",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "sites",
-    title: "npx remotion cloudrun sites",
-    relativePath: "docs/cloudrun/cli/sites.mdx",
-    compId: "articles-docs-cloudrun-cli-sites",
-    crumb: "Cloud Run CLI Reference",
+    id: "ensure-ffprobe",
+    title: "ensureFfprobe()",
+    relativePath: "docs/renderer/ensure-ffprobe.mdx",
+    compId: "articles-docs-renderer-ensure-ffprobe",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "still",
-    title: "npx remotion cloudrun still",
-    relativePath: "docs/cloudrun/cli/still.mdx",
-    compId: "articles-docs-cloudrun-cli-still",
-    crumb: "Cloud Run CLI Reference",
+    id: "render-media",
+    title: "renderMedia()",
+    relativePath: "docs/renderer/render-media.mdx",
+    compId: "articles-docs-renderer-render-media",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "cli",
-    title: "@remotion/cloudrun - CLI",
-    relativePath: "docs/cloudrun/cli.mdx",
-    compId: "articles-docs-cloudrun-cli",
-    crumb: null,
+    id: "ensure-browser",
+    title: "ensureBrowser()",
+    relativePath: "docs/renderer/ensure-browser.mdx",
+    compId: "articles-docs-renderer-ensure-browser",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "deleteservice",
-    title: "deleteService()",
-    relativePath: "docs/cloudrun/deleteservice.mdx",
-    compId: "articles-docs-cloudrun-deleteservice",
-    crumb: "Cloud Run API",
+    id: "render-still",
+    title: "renderStill()",
+    relativePath: "docs/renderer/render-still.mdx",
+    compId: "articles-docs-renderer-render-still",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "deletesite",
-    title: "deleteSite()",
-    relativePath: "docs/cloudrun/deletesite.mdx",
-    compId: "articles-docs-cloudrun-deletesite",
-    crumb: "Cloud Run API",
+    id: "render-frames",
+    title: "renderFrames()",
+    relativePath: "docs/renderer/render-frames.mdx",
+    compId: "articles-docs-renderer-render-frames",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "deployservice",
-    title: "deployService()",
-    relativePath: "docs/cloudrun/deployservice.mdx",
-    compId: "articles-docs-cloudrun-deployservice",
-    crumb: "Cloud Run API",
+    id: "bundle",
+    title: "bundle()",
+    relativePath: "docs/bundle.mdx",
+    compId: "articles-docs-bundle",
+    crumb: "@remotion/bundler",
   },
   {
-    id: "deploysite",
-    title: "deploySite()",
-    relativePath: "docs/cloudrun/deploysite.mdx",
-    compId: "articles-docs-cloudrun-deploysite",
-    crumb: "Cloud Run API",
-  },
-  {
-    id: "generate-env",
-    title: "Generate .env File",
-    relativePath: "docs/cloudrun/generate-env.mdx",
-    compId: "articles-docs-cloudrun-generate-env",
-    crumb: "Cloud Run",
-  },
-  {
-    id: "getserviceinfo",
-    title: "getServiceInfo()",
-    relativePath: "docs/cloudrun/getServiceinfo.mdx",
-    compId: "articles-docs-cloudrun-getServiceinfo",
-    crumb: "Cloud Run API",
+    id: "img",
+    title: "<Img>",
+    relativePath: "docs/img.mdx",
+    compId: "articles-docs-img",
+    crumb: "API",
   },
   {
-    id: "getorcreatebucket",
-    title: "getOrCreateBucket()",
-    relativePath: "docs/cloudrun/getorcreatebucket.mdx",
-    compId: "articles-docs-cloudrun-getorcreatebucket",
-    crumb: "Cloud Run API",
+    id: "encoding",
+    title: "Encoding Guide",
+    relativePath: "docs/encoding.mdx",
+    compId: "articles-docs-encoding",
+    crumb: "Codecs and more",
   },
   {
-    id: "getregions",
-    title: "getRegions()",
-    relativePath: "docs/cloudrun/getregions.mdx",
-    compId: "articles-docs-cloudrun-getregions",
-    crumb: "Cloud Run API",
+    id: "get-remotion-environment",
+    title: "getRemotionEnvironment()",
+    relativePath: "docs/get-remotion-environment.mdx",
+    compId: "articles-docs-get-remotion-environment",
+    crumb: "API",
   },
   {
-    id: "getservices",
-    title: "getServices()",
-    relativePath: "docs/cloudrun/getservices.mdx",
-    compId: "articles-docs-cloudrun-getservices",
-    crumb: "Cloud Run API",
+    id: "sequence",
+    title: "<Sequence>",
+    relativePath: "docs/sequence.mdx",
+    compId: "articles-docs-sequence",
+    crumb: "API",
   },
   {
-    id: "getsites",
-    title: "getSites()",
-    relativePath: "docs/cloudrun/getsites.mdx",
-    compId: "articles-docs-cloudrun-getsites",
-    crumb: "Cloud Run API",
+    id: "the-fundamentals",
+    title: "The fundamentals",
+    relativePath: "docs/the-fundamentals.mdx",
+    compId: "articles-docs-the-fundamentals",
+    crumb: "Getting started",
   },
   {
-    id: "instancecount",
-    title: "Instance Count",
-    relativePath: "docs/cloudrun/instancecount.mdx",
-    compId: "articles-docs-cloudrun-instancecount",
-    crumb: "Cloud Run",
+    id: "ssr",
+    title: "Server-Side Rendering",
+    relativePath: "docs/ssr.mdx",
+    compId: "articles-docs-ssr",
+    crumb: "The power of",
   },
   {
-    id: "cloudrun/light-client",
-    title: "Light client",
-    relativePath: "docs/cloudrun/light-client.mdx",
-    compId: "articles-docs-cloudrun-light-client",
-    crumb: "Cloud Run",
+    id: "after-effects",
+    title: "Import from After Effects",
+    relativePath: "docs/after-effects.mdx",
+    compId: "articles-docs-after-effects",
+    crumb: "Integrations",
   },
   {
-    id: "cloudrun/limits",
-    title: "Cloud Run Limits",
-    relativePath: "docs/cloudrun/limits.mdx",
-    compId: "articles-docs-cloudrun-limits",
-    crumb: "Cloud Run",
+    id: "assets",
+    title: "Importing assets",
+    relativePath: "docs/importing-assets.mdx",
+    compId: "articles-docs-importing-assets",
+    crumb: "How To",
   },
   {
-    id: "cloudrun/multiple-buckets",
-    title: "Multiple buckets in Remotion Cloud Run",
-    relativePath: "docs/cloudrun/multiple-buckets.mdx",
-    compId: "articles-docs-cloudrun-multiple-buckets",
-    crumb: "@remotion/cloudrun",
+    id: "get-video-metadata",
+    title: "getVideoMetadata()",
+    relativePath: "docs/get-video-metadata.mdx",
+    compId: "articles-docs-get-video-metadata",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "cloudrun/permissions",
-    title: "Permissions",
-    relativePath: "docs/cloudrun/permissions.mdx",
-    compId: "articles-docs-cloudrun-permissions",
-    crumb: "Cloud Run",
+    id: "media-playback-error",
+    title: "Could not play video/audio with src",
+    relativePath: "docs/media-playback-error.mdx",
+    compId: "articles-docs-media-playback-error",
+    crumb: "Troubleshooting",
   },
   {
-    id: "region-selection",
-    title: "Region selection",
-    relativePath: "docs/cloudrun/region-selection.mdx",
-    compId: "articles-docs-cloudrun-region-selection",
-    crumb: "Cloud Run",
+    id: "staticfile-relative-paths",
+    title: "staticFile() does not support relative paths",
+    relativePath: "docs/static-file-relative-paths.mdx",
+    compId: "articles-docs-static-file-relative-paths",
+    crumb: "Troubleshooting",
   },
   {
-    id: "rendermediaoncloudrun",
-    title: "renderMediaOnCloudrun()",
-    relativePath: "docs/cloudrun/rendermediaoncloudrun.mdx",
-    compId: "articles-docs-cloudrun-rendermediaoncloudrun",
-    crumb: "Cloud Run API",
+    id: "interpolate",
+    title: "interpolate()",
+    relativePath: "docs/interpolate.mdx",
+    compId: "articles-docs-interpolate",
+    crumb: "API",
   },
   {
-    id: "renderstilloncloudrun",
-    title: "renderStillOnCloudrun()",
-    relativePath: "docs/cloudrun/renderstilloncloudrun.mdx",
-    compId: "articles-docs-cloudrun-renderstilloncloudrun",
-    crumb: "Cloud Run API",
+    id: "render-as-gif",
+    title: "Rendering GIFs",
+    relativePath: "docs/render-as-gif.mdx",
+    compId: "articles-docs-render-as-gif",
+    crumb: "Techniques",
   },
   {
-    id: "setup",
-    title: "Setup",
-    relativePath: "docs/cloudrun/setup.mdx",
-    compId: "articles-docs-cloudrun-setup",
-    crumb: "Cloud Run",
+    id: "webpack-dynamic-imports",
+    title: "Webpack dynamic imports",
+    relativePath: "docs/dynamic-import.mdx",
+    compId: "articles-docs-dynamic-import",
+    crumb: "Knowledge Base",
   },
   {
-    id: "speculateservicename",
-    title: "speculateServiceName()",
-    relativePath: "docs/cloudrun/speculateservicename.mdx",
-    compId: "articles-docs-cloudrun-speculateservicename",
-    crumb: "Cloud Run API",
+    id: "terminology/studio",
+    title: "Remotion Studio",
+    relativePath: "docs/terminology/studio.mdx",
+    compId: "articles-docs-terminology-studio",
+    crumb: "Terminology",
   },
   {
-    id: "testpermissions",
-    title: "testPermissions()",
-    relativePath: "docs/cloudrun/testpermissions.mdx",
-    compId: "articles-docs-cloudrun-testpermissions",
-    crumb: "Cloud Run API",
+    id: "terminology/bundle",
+    title: "Remotion Bundle",
+    relativePath: "docs/terminology/bundle.mdx",
+    compId: "articles-docs-terminology-bundle",
+    crumb: "Terminology",
   },
   {
-    id: "uninstall",
-    title: "Uninstall Cloud Run",
-    relativePath: "docs/cloudrun/uninstall.mdx",
-    compId: "articles-docs-cloudrun-uninstall",
-    crumb: null,
+    id: "terminology/sequence",
+    title: "Sequence",
+    relativePath: "docs/terminology/sequence.mdx",
+    compId: "articles-docs-terminology-sequence",
+    crumb: "Terminology",
   },
   {
-    id: "upgrading",
-    title: "Upgrading Cloud Run",
-    relativePath: "docs/cloudrun/upgrading.mdx",
-    compId: "articles-docs-cloudrun-upgrading",
-    crumb: null,
+    id: "terminology/concurrency",
+    title: "Concurrency",
+    relativePath: "docs/terminology/concurrency.mdx",
+    compId: "articles-docs-terminology-concurrency",
+    crumb: "Terminology",
   },
   {
-    id: "cloudrun-alpha",
-    title: "Cloud Run Alpha",
-    relativePath: "docs/cloudrun-alpha.mdx",
-    compId: "articles-docs-cloudrun-alpha",
-    crumb: "Version Upgrade",
+    id: "terminology/entry-point",
+    title: "Entry point",
+    relativePath: "docs/terminology/entry-point.mdx",
+    compId: "articles-docs-terminology-entry-point",
+    crumb: "Terminology",
   },
   {
-    id: "cloudrun",
-    title: "@remotion/cloudrun",
-    relativePath: "docs/cloudrun.mdx",
-    compId: "articles-docs-cloudrun",
-    crumb: null,
+    id: "terminology/player",
+    title: "Remotion Player",
+    relativePath: "docs/terminology/player.mdx",
+    compId: "articles-docs-terminology-player",
+    crumb: "Terminology",
   },
   {
-    id: "compare/motion-canvas",
-    title: "How does Remotion compare to Motion Canvas?",
-    relativePath: "docs/compare/motion-canvas.mdx",
-    compId: "articles-docs-compare-motion-canvas",
-    crumb: "FAQ",
+    id: "terminology/cloud-run-url",
+    title: "Cloud Run URL",
+    relativePath: "docs/terminology/cloud-run-url.mdx",
+    compId: "articles-docs-terminology-cloud-run-url",
+    crumb: "Terminology",
   },
   {
-    id: "composition",
-    title: "<Composition>",
-    relativePath: "docs/composition.mdx",
-    compId: "articles-docs-composition",
-    crumb: "API",
+    id: "terminology/input-props",
+    title: "Input Props",
+    relativePath: "docs/terminology/input-props.mdx",
+    compId: "articles-docs-terminology-input-props",
+    crumb: "Terminology",
   },
   {
-    id: "config",
-    title: "Configuration file",
-    relativePath: "docs/config.mdx",
-    compId: "articles-docs-config",
-    crumb: "remotion.config.ts",
+    id: "terminology/public-dir",
+    title: "Public directory",
+    relativePath: "docs/terminology/public-dir.mdx",
+    compId: "articles-docs-terminology-public-dir",
+    crumb: "Terminology",
   },
   {
-    id: "continue-render",
-    title: "continueRender()",
-    relativePath: "docs/continue-render.mdx",
-    compId: "articles-docs-continue-render",
-    crumb: "API",
+    id: "terminology/root-file",
+    title: "Root file",
+    relativePath: "docs/terminology/root-file.mdx",
+    compId: "articles-docs-terminology-root-file",
+    crumb: "Terminology",
   },
   {
-    id: "contributing/bounty",
-    title: "Take and solve bounty issues",
-    relativePath: "docs/contributing/bounty.mdx",
-    compId: "articles-docs-contributing-bounty",
-    crumb: "Contributing",
+    id: "terminology/service-name",
+    title: "Service Name",
+    relativePath: "docs/terminology/service-name.mdx",
+    compId: "articles-docs-terminology-service-name",
+    crumb: "Terminology",
   },
   {
-    id: "contributing/docs",
-    title: "Contributing to the documentation",
-    relativePath: "docs/contributing/docs.mdx",
-    compId: "articles-docs-contributing-docs",
-    crumb: "Contributing",
+    id: "terminology/serve-url",
+    title: "Serve URL",
+    relativePath: "docs/terminology/serve-url.mdx",
+    compId: "articles-docs-terminology-serve-url",
+    crumb: "Terminology",
   },
   {
-    id: "contributing/feature",
-    title: "Implementing a new feature",
-    relativePath: "docs/contributing/feature.mdx",
-    compId: "articles-docs-contributing-feature",
-    crumb: "Contributing",
+    id: "terminology/composition",
+    title: "Composition",
+    relativePath: "docs/terminology/composition.mdx",
+    compId: "articles-docs-terminology-composition",
+    crumb: "Terminology",
   },
   {
-    id: "contributing/formatting",
-    title: "Formatting in the Remotion repo",
-    relativePath: "docs/contributing/formatting.mdx",
-    compId: "articles-docs-contributing-formatting",
-    crumb: "Contributing",
+    id: "terminology/remotion-root",
+    title: "Remotion Root",
+    relativePath: "docs/terminology/remotion-root.mdx",
+    compId: "articles-docs-terminology-remotion-root",
+    crumb: "Terminology",
   },
   {
-    id: "contributing/index",
-    title: "Contributing to Remotion",
-    relativePath: "docs/contributing/index.mdx",
-    compId: "articles-docs-contributing-index",
-    crumb: "How to be awesome",
+    id: "motion-blur/camera-motion-blur",
+    title: "<CameraMotionBlur>",
+    relativePath: "docs/motion-blur/camera-motion-blur.mdx",
+    compId: "articles-docs-motion-blur-camera-motion-blur",
+    crumb: "Realistic camera effect",
   },
   {
-    id: "contributing/ineligible",
-    title: "Ineligible for bounties",
-    relativePath: "docs/contributing/ineligible.mdx",
-    compId: "articles-docs-contributing-ineligible",
-    crumb: "Contributing",
+    id: "motion-blur/index",
+    title: "@remotion/motion-blur",
+    relativePath: "docs/motion-blur/index.mdx",
+    compId: "articles-docs-motion-blur-index",
+    crumb: null,
   },
   {
-    id: "contributing/option",
-    title: "Implementing a new option",
-    relativePath: "docs/contributing/option.mdx",
-    compId: "articles-docs-contributing-option",
-    crumb: "Contributing",
+    id: "motion-blur/motion-blur",
+    title: "<MotionBlur>",
+    relativePath: "docs/motion-blur/motion-blur.mdx",
+    compId: "articles-docs-motion-blur-motion-blur",
+    crumb: null,
   },
   {
-    id: "contributing/presentation",
-    title: "Contribute your own presentation",
-    relativePath: "docs/contributing/presentation.mdx",
-    compId: "articles-docs-contributing-presentation",
-    crumb: "Contributing",
+    id: "motion-blur/trail",
+    title: "<Trail>",
+    relativePath: "docs/motion-blur/trail.mdx",
+    compId: "articles-docs-motion-blur-trail",
+    crumb: "Motion blur",
   },
   {
-    id: "contributing/rust",
-    title: "Contributing Rust code",
-    relativePath: "docs/contributing/rust.mdx",
-    compId: "articles-docs-contributing-rust",
-    crumb: "Contributing",
+    id: "freeze",
+    title: "<Freeze>",
+    relativePath: "docs/freeze.mdx",
+    compId: "articles-docs-freeze",
+    crumb: "API",
   },
   {
-    id: "authoring-packages",
-    title: "Authoring a Remotion library",
-    relativePath: "docs/creating-a-library.mdx",
-    compId: "articles-docs-creating-a-library",
-    crumb: "How to",
+    id: "terminology",
+    title: "Terminology",
+    relativePath: "docs/terminology.mdx",
+    compId: "articles-docs-terminology",
+    crumb: "The Remotion dictionary",
   },
   {
     id: "data-fetching",
@@ -644,95 +609,18 @@ export const articles = [
     crumb: "How to",
   },
   {
-    id: "dataset-render",
-    title: "Render videos programmatically from a dataset",
-    relativePath: "docs/dataset-render.mdx",
-    compId: "articles-docs-dataset-render",
-    crumb: "Tutorials",
-  },
-  {
-    id: "delay-render",
-    title: "delayRender() and continueRender()",
-    relativePath: "docs/delay-render.mdx",
-    compId: "articles-docs-delay-render",
-    crumb: "How to",
-  },
-  {
-    id: "docker",
-    title: "Dockerizing a Remotion app",
-    relativePath: "docs/docker.mdx",
-    compId: "articles-docs-docker",
-    crumb: "Building video apps",
-  },
-  {
-    id: "webpack-dynamic-imports",
-    title: "Webpack dynamic imports",
-    relativePath: "docs/dynamic-import.mdx",
-    compId: "articles-docs-dynamic-import",
-    crumb: "Knowledge Base",
-  },
-  {
-    id: "dynamic-metadata",
-    title: "Variable duration and dimensions",
-    relativePath: "docs/dynamic-metadata.mdx",
-    compId: "articles-docs-dynamic-metadata",
-    crumb: "How To",
-  },
-  {
-    id: "easing",
-    title: "Easing",
-    relativePath: "docs/easing.mdx",
-    compId: "articles-docs-easing",
-    crumb: "API",
-  },
-  {
-    id: "enable-scss/enable-scss",
-    title: "enableScss()",
-    relativePath: "docs/enable-scss/enable-scss.mdx",
-    compId: "articles-docs-enable-scss-enable-scss",
-    crumb: "@remotion/enable-scss",
-  },
-  {
-    id: "enable-scss/overview",
-    title: "@remotion/enable-scss",
-    relativePath: "docs/enable-scss/overview.mdx",
-    compId: "articles-docs-enable-scss-overview",
-    crumb: null,
-  },
-  {
-    id: "enametoolong",
-    title: "ENAMETOOLONG",
-    relativePath: "docs/enametoolong.mdx",
-    compId: "articles-docs-enametoolong",
-    crumb: "Troubleshooting",
-  },
-  {
-    id: "encoding",
-    title: "Encoding Guide",
-    relativePath: "docs/encoding.mdx",
-    compId: "articles-docs-encoding",
-    crumb: "Codecs and more",
-  },
-  {
-    id: "env-variables",
-    title: "Environment variables",
-    relativePath: "docs/env-variables.mdx",
-    compId: "articles-docs-env-variables",
-    crumb: "How To",
-  },
-  {
-    id: "ffmpeg",
-    title: "Installing FFmpeg",
-    relativePath: "docs/ffmpeg.mdx",
-    compId: "articles-docs-ffmpeg",
-    crumb: "(you don't have to)",
+    id: "target-closed",
+    title: "'Target closed' error message",
+    relativePath: "docs/target-closed.mdx",
+    compId: "articles-docs-target-closed",
+    crumb: "Troubleshooting",
   },
   {
-    id: "figma",
-    title: "Import from Figma",
-    relativePath: "docs/figma.mdx",
-    compId: "articles-docs-figma",
-    crumb: "The best of both",
+    id: "wrong-composition-mount",
+    title: "Wrongly mounted <Composition>",
+    relativePath: "docs/wrong-composition-mount.mdx",
+    compId: "articles-docs-wrong-composition-mount",
+    crumb: "Troubleshooting",
   },
   {
     id: "flickering",
@@ -742,213 +630,199 @@ export const articles = [
     crumb: "How to avoid",
   },
   {
-    id: "folder",
-    title: "<Folder>",
-    relativePath: "docs/folder.mdx",
-    compId: "articles-docs-folder",
-    crumb: "API",
-  },
-  {
-    id: "font-picker",
-    title: "Build a Google Font picker",
-    relativePath: "docs/font-picker.mdx",
-    compId: "articles-docs-font-picker",
-    crumb: "Building video apps",
-  },
-  {
-    id: "fonts-api",
-    title: "@remotion/fonts",
-    relativePath: "docs/fonts-api/index.mdx",
-    compId: "articles-docs-fonts-api-index",
-    crumb: null,
+    id: "contributing/ineligible",
+    title: "Ineligible for bounties",
+    relativePath: "docs/contributing/ineligible.mdx",
+    compId: "articles-docs-contributing-ineligible",
+    crumb: "Contributing",
   },
   {
-    id: "fonts-api/load-font",
-    title: "loadFont()",
-    relativePath: "docs/fonts-api/load-font.mdx",
-    compId: "articles-docs-fonts-api-load-font",
-    crumb: "@remotion/fonts",
+    id: "contributing/bounty",
+    title: "Take and solve bounty issues",
+    relativePath: "docs/contributing/bounty.mdx",
+    compId: "articles-docs-contributing-bounty",
+    crumb: "Contributing",
   },
   {
-    id: "fonts",
-    title: "Using fonts",
-    relativePath: "docs/fonts.mdx",
-    compId: "articles-docs-fonts",
-    crumb: "Techniques",
+    id: "contributing/rust",
+    title: "Contributing Rust code",
+    relativePath: "docs/contributing/rust.mdx",
+    compId: "articles-docs-contributing-rust",
+    crumb: "Contributing",
   },
   {
-    id: "freeze",
-    title: "<Freeze>",
-    relativePath: "docs/freeze.mdx",
-    compId: "articles-docs-freeze",
-    crumb: "API",
+    id: "contributing/option",
+    title: "Implementing a new option",
+    relativePath: "docs/contributing/option.mdx",
+    compId: "articles-docs-contributing-option",
+    crumb: "Contributing",
   },
   {
-    id: "get-audio-data",
-    title: "getAudioData()",
-    relativePath: "docs/get-audio-data.mdx",
-    compId: "articles-docs-get-audio-data",
-    crumb: "@remotion/media-utils",
+    id: "contributing/presentation",
+    title: "Contribute your own presentation",
+    relativePath: "docs/contributing/presentation.mdx",
+    compId: "articles-docs-contributing-presentation",
+    crumb: "Contributing",
   },
   {
-    id: "get-audio-duration-in-seconds",
-    title: "getAudioDurationInSeconds()",
-    relativePath: "docs/get-audio-duration-in-seconds.mdx",
-    compId: "articles-docs-get-audio-duration-in-seconds",
-    crumb: "@remotion/media-utils",
+    id: "contributing/feature",
+    title: "Implementing a new feature",
+    relativePath: "docs/contributing/feature.mdx",
+    compId: "articles-docs-contributing-feature",
+    crumb: "Contributing",
   },
   {
-    id: "get-image-dimensions",
-    title: "getImageDimensions()",
-    relativePath: "docs/get-image-dimensions.mdx",
-    compId: "articles-docs-get-image-dimensions",
-    crumb: "@remotion/media-utils",
+    id: "contributing/index",
+    title: "Contributing to Remotion",
+    relativePath: "docs/contributing/index.mdx",
+    compId: "articles-docs-contributing-index",
+    crumb: "How to be awesome",
   },
   {
-    id: "get-input-props",
-    title: "getInputProps()",
-    relativePath: "docs/get-input-props.mdx",
-    compId: "articles-docs-get-input-props",
-    crumb: "API",
+    id: "contributing/formatting",
+    title: "Formatting in the Remotion repo",
+    relativePath: "docs/contributing/formatting.mdx",
+    compId: "articles-docs-contributing-formatting",
+    crumb: "Contributing",
   },
   {
-    id: "get-remotion-environment",
-    title: "getRemotionEnvironment()",
-    relativePath: "docs/get-remotion-environment.mdx",
-    compId: "articles-docs-get-remotion-environment",
-    crumb: "API",
+    id: "contributing/docs",
+    title: "Contributing to the documentation",
+    relativePath: "docs/contributing/docs.mdx",
+    compId: "articles-docs-contributing-docs",
+    crumb: "Contributing",
   },
   {
-    id: "getstaticfiles",
-    title: "getStaticFiles()",
-    relativePath: "docs/get-static-files.mdx",
-    compId: "articles-docs-get-static-files",
-    crumb: "API",
+    id: "third-party",
+    title: "Third party integrations",
+    relativePath: "docs/third-party.mdx",
+    compId: "articles-docs-third-party",
+    crumb: "Integrations",
   },
   {
-    id: "get-video-metadata",
-    title: "getVideoMetadata()",
-    relativePath: "docs/get-video-metadata.mdx",
-    compId: "articles-docs-get-video-metadata",
-    crumb: "@remotion/media-utils",
+    id: "tailwind",
+    title: "TailwindCSS",
+    relativePath: "docs/tailwind.mdx",
+    compId: "articles-docs-tailwind",
+    crumb: "text-lg font-bold",
   },
   {
-    id: "get-waveform-portion",
-    title: "getWaveformPortion()",
-    relativePath: "docs/get-waveform-portion.mdx",
-    compId: "articles-docs-get-waveform-portion",
+    id: "get-image-dimensions",
+    title: "getImageDimensions()",
+    relativePath: "docs/get-image-dimensions.mdx",
+    compId: "articles-docs-get-image-dimensions",
     crumb: "@remotion/media-utils",
   },
   {
-    id: "getting-started",
-    title: "Creating a new project",
-    relativePath: "docs/getting-started.mdx",
-    compId: "articles-docs-getting-started",
-    crumb: "Let's begin!",
-  },
-  {
-    id: "get-gif-duration-in-seconds",
-    title: "getGifDurationInSeconds()",
-    relativePath: "docs/gif/get-gif-duration-in-seconds.mdx",
-    compId: "articles-docs-gif-get-gif-duration-in-seconds",
-    crumb: "@remotion/gif",
+    id: "cancel-render",
+    title: "cancelRender()",
+    relativePath: "docs/cancel-render.mdx",
+    compId: "articles-docs-cancel-render",
+    crumb: "How to",
   },
   {
-    id: "gif/gif",
-    title: "<Gif>",
-    relativePath: "docs/gif/gif.mdx",
-    compId: "articles-docs-gif-gif",
-    crumb: "@remotion/gif",
+    id: "spline",
+    title: "Import from Spline",
+    relativePath: "docs/spline.mdx",
+    compId: "articles-docs-spline",
+    crumb: "Integrations",
   },
   {
-    id: "gif/index",
-    title: "@remotion/gif",
-    relativePath: "docs/gif/index.mdx",
-    compId: "articles-docs-gif-index",
-    crumb: null,
+    id: "use-video-texture",
+    title: "useVideoTexture()",
+    relativePath: "docs/use-video-texture.mdx",
+    compId: "articles-docs-use-video-texture",
+    crumb: "@remotion/three",
   },
   {
-    id: "gif/preload-gif",
-    title: "preloadGif()",
-    relativePath: "docs/gif/preload-gif.mdx",
-    compId: "articles-docs-gif-preload-gif",
-    crumb: "@remotion/gif",
+    id: "gpu",
+    title: "Using the GPU",
+    relativePath: "docs/gpu.mdx",
+    compId: "articles-docs-gpu",
+    crumb: "Need for Speed",
   },
   {
-    id: "google-fonts/get-available-fonts",
-    title: "getAvailableFonts()",
-    relativePath: "docs/google-fonts/get-available-fonts.mdx",
-    compId: "articles-docs-google-fonts-get-available-fonts",
-    crumb: "@remotion/google-fonts",
+    id: "timeout",
+    title: "Debugging timeouts",
+    relativePath: "docs/timeout.mdx",
+    compId: "articles-docs-timeout",
+    crumb: "Troubleshooting",
   },
   {
-    id: "google-fonts/get-info",
-    title: "getInfo()",
-    relativePath: "docs/google-fonts/get-info.mdx",
-    compId: "articles-docs-google-fonts-get-info",
-    crumb: "@remotion/google-fonts",
+    id: "renderer",
+    title: "@remotion/renderer",
+    relativePath: "docs/renderer.mdx",
+    compId: "articles-docs-renderer",
+    crumb: null,
   },
   {
-    id: "google-fonts",
-    title: "@remotion/google-fonts",
-    relativePath: "docs/google-fonts/index.mdx",
-    compId: "articles-docs-google-fonts-index",
+    id: "three",
+    title: "@remotion/three",
+    relativePath: "docs/three.mdx",
+    compId: "articles-docs-three",
     crumb: null,
   },
   {
-    id: "google-fonts/load-font",
-    title: "loadFont()",
-    relativePath: "docs/google-fonts/load-font.mdx",
-    compId: "articles-docs-google-fonts-load-font",
-    crumb: "@remotion/google-fonts",
+    id: "absolute-fill",
+    title: "<AbsoluteFill>",
+    relativePath: "docs/absolute-fill.mdx",
+    compId: "articles-docs-absolute-fill",
+    crumb: "API",
   },
   {
-    id: "gpu",
-    title: "Using the GPU",
-    relativePath: "docs/gpu.mdx",
-    compId: "articles-docs-gpu",
-    crumb: "Need for Speed",
+    id: "offthreadvideo",
+    title: "<OffthreadVideo>",
+    relativePath: "docs/offthreadvideo.mdx",
+    compId: "articles-docs-offthreadvideo",
+    crumb: "API",
   },
   {
-    id: "props-resolution",
-    title: "How props get resolved",
-    relativePath: "docs/how-props-flow.mdx",
-    compId: "articles-docs-how-props-flow",
-    crumb: "Parameterized videos",
+    id: "dynamic-metadata",
+    title: "Variable duration and dimensions",
+    relativePath: "docs/dynamic-metadata.mdx",
+    compId: "articles-docs-dynamic-metadata",
+    crumb: "How To",
   },
   {
-    id: "iframe",
-    title: "<IFrame>",
-    relativePath: "docs/iframe.mdx",
-    compId: "articles-docs-iframe",
-    crumb: "API",
+    id: "zod-types/z-textarea",
+    title: "zTextarea()",
+    relativePath: "docs/zod-types/z-textarea.mdx",
+    compId: "articles-docs-zod-types-z-textarea",
+    crumb: null,
   },
   {
-    id: "img",
-    title: "<Img>",
-    relativePath: "docs/img.mdx",
-    compId: "articles-docs-img",
-    crumb: "API",
+    id: "zod-types/z-color",
+    title: "zColor()",
+    relativePath: "docs/zod-types/z-color.mdx",
+    compId: "articles-docs-zod-types-z-color",
+    crumb: null,
   },
   {
-    id: "assets",
-    title: "Importing assets",
-    relativePath: "docs/importing-assets.mdx",
-    compId: "articles-docs-importing-assets",
-    crumb: "How To",
+    id: "zod-types/index",
+    title: "@remotion/zod-types",
+    relativePath: "docs/zod-types/index.mdx",
+    compId: "articles-docs-zod-types-index",
+    crumb: "Schema",
   },
   {
-    id: "install-whisper-cpp/convert-to-captions",
-    title: "convertToCaptions()",
-    relativePath: "docs/install-whisper-cpp/convert-to-captions.mdx",
-    compId: "articles-docs-install-whisper-cpp-convert-to-captions",
-    crumb: "@remotion/install-whisper-cpp",
+    id: "non-seekable-media",
+    title: "Non-seekable media",
+    relativePath: "docs/non-seekable-media.mdx",
+    compId: "articles-docs-non-seekable-media",
+    crumb: "Troubleshooting",
   },
   {
-    id: "install-whisper-cpp/download-whisper-model",
-    title: "downloadWhisperModel()",
-    relativePath: "docs/install-whisper-cpp/download-whisper-model.mdx",
-    compId: "articles-docs-install-whisper-cpp-download-whisper-model",
+    id: "docker",
+    title: "Dockerizing a Remotion app",
+    relativePath: "docs/docker.mdx",
+    compId: "articles-docs-docker",
+    crumb: "Building video apps",
+  },
+  {
+    id: "install-whisper-cpp/install-whisper-cpp",
+    title: "installWhisperCpp()",
+    relativePath: "docs/install-whisper-cpp/install-whisper-cpp.mdx",
+    compId: "articles-docs-install-whisper-cpp-install-whisper-cpp",
     crumb: "@remotion/install-whisper-cpp",
   },
   {
@@ -958,13 +832,6 @@ export const articles = [
     compId: "articles-docs-install-whisper-cpp-index",
     crumb: "Transcribe audio locally",
   },
-  {
-    id: "install-whisper-cpp/install-whisper-cpp",
-    title: "installWhisperCpp()",
-    relativePath: "docs/install-whisper-cpp/install-whisper-cpp.mdx",
-    compId: "articles-docs-install-whisper-cpp-install-whisper-cpp",
-    crumb: "@remotion/install-whisper-cpp",
-  },
   {
     id: "install-whisper-cpp/transcribe",
     title: "transcribe()",
@@ -973,710 +840,706 @@ export const articles = [
     crumb: "@remotion/install-whisper-cpp",
   },
   {
-    id: "interpolate-colors",
-    title: "interpolateColors()",
-    relativePath: "docs/interpolate-colors.mdx",
-    compId: "articles-docs-interpolate-colors",
-    crumb: "API",
-  },
-  {
-    id: "interpolate",
-    title: "interpolate()",
-    relativePath: "docs/interpolate.mdx",
-    compId: "articles-docs-interpolate",
-    crumb: "API",
+    id: "install-whisper-cpp/convert-to-captions",
+    title: "convertToCaptions()",
+    relativePath: "docs/install-whisper-cpp/convert-to-captions.mdx",
+    compId: "articles-docs-install-whisper-cpp-convert-to-captions",
+    crumb: "@remotion/install-whisper-cpp",
   },
   {
-    id: "javascript",
-    title: "Plain JavaScript",
-    relativePath: "docs/jsx-support.mdx",
-    compId: "articles-docs-jsx-support",
-    crumb: "How To",
+    id: "install-whisper-cpp/download-whisper-model",
+    title: "downloadWhisperModel()",
+    relativePath: "docs/install-whisper-cpp/download-whisper-model.mdx",
+    compId: "articles-docs-install-whisper-cpp-download-whisper-model",
+    crumb: "@remotion/install-whisper-cpp",
   },
   {
-    id: "lambda/api",
-    title: "@remotion/lambda",
-    relativePath: "docs/lambda/api.mdx",
-    compId: "articles-docs-lambda-api",
-    crumb: "Render videos without servers on AWS",
+    id: "get-audio-duration-in-seconds",
+    title: "getAudioDurationInSeconds()",
+    relativePath: "docs/get-audio-duration-in-seconds.mdx",
+    compId: "articles-docs-get-audio-duration-in-seconds",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "authentication",
-    title: "Authentication",
-    relativePath: "docs/lambda/authentication.mdx",
-    compId: "articles-docs-lambda-authentication",
-    crumb: "Lambda",
+    id: "dataset-render",
+    title: "Render videos programmatically from a dataset",
+    relativePath: "docs/dataset-render.mdx",
+    compId: "articles-docs-dataset-render",
+    crumb: "Tutorials",
   },
   {
-    id: "autodelete",
-    title: "Auto-delete renders",
-    relativePath: "docs/lambda/autodelete.mdx",
-    compId: "articles-docs-lambda-autodelete",
-    crumb: "Lambda API",
+    id: "prefetch",
+    title: "prefetch()",
+    relativePath: "docs/prefetch.mdx",
+    compId: "articles-docs-prefetch",
+    crumb: "API",
   },
   {
-    id: "lambda/bucket-naming",
-    title: "Bucket names in Remotion Lambda",
-    relativePath: "docs/lambda/bucket-naming.mdx",
-    compId: "articles-docs-lambda-bucket-naming",
-    crumb: "@remotion/lambda",
+    id: "audio-buffer-to-data-url",
+    title: "audioBufferToDataUrl()",
+    relativePath: "docs/audiobuffertodataurl.mdx",
+    compId: "articles-docs-audiobuffertodataurl",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "changelog",
-    title: "Prerelease Changelog",
-    relativePath: "docs/lambda/changelog.mdx",
-    compId: "articles-docs-lambda-changelog",
-    crumb: null,
+    id: "animating-properties",
+    title: "Animating properties",
+    relativePath: "docs/animating-properties.mdx",
+    compId: "articles-docs-animating-properties",
+    crumb: "The basics",
   },
   {
-    id: "checklist",
-    title: "Production Checklist",
-    relativePath: "docs/lambda/checklist.mdx",
-    compId: "articles-docs-lambda-checklist",
-    crumb: "Lambda",
+    id: "getstaticfiles",
+    title: "getStaticFiles()",
+    relativePath: "docs/get-static-files.mdx",
+    compId: "articles-docs-get-static-files",
+    crumb: "API",
   },
   {
-    id: "lambda/cli/compositions",
-    title: "npx remotion lambda compositions",
-    relativePath: "docs/lambda/cli/compositions.mdx",
-    compId: "articles-docs-lambda-cli-compositions",
-    crumb: "Lambda CLI Reference",
+    id: "paths/reverse-path",
+    title: "reversePath()",
+    relativePath: "docs/paths/reverse-path.mdx",
+    compId: "articles-docs-paths-reverse-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "functions",
-    title: "npx remotion lambda functions",
-    relativePath: "docs/lambda/cli/functions.mdx",
-    compId: "articles-docs-lambda-cli-functions",
-    crumb: "Lambda CLI Reference",
+    id: "paths/evolve-path",
+    title: "evolvePath()",
+    relativePath: "docs/paths/evolve-path.mdx",
+    compId: "articles-docs-paths-evolve-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "policies",
-    title: "npx remotion lambda policies",
-    relativePath: "docs/lambda/cli/policies.mdx",
-    compId: "articles-docs-lambda-cli-policies",
-    crumb: "Lambda CLI Reference",
+    id: "paths/get-subpaths",
+    title: "getSubpaths()",
+    relativePath: "docs/paths/get-subpaths.mdx",
+    compId: "articles-docs-paths-get-subpaths",
+    crumb: "@remotion/paths",
   },
   {
-    id: "quotas",
-    title: "npx remotion lambda quotas",
-    relativePath: "docs/lambda/cli/quotas.mdx",
-    compId: "articles-docs-lambda-cli-quotas",
-    crumb: "Lambda CLI Reference",
+    id: "paths/get-point-at-length",
+    title: "getPointAtLength()",
+    relativePath: "docs/paths/get-point-at-length.mdx",
+    compId: "articles-docs-paths-get-point-at-length",
+    crumb: "@remotion/paths",
   },
   {
-    id: "regions",
-    title: "npx remotion lambda regions",
-    relativePath: "docs/lambda/cli/regions.mdx",
-    compId: "articles-docs-lambda-cli-regions",
-    crumb: "Lambda CLI Reference",
+    id: "paths/get-length",
+    title: "getLength()",
+    relativePath: "docs/paths/get-length.mdx",
+    compId: "articles-docs-paths-get-length",
+    crumb: "@remotion/paths",
   },
   {
-    id: "render",
-    title: "npx remotion lambda render",
-    relativePath: "docs/lambda/cli/render.mdx",
-    compId: "articles-docs-lambda-cli-render",
-    crumb: "Lambda CLI Reference",
+    id: "paths/scale-path",
+    title: "scalePath()",
+    relativePath: "docs/paths/scale-path.mdx",
+    compId: "articles-docs-paths-scale-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "sites",
-    title: "npx remotion lambda sites",
-    relativePath: "docs/lambda/cli/sites.mdx",
-    compId: "articles-docs-lambda-cli-sites",
-    crumb: "Lambda CLI Reference",
+    id: "paths/get-parts",
+    title: "getParts()",
+    relativePath: "docs/paths/get-parts.mdx",
+    compId: "articles-docs-paths-get-parts",
+    crumb: "@remotion/paths",
   },
   {
-    id: "still",
-    title: "npx remotion lambda still",
-    relativePath: "docs/lambda/cli/still.mdx",
-    compId: "articles-docs-lambda-cli-still",
-    crumb: "Lambda CLI Reference",
+    id: "paths/get-tangent-at-length",
+    title: "getTangentAtLength()",
+    relativePath: "docs/paths/get-tangent-at-length.mdx",
+    compId: "articles-docs-paths-get-tangent-at-length",
+    crumb: "@remotion/paths",
   },
   {
-    id: "cli",
-    title: "@remotion/lambda - CLI",
-    relativePath: "docs/lambda/cli.mdx",
-    compId: "articles-docs-lambda-cli",
-    crumb: null,
+    id: "paths/get-instruction-index-at-length",
+    title: "getInstructionIndexAtLength()",
+    relativePath: "docs/paths/get-instruction-index-at-length.mdx",
+    compId: "articles-docs-paths-get-instruction-index-at-length",
+    crumb: "@remotion/paths",
   },
   {
-    id: "concurrency",
-    title: "Concurrency",
-    relativePath: "docs/lambda/concurrency.mdx",
-    compId: "articles-docs-lambda-concurrency",
-    crumb: "Lambda",
+    id: "paths/extend-viewbox",
+    title: "extendViewBox()",
+    relativePath: "docs/paths/extend-viewbox.mdx",
+    compId: "articles-docs-paths-extend-viewbox",
+    crumb: "@remotion/paths",
   },
-  {
-    id: "optimizing-cost",
-    title: "Optimizing for cost",
-    relativePath: "docs/lambda/cost.mdx",
-    compId: "articles-docs-lambda-cost",
-    crumb: "Lambda",
+  {
+    id: "paths/parse-path",
+    title: "parsePath()",
+    relativePath: "docs/paths/parse-path.mdx",
+    compId: "articles-docs-paths-parse-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "custom-destination",
-    title: "Customizing Lambda output destination",
-    relativePath: "docs/lambda/custom-destination.mdx",
-    compId: "articles-docs-lambda-custom-destination",
-    crumb: "Lambda",
+    id: "paths/translate-path",
+    title: "translatePath()",
+    relativePath: "docs/paths/translate-path.mdx",
+    compId: "articles-docs-paths-translate-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "lambda/custom-layers",
-    title: "Custom Layers",
-    relativePath: "docs/lambda/custom-layers.mdx",
-    compId: "articles-docs-lambda-custom-layers",
-    crumb: "Lambda",
+    id: "paths/normalize-path",
+    title: "normalizePath()",
+    relativePath: "docs/paths/normalize-path.mdx",
+    compId: "articles-docs-paths-normalize-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "deletefunction",
-    title: "deleteFunction()",
-    relativePath: "docs/lambda/deletefunction.mdx",
-    compId: "articles-docs-lambda-deletefunction",
-    crumb: "Lambda API",
+    id: "paths/index",
+    title: "@remotion/paths",
+    relativePath: "docs/paths/index.mdx",
+    compId: "articles-docs-paths-index",
+    crumb: "SVG",
   },
   {
-    id: "deleterender",
-    title: "deleteRender()",
-    relativePath: "docs/lambda/deleterender.mdx",
-    compId: "articles-docs-lambda-deleterender",
-    crumb: "Lambda API",
+    id: "paths/get-bounding-box",
+    title: "getBoundingBox()",
+    relativePath: "docs/paths/get-bounding-box.mdx",
+    compId: "articles-docs-paths-get-bounding-box",
+    crumb: "@remotion/paths",
   },
   {
-    id: "deletesite",
-    title: "deleteSite()",
-    relativePath: "docs/lambda/deletesite.mdx",
-    compId: "articles-docs-lambda-deletesite",
-    crumb: "Lambda API",
+    id: "paths/warp-path",
+    title: "warpPath()",
+    relativePath: "docs/paths/warp-path.mdx",
+    compId: "articles-docs-paths-warp-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "deployfunction",
-    title: "deployFunction()",
-    relativePath: "docs/lambda/deployfunction.mdx",
-    compId: "articles-docs-lambda-deployfunction",
-    crumb: "Lambda API",
+    id: "paths/interpolate-path",
+    title: "interpolatePath()",
+    relativePath: "docs/paths/interpolate-path.mdx",
+    compId: "articles-docs-paths-interpolate-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "deploysite",
-    title: "deploySite()",
-    relativePath: "docs/lambda/deploysite.mdx",
-    compId: "articles-docs-lambda-deploysite",
-    crumb: "Lambda API",
+    id: "paths/serialize-instructions",
+    title: "serializeInstructions()",
+    relativePath: "docs/paths/serialize-instructions.mdx",
+    compId: "articles-docs-paths-serialize-instructions",
+    crumb: "@remotion/paths",
   },
   {
-    id: "disk-size",
-    title: "Disk size",
-    relativePath: "docs/lambda/disk-size.mdx",
-    compId: "articles-docs-lambda-disk-size",
-    crumb: "Lambda",
+    id: "paths/reset-path",
+    title: "resetPath()",
+    relativePath: "docs/paths/reset-path.mdx",
+    compId: "articles-docs-paths-reset-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "downloadmedia",
-    title: "downloadMedia()",
-    relativePath: "docs/lambda/downloadmedia.mdx",
-    compId: "articles-docs-lambda-downloadmedia",
-    crumb: "Lambda API",
+    id: "paths/reduce-instructions",
+    title: "reduceInstructions()",
+    relativePath: "docs/paths/reduce-instructions.mdx",
+    compId: "articles-docs-paths-reduce-instructions",
+    crumb: "@remotion/paths",
   },
   {
-    id: "downloadvideo",
-    title: "downloadVideo()",
-    relativePath: "docs/lambda/downloadvideo.mdx",
-    compId: "articles-docs-lambda-downloadvideo",
-    crumb: "Lambda API",
+    id: "rive/index",
+    title: "@remotion/rive",
+    relativePath: "docs/rive/index.mdx",
+    compId: "articles-docs-rive-index",
+    crumb: "Integrations",
   },
   {
-    id: "estimateprice",
-    title: "estimatePrice()",
-    relativePath: "docs/lambda/estimateprice.mdx",
-    compId: "articles-docs-lambda-estimateprice",
-    crumb: "Lambda API",
+    id: "rive/remotionrivecanvas",
+    title: "<RemotionRiveCanvas>",
+    relativePath: "docs/rive/remotionrivecanvas.mdx",
+    compId: "articles-docs-rive-remotionrivecanvas",
+    crumb: "@remotion/rive",
   },
   {
-    id: "lambda/faq",
-    title: "FAQ",
-    relativePath: "docs/lambda/faq.mdx",
-    compId: "articles-docs-lambda-faq",
-    crumb: "Lambda",
+    id: "layers",
+    title: "Layers",
+    relativePath: "docs/layers.mdx",
+    compId: "articles-docs-layers",
+    crumb: "Designing videos",
   },
   {
-    id: "feb-2022-outage",
-    title: "February 2022 Outage",
-    relativePath: "docs/lambda/feb-2022-outage.mdx",
-    compId: "articles-docs-lambda-feb-2022-outage",
-    crumb: null,
+    id: "use-img-and-iframe",
+    title: "<Img>, <Video> and <Audio>",
+    relativePath: "docs/use-img-and-iframe.mdx",
+    compId: "articles-docs-use-img-and-iframe",
+    crumb: "Best practices",
   },
   {
-    id: "lambda/feb-2023-incident",
-    title: "Upgrade your Lambda functions to prevent breakage",
-    relativePath: "docs/lambda/feb-2023-incident.mdx",
-    compId: "articles-docs-lambda-feb-2023-incident",
-    crumb: "DevOps advisory",
+    id: "font-picker",
+    title: "Build a Google Font picker",
+    relativePath: "docs/font-picker.mdx",
+    compId: "articles-docs-font-picker",
+    crumb: "Building video apps",
   },
   {
-    id: "getawsclient",
-    title: "getAwsClient()",
-    relativePath: "docs/lambda/getawsclient.mdx",
-    compId: "articles-docs-lambda-getawsclient",
-    crumb: "Lambda API",
+    id: "support",
+    title: "Support Policy",
+    relativePath: "docs/support.mdx",
+    compId: "articles-docs-support",
+    crumb: "Let us help you",
   },
   {
-    id: "getcompositionsonlambda",
-    title: "getCompositionsOnLambda()",
-    relativePath: "docs/lambda/getcompositionsonlambda.mdx",
-    compId: "articles-docs-lambda-getcompositionsonlambda",
-    crumb: "Lambda API",
+    id: "register-root",
+    title: "registerRoot()",
+    relativePath: "docs/register-root.mdx",
+    compId: "articles-docs-register-root",
+    crumb: "API",
   },
   {
-    id: "getfunctioninfo",
-    title: "getFunctionInfo()",
-    relativePath: "docs/lambda/getfunctioninfo.mdx",
-    compId: "articles-docs-lambda-getfunctioninfo",
-    crumb: "Lambda API",
+    id: "props-resolution",
+    title: "How props get resolved",
+    relativePath: "docs/how-props-flow.mdx",
+    compId: "articles-docs-how-props-flow",
+    crumb: "Parameterized videos",
   },
   {
-    id: "getfunctions",
-    title: "getFunctions()",
-    relativePath: "docs/lambda/getfunctions.mdx",
-    compId: "articles-docs-lambda-getfunctions",
-    crumb: "Lambda API",
+    id: "preview",
+    title: "Preview your video",
+    relativePath: "docs/preview.mdx",
+    compId: "articles-docs-preview",
+    crumb: "Timeline basics",
   },
   {
-    id: "getorcreatebucket",
-    title: "getOrCreateBucket()",
-    relativePath: "docs/lambda/getorcreatebucket.mdx",
-    compId: "articles-docs-lambda-getorcreatebucket",
-    crumb: "Lambda API",
+    id: "shapes/triangle",
+    title: "<Triangle />",
+    relativePath: "docs/shapes/triangle.mdx",
+    compId: "articles-docs-shapes-triangle",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "getregions",
-    title: "getRegions()",
-    relativePath: "docs/lambda/getregions.mdx",
-    compId: "articles-docs-lambda-getregions",
-    crumb: "Lambda API",
+    id: "shapes/rect",
+    title: "<Rect />",
+    relativePath: "docs/shapes/rect.mdx",
+    compId: "articles-docs-shapes-rect",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "getrenderprogress",
-    title: "getRenderProgress()",
-    relativePath: "docs/lambda/getrenderprogress.mdx",
-    compId: "articles-docs-lambda-getrenderprogress",
-    crumb: "Lambda API",
+    id: "shapes/polygon",
+    title: "<Polygon />",
+    relativePath: "docs/shapes/polygon.mdx",
+    compId: "articles-docs-shapes-polygon",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "getrolepolicy",
-    title: "getRolePolicy()",
-    relativePath: "docs/lambda/getrolepolicy.mdx",
-    compId: "articles-docs-lambda-getrolepolicy",
-    crumb: "Lambda API",
+    id: "shapes/circle",
+    title: "<Circle />",
+    relativePath: "docs/shapes/circle.mdx",
+    compId: "articles-docs-shapes-circle",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "getsites",
-    title: "getSites()",
-    relativePath: "docs/lambda/getsites.mdx",
-    compId: "articles-docs-lambda-getsites",
-    crumb: "Lambda API",
+    id: "shapes/make-rect",
+    title: "makeRect()",
+    relativePath: "docs/shapes/make-rect.mdx",
+    compId: "articles-docs-shapes-make-rect",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "getuserpolicy",
-    title: "getUserPolicy()",
-    relativePath: "docs/lambda/getuserpolicy.mdx",
-    compId: "articles-docs-lambda-getuserpolicy",
-    crumb: "Lambda API",
+    id: "shapes/index",
+    title: "@remotion/shapes",
+    relativePath: "docs/shapes/index.mdx",
+    compId: "articles-docs-shapes-index",
+    crumb: "SVG component library",
   },
   {
-    id: "lambda/go",
-    title: "Triggering renders from Go",
-    relativePath: "docs/lambda/go.mdx",
-    compId: "articles-docs-lambda-go",
-    crumb: "@remotion/lambda",
+    id: "shapes/ellipse",
+    title: "<Ellipse />",
+    relativePath: "docs/shapes/ellipse.mdx",
+    compId: "articles-docs-shapes-ellipse",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "how-lambda-works",
-    title: "How Remotion Lambda works",
-    relativePath: "docs/lambda/how-lambda-works.mdx",
-    compId: "articles-docs-lambda-how-lambda-works",
-    crumb: "Lambda",
+    id: "shapes/make-triangle",
+    title: "makeTriangle()",
+    relativePath: "docs/shapes/make-triangle.mdx",
+    compId: "articles-docs-shapes-make-triangle",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "lambda/insights",
-    title: "Enable Lambda Insights",
-    relativePath: "docs/lambda/insights.mdx",
-    compId: "articles-docs-lambda-insights",
-    crumb: "Lambda",
+    id: "shapes/pie",
+    title: "<Pie />",
+    relativePath: "docs/shapes/pie.mdx",
+    compId: "articles-docs-shapes-pie",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "lambda/light-client",
-    title: "Light client",
-    relativePath: "docs/lambda/light-client.mdx",
-    compId: "articles-docs-lambda-light-client",
-    crumb: "Lambda",
+    id: "shapes/make-star",
+    title: "makeStar()",
+    relativePath: "docs/shapes/make-star.mdx",
+    compId: "articles-docs-shapes-make-star",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "lambda/limits",
-    title: "Lambda Limits",
-    relativePath: "docs/lambda/limits.mdx",
-    compId: "articles-docs-lambda-limits",
-    crumb: "Lambda",
+    id: "shapes/make-pie",
+    title: "makePie()",
+    relativePath: "docs/shapes/make-pie.mdx",
+    compId: "articles-docs-shapes-make-pie",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "lambda/multiple-buckets",
-    title: "Multiple buckets in Remotion Lambda",
-    relativePath: "docs/lambda/multiple-buckets.mdx",
-    compId: "articles-docs-lambda-multiple-buckets",
-    crumb: "@remotion/lambda",
+    id: "shapes/make-ellipse",
+    title: "makeEllipse()",
+    relativePath: "docs/shapes/make-ellipse.mdx",
+    compId: "articles-docs-shapes-make-ellipse",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "naming-convention",
-    title: "Function naming convention",
-    relativePath: "docs/lambda/naming-convention.mdx",
-    compId: "articles-docs-lambda-naming-convention",
-    crumb: "Lambda",
+    id: "shapes/star",
+    title: "<Star />",
+    relativePath: "docs/shapes/star.mdx",
+    compId: "articles-docs-shapes-star",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "lambda/permissions",
-    title: "Permissions",
-    relativePath: "docs/lambda/permissions.mdx",
-    compId: "articles-docs-lambda-permissions",
-    crumb: "Lambda",
+    id: "shapes/make-polygon",
+    title: "makePolygon()",
+    relativePath: "docs/shapes/make-polygon.mdx",
+    compId: "articles-docs-shapes-make-polygon",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "lambda/php",
-    title: "Triggering renders from PHP",
-    relativePath: "docs/lambda/php.mdx",
-    compId: "articles-docs-lambda-php",
-    crumb: "@remotion/lambda",
+    id: "shapes/make-circle",
+    title: "makeCircle()",
+    relativePath: "docs/shapes/make-circle.mdx",
+    compId: "articles-docs-shapes-make-circle",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "presignurl",
-    title: "presignUrl()",
-    relativePath: "docs/lambda/presignurl.mdx",
-    compId: "articles-docs-lambda-presignurl",
-    crumb: "Lambda API",
+    id: "series",
+    title: "<Series>",
+    relativePath: "docs/series.mdx",
+    compId: "articles-docs-series",
+    crumb: "API",
   },
   {
-    id: "lambda/python",
-    title: "Triggering renders from Python",
-    relativePath: "docs/lambda/python.mdx",
-    compId: "articles-docs-lambda-python",
-    crumb: "@remotion/lambda",
+    id: "bun",
+    title: "Bun support",
+    relativePath: "docs/bun.mdx",
+    compId: "articles-docs-bun",
+    crumb: "bun bun bun bun bun",
   },
   {
-    id: "region-selection",
-    title: "Region selection",
-    relativePath: "docs/lambda/region-selection.mdx",
-    compId: "articles-docs-lambda-region-selection",
-    crumb: "Lambda",
+    id: "loading-root-component",
+    title: "Root component Timeout",
+    relativePath: "docs/troubleshooting/loading-root-component.mdx",
+    compId: "articles-docs-troubleshooting-loading-root-component",
+    crumb: "Troubleshooting",
   },
   {
-    id: "rendermediaonlambda",
-    title: "renderMediaOnLambda()",
-    relativePath: "docs/lambda/rendermediaonlambda.mdx",
-    compId: "articles-docs-lambda-rendermediaonlambda",
-    crumb: "Lambda API",
+    id: "troubleshooting/background-image",
+    title: "Flickering when using background-image or mask-image",
+    relativePath: "docs/troubleshooting/background-image.mdx",
+    compId: "articles-docs-troubleshooting-background-image",
+    crumb: "Common mistakes",
   },
   {
-    id: "renderstillonlambda",
-    title: "renderStillOnLambda()",
-    relativePath: "docs/lambda/renderstillonlambda.mdx",
-    compId: "articles-docs-lambda-renderstillonlambda",
-    crumb: "Lambda API",
+    id: "rosetta",
+    title: "Apple Silicon under Rosetta",
+    relativePath: "docs/troubleshooting/rosetta.mdx",
+    compId: "articles-docs-troubleshooting-rosetta",
+    crumb: "Troubleshooting",
   },
   {
-    id: "rendervideoonlambda",
-    title: "renderVideoOnLambda()",
-    relativePath: "docs/lambda/rendervideoonlambda.mdx",
-    compId: "articles-docs-lambda-rendervideoonlambda",
-    crumb: "Lambda API",
+    id: "troubleshooting/delay-render-proxy",
+    title: "Loading <Img> with src http://localhost:3000/proxy",
+    relativePath: "docs/troubleshooting/delay-render-proxy.mdx",
+    compId: "articles-docs-troubleshooting-delay-render-proxy",
+    crumb: "Troubleshooting",
   },
   {
-    id: "runtime",
-    title: "Runtime",
-    relativePath: "docs/lambda/runtime.mdx",
-    compId: "articles-docs-lambda-runtime",
-    crumb: "Lambda",
+    id: "broken-fast-refresh",
+    title: "Fast Refresh not working",
+    relativePath: "docs/troubleshooting/broken-fast-refresh.mdx",
+    compId: "articles-docs-troubleshooting-broken-fast-refresh",
+    crumb: "Troubleshooting",
   },
   {
-    id: "lambda/s3-public-access",
-    title: "Cannot create a S3 bucket using Remotion",
-    relativePath: "docs/lambda/s3-public-access.mdx",
-    compId: "articles-docs-lambda-s3-public-access",
-    crumb: "DevOps advisory",
+    id: "troubleshooting/timed-out-page-function",
+    title: "Timed out evaluating page function",
+    relativePath: "docs/troubleshooting/timed-out-page-function.mdx",
+    compId: "articles-docs-troubleshooting-timed-out-page-function",
+    crumb: "Troubleshooting",
   },
   {
-    id: "lambda/serverless-framework-integration",
-    title: "Using the Serverless Framework with Remotion Lambda",
-    relativePath: "docs/lambda/serverless-framework-integration.mdx",
-    compId: "articles-docs-lambda-serverless-framework-integration",
-    crumb: "@remotion/lambda",
+    id: "troubleshooting/browser-launch",
+    title: "Failed to launch the browser process",
+    relativePath: "docs/troubleshooting/browser-launch.mdx",
+    compId: "articles-docs-troubleshooting-browser-launch",
+    crumb: "Troubleshooting",
   },
   {
-    id: "setup",
-    title: "Setup",
-    relativePath: "docs/lambda/setup.mdx",
-    compId: "articles-docs-lambda-setup",
-    crumb: "Lambda",
+    id: "troubleshooting/sigkill",
+    title: "Process quit with signal SIGKILL",
+    relativePath: "docs/troubleshooting/sigkill.mdx",
+    compId: "articles-docs-troubleshooting-sigkill",
+    crumb: "Troubleshooting",
   },
   {
-    id: "simulatepermissions",
-    title: "simulatePermissions()",
-    relativePath: "docs/lambda/simulatepermissions.mdx",
-    compId: "articles-docs-lambda-simulatepermissions",
-    crumb: "Lambda API",
+    id: "troubleshooting/nextjs-image",
+    title: "Flickering when using Next.js <Image> tag",
+    relativePath: "docs/troubleshooting/nextjs-image.mdx",
+    compId: "articles-docs-troubleshooting-nextjs-image",
+    crumb: "Common mistakes",
   },
   {
-    id: "speculatefunctionname",
-    title: "speculateFunctionName()",
-    relativePath: "docs/lambda/speculateFunctionName.mdx",
-    compId: "articles-docs-lambda-speculateFunctionName",
-    crumb: "Lambda API",
+    id: "player-flicker",
+    title: "Avoiding flickering in <Player>",
+    relativePath: "docs/troubleshooting/video-flicker.mdx",
+    compId: "articles-docs-troubleshooting-video-flicker",
+    crumb: "Frame-perfection",
   },
   {
-    id: "optimizing-speed",
-    title: "Optimizing for speed",
-    relativePath: "docs/lambda/speed.mdx",
-    compId: "articles-docs-lambda-speed",
-    crumb: "Lambda",
+    id: "troubleshooting/debug-failed-render",
+    title: "Debugging render failures",
+    relativePath: "docs/troubleshooting/debug-failed-render.mdx",
+    compId: "articles-docs-troubleshooting-debug-failed-render",
+    crumb: "Troubleshooting",
   },
   {
-    id: "lambda/sqs",
-    title: "Using Lambda with SQS",
-    relativePath: "docs/lambda/sqs.mdx",
-    compId: "articles-docs-lambda-sqs",
-    crumb: "@remotion/lambda",
+    id: "defaultprops-too-big",
+    title: "defaultProps too big - could not serialize",
+    relativePath: "docs/troubleshooting/defaultprops-too-big.mdx",
+    compId: "articles-docs-troubleshooting-defaultprops-too-big",
+    crumb: "Troubleshooting",
   },
   {
-    id: "bucket-disallows-acl",
-    title: "The bucket does not allow ACLs",
-    relativePath: "docs/lambda/troubleshooting/bucket-disallows-acl.mdx",
-    compId: "articles-docs-lambda-troubleshooting-bucket-disallows-acl",
-    crumb: "Lambda Troubleshooting",
+    id: "troubleshooting/bundling-bundle",
+    title: "Calling bundle() in bundled code",
+    relativePath: "docs/troubleshooting/bundling-bundle.mdx",
+    compId: "articles-docs-troubleshooting-bundling-bundle",
+    crumb: "Troubleshooting",
   },
   {
-    id: "debug",
-    title: "Debugging failed Lambda renders",
-    relativePath: "docs/lambda/troubleshooting/debug.mdx",
-    compId: "articles-docs-lambda-troubleshooting-debug",
-    crumb: "Lambda",
+    id: "troubleshooting/could-not-be-parsed-as-a-value-list",
+    title: "The source provided could not be parsed as a value list",
+    relativePath:
+      "docs/troubleshooting/could-not-be-parsed-as-a-value-list.mdx",
+    compId: "articles-docs-troubleshooting-could-not-be-parsed-as-a-value-list",
+    crumb: "Browser quirks",
   },
   {
-    id: "permissions",
-    title: "AWS Permissions Troubleshooting",
-    relativePath: "docs/lambda/troubleshooting/permissions.mdx",
-    compId: "articles-docs-lambda-troubleshooting-permissions",
-    crumb: "Lambda Troubleshooting",
+    id: "cli/studio",
+    title: "npx remotion studio",
+    relativePath: "docs/cli/studio.mdx",
+    compId: "articles-docs-cli-studio",
+    crumb: "CLI Reference",
   },
   {
-    id: "rate-limit",
-    title: "AWS Rate Limit Troubleshooting",
-    relativePath: "docs/lambda/troubleshooting/rate-limit.mdx",
-    compId: "articles-docs-lambda-troubleshooting-rate-limit",
-    crumb: "Lambda Troubleshooting",
+    id: "cli",
+    title: "Command line reference",
+    relativePath: "docs/cli/cli.mdx",
+    compId: "articles-docs-cli-cli",
+    crumb: null,
   },
   {
-    id: "lambda/troubleshooting/security-token",
-    title: "'\"The security token included in the request is invalid\"'",
-    relativePath: "docs/lambda/troubleshooting/security-token.mdx",
-    compId: "articles-docs-lambda-troubleshooting-security-token",
-    crumb: "Lambda",
+    id: "cli/bundle",
+    title: "npx remotion bundle",
+    relativePath: "docs/cli/bundle.mdx",
+    compId: "articles-docs-cli-bundle",
+    crumb: "CLI Reference",
   },
   {
-    id: "unrecognizedclientexception",
-    title: "UnrecognizedClientException",
-    relativePath: "docs/lambda/troubleshooting/unrecognizedclientexception.mdx",
-    compId: "articles-docs-lambda-troubleshooting-unrecognizedclientexception",
-    crumb: "Lambda Troubleshooting",
+    id: "cli/gpu",
+    title: "npx remotion gpu",
+    relativePath: "docs/cli/gpu.mdx",
+    compId: "articles-docs-cli-gpu",
+    crumb: "CLI Reference",
   },
   {
-    id: "uninstall",
-    title: "Uninstall Lambda",
-    relativePath: "docs/lambda/uninstall.mdx",
-    compId: "articles-docs-lambda-uninstall",
-    crumb: null,
+    id: "cli/install",
+    title: "npx remotion install",
+    relativePath: "docs/cli/install.mdx",
+    compId: "articles-docs-cli-install",
+    crumb: "CLI Reference",
   },
   {
-    id: "upgrading",
-    title: "Upgrading Lambda",
-    relativePath: "docs/lambda/upgrading.mdx",
-    compId: "articles-docs-lambda-upgrading",
-    crumb: null,
+    id: "cli/versions",
+    title: "npx remotion versions",
+    relativePath: "docs/cli/versions.mdx",
+    compId: "articles-docs-cli-versions",
+    crumb: "CLI Reference",
   },
   {
-    id: "validatewebhooksignature",
-    title: "validateWebhookSignature()",
-    relativePath: "docs/lambda/validatewebhooksignature.mdx",
-    compId: "articles-docs-lambda-validatewebhooksignature",
-    crumb: "Lambda API",
+    id: "cli/browser/index",
+    title: "npx remotion browser",
+    relativePath: "docs/cli/browser/index.mdx",
+    compId: "articles-docs-cli-browser-index",
+    crumb: "@remotion/cli",
   },
   {
-    id: "lambda/webhooks",
-    title: "Webhooks",
-    relativePath: "docs/lambda/webhooks.mdx",
-    compId: "articles-docs-lambda-webhooks",
-    crumb: "Lambda",
+    id: "cli/browser/ensure",
+    title: "npx remotion browser ensure",
+    relativePath: "docs/cli/browser/ensure.mdx",
+    compId: "articles-docs-cli-browser-ensure",
+    crumb: "@remotion/cli",
   },
   {
-    id: "lambda/without-iam/ec2",
-    title: "Authenticating Lambda with EC2",
-    relativePath: "docs/lambda/without-iam/ec2.mdx",
-    compId: "articles-docs-lambda-without-iam-ec2",
-    crumb: "@remotion/lambda",
+    id: "ffprobe",
+    title: "npx remotion ffprobe",
+    relativePath: "docs/cli/ffprobe.mdx",
+    compId: "articles-docs-cli-ffprobe",
+    crumb: "@remotion/cli",
   },
   {
-    id: "lambda/without-iam/example",
-    title: "Example setup without IAM user",
-    relativePath: "docs/lambda/without-iam/example.mdx",
-    compId: "articles-docs-lambda-without-iam-example",
-    crumb: "IAM Roles Example",
+    id: "cli/render",
+    title: "npx remotion render",
+    relativePath: "docs/cli/render.mdx",
+    compId: "articles-docs-cli-render",
+    crumb: "CLI Reference",
   },
   {
-    id: "lambda/without-iam/index",
-    title: "Using Lambda with IAM roles",
-    relativePath: "docs/lambda/without-iam/index.mdx",
-    compId: "articles-docs-lambda-without-iam-index",
-    crumb: "Lambda without IAM",
+    id: "cli/compositions",
+    title: "npx remotion compositions",
+    relativePath: "docs/cli/compositions.mdx",
+    compId: "articles-docs-cli-compositions",
+    crumb: "CLI Reference",
   },
   {
-    id: "lambda",
-    title: "@remotion/lambda",
-    relativePath: "docs/lambda.mdx",
-    compId: "articles-docs-lambda",
-    crumb: null,
+    id: "cli/benchmark",
+    title: "npx remotion benchmark",
+    relativePath: "docs/cli/benchmark.mdx",
+    compId: "articles-docs-cli-benchmark",
+    crumb: "CLI Reference",
   },
   {
-    id: "layers",
-    title: "Layers",
-    relativePath: "docs/layers.mdx",
-    compId: "articles-docs-layers",
-    crumb: "Designing videos",
+    id: "ffmpeg",
+    title: "npx remotion ffmpeg",
+    relativePath: "docs/cli/ffmpeg.mdx",
+    compId: "articles-docs-cli-ffmpeg",
+    crumb: "@remotion/cli",
   },
   {
-    id: "layout-utils/best-practices",
-    title: "Best practices for @remotion/layout-utils",
-    relativePath: "docs/layout-utils/best-practices.mdx",
-    compId: "articles-docs-layout-utils-best-practices",
-    crumb: "@remotion/layout-utils",
+    id: "cli/still",
+    title: "npx remotion still",
+    relativePath: "docs/cli/still.mdx",
+    compId: "articles-docs-cli-still",
+    crumb: "CLI Reference",
   },
   {
-    id: "layout-utils/fill-text-box",
-    title: "fillTextBox()",
-    relativePath: "docs/layout-utils/fill-text-box.mdx",
-    compId: "articles-docs-layout-utils-fill-text-box",
-    crumb: "@remotion/layout-utils",
+    id: "cli/help",
+    title: "npx remotion help",
+    relativePath: "docs/cli/help.mdx",
+    compId: "articles-docs-cli-help",
+    crumb: "CLI Reference",
   },
   {
-    id: "layout-utils/fit-text",
-    title: "fitText()",
-    relativePath: "docs/layout-utils/fit-text.mdx",
-    compId: "articles-docs-layout-utils-fit-text",
-    crumb: "@remotion/layout-utils",
+    id: "cli/upgrade",
+    title: "npx remotion upgrade",
+    relativePath: "docs/cli/upgrade.mdx",
+    compId: "articles-docs-cli-upgrade",
+    crumb: "CLI Reference",
   },
   {
-    id: "layout-utils/index",
-    title: "@remotion/layout-utils",
-    relativePath: "docs/layout-utils/index.mdx",
-    compId: "articles-docs-layout-utils-index",
-    crumb: null,
+    id: "watchstaticfile",
+    title: "watchStaticFile()",
+    relativePath: "docs/watch-static-file.mdx",
+    compId: "articles-docs-watch-static-file",
+    crumb: "API",
   },
   {
-    id: "layout-utils/measure-text",
-    title: "measureText()",
-    relativePath: "docs/layout-utils/measure-text.mdx",
-    compId: "articles-docs-layout-utils-measure-text",
-    crumb: "@remotion/layout-utils",
+    id: "chromium-flags",
+    title: "Chromium flags",
+    relativePath: "docs/chromium-flags.mdx",
+    compId: "articles-docs-chromium-flags",
+    crumb: "Tweaks",
   },
   {
-    id: "legacy-babel",
-    title: "Using legacy Babel transpilation",
-    relativePath: "docs/legacy-babel-loader.mdx",
-    compId: "articles-docs-legacy-babel-loader",
-    crumb: "How To",
+    id: "noise-visualization",
+    title: "Noise visualization",
+    relativePath: "docs/noise-visualization.mdx",
+    compId: "articles-docs-noise-visualization",
+    crumb: "Techniques",
   },
   {
-    id: "license",
-    title: "License & Pricing",
-    relativePath: "docs/license.mdx",
-    compId: "articles-docs-license",
-    crumb: null,
+    id: "acknowledgements",
+    title: "Acknowledgements",
+    relativePath: "docs/acknowledgements.mdx",
+    compId: "articles-docs-acknowledgements",
+    crumb: "Credits",
   },
   {
-    id: "loop",
-    title: "<Loop>",
-    relativePath: "docs/loop.mdx",
-    compId: "articles-docs-loop",
-    crumb: "API",
+    id: "miscellaneous/snippets/accelerated-video",
+    title: "Change the speed of a video over time",
+    relativePath: "docs/miscellaneous/snippets/accelerated-video.mdx",
+    compId: "articles-docs-miscellaneous-snippets-accelerated-video",
+    crumb: "Snippets",
   },
   {
-    id: "getlottiemetadata",
-    title: "getLottieMetadata()",
-    relativePath: "docs/lottie/getlottiemetadata.mdx",
-    compId: "articles-docs-lottie-getlottiemetadata",
-    crumb: "@remotion/lottie",
+    id: "miscellaneous/snippets/fps-converter",
+    title: "FPS converter",
+    relativePath: "docs/miscellaneous/snippets/fps-converter.mdx",
+    compId: "articles-docs-miscellaneous-snippets-fps-converter",
+    crumb: "Snippets",
   },
   {
-    id: "lottie-index",
-    title: "@remotion/lottie",
-    relativePath: "docs/lottie/index.mdx",
-    compId: "articles-docs-lottie-index",
-    crumb: null,
+    id: "hls",
+    title: "HLS support (HTTP Live Streaming)",
+    relativePath: "docs/miscellaneous/snippets/hls.mdx",
+    compId: "articles-docs-miscellaneous-snippets-hls",
+    crumb: "Video",
   },
   {
-    id: "lottie-comp",
-    title: "<Lottie>",
-    relativePath: "docs/lottie/lottie-comp.mdx",
-    compId: "articles-docs-lottie-lottie-comp",
-    crumb: "@remotion/lottie",
+    id: "miscellaneous/snippets/offthread-video-while-rendering",
+    title: "<OffthreadVideo /> while rendering",
+    relativePath:
+      "docs/miscellaneous/snippets/offthread-video-while-rendering.mdx",
+    compId:
+      "articles-docs-miscellaneous-snippets-offthread-video-while-rendering",
+    crumb: "Snippets",
   },
   {
-    id: "lottie-lottiefiles",
-    title: "Finding Lottie files to use",
-    relativePath: "docs/lottie/lottie-lottiefiles.mdx",
-    compId: "articles-docs-lottie-lottie-lottiefiles",
-    crumb: "Resources",
+    id: "miscellaneous/snippets/player-in-iframe",
+    title: "Embedding a <Player> into an <iframe>",
+    relativePath: "docs/miscellaneous/snippets/player-in-iframe.mdx",
+    compId: "articles-docs-miscellaneous-snippets-player-in-iframe",
+    crumb: "Snippets",
   },
   {
-    id: "lottie-remote",
-    title: "Loading Lottie animations from a URL",
-    relativePath: "docs/lottie/lottie-remote.mdx",
-    compId: "articles-docs-lottie-lottie-remote",
-    crumb: "@remotion/lottie",
+    id: "miscellaneous/snippets/combine-compositions",
+    title: "How do I combine compositions?",
+    relativePath: "docs/miscellaneous/snippets/combine-compositions.mdx",
+    compId: "articles-docs-miscellaneous-snippets-combine-compositions",
+    crumb: "FAQ",
   },
   {
-    id: "lottie-staticfile",
-    title: "Loading Lottie animations from staticFile()",
-    relativePath: "docs/lottie/lottie-staticfile.mdx",
-    compId: "articles-docs-lottie-lottie-staticfile",
-    crumb: "@remotion/lottie",
+    id: "miscellaneous/snippets/align-duration",
+    title: "How do I make the composition the same duration as my video?",
+    relativePath: "docs/miscellaneous/snippets/align-duration.mdx",
+    compId: "articles-docs-miscellaneous-snippets-align-duration",
+    crumb: "FAQ",
   },
   {
-    id: "measure-spring",
-    title: "measureSpring()",
-    relativePath: "docs/measure-spring.mdx",
-    compId: "articles-docs-measure-spring",
-    crumb: "API",
+    id: "miscellaneous/snippets/use-delay-render",
+    title: "useDelayRender()",
+    relativePath: "docs/miscellaneous/snippets/use-delay-render.mdx",
+    compId: "articles-docs-miscellaneous-snippets-use-delay-render",
+    crumb: "Snippets",
   },
   {
-    id: "measuring",
-    title: "Measuring DOM nodes",
-    relativePath: "docs/measuring.mdx",
-    compId: "articles-docs-measuring",
-    crumb: "How To",
+    id: "miscellaneous/linux-single-process",
+    title: "Multiple cores on Linux",
+    relativePath: "docs/miscellaneous/linux-single-process.mdx",
+    compId: "articles-docs-miscellaneous-linux-single-process",
+    crumb: "Server-side rendering",
   },
   {
-    id: "media-playback-error",
-    title: "Could not play video/audio with src",
-    relativePath: "docs/media-playback-error.mdx",
-    compId: "articles-docs-media-playback-error",
-    crumb: "Troubleshooting",
+    id: "miscellaneous/vercel-functions",
+    title: "Can I render videos using Vercel Serverless functions?",
+    relativePath: "docs/miscellaneous/vercel-functions.mdx",
+    compId: "articles-docs-miscellaneous-vercel-functions",
+    crumb: "FAQ",
   },
   {
-    id: "media-utils/index",
-    title: "@remotion/media-utils",
-    relativePath: "docs/media-utils/index.mdx",
-    compId: "articles-docs-media-utils-index",
-    crumb: null,
+    id: "miscellaneous/chrome-headless-shell",
+    title: "Chrome Headless Shell",
+    relativePath: "docs/miscellaneous/chrome-headless-shell.mdx",
+    compId: "articles-docs-miscellaneous-chrome-headless-shell",
+    crumb: "FAQ",
   },
   {
-    id: "miscellaneous/absolute-paths",
-    title: "Files with absolute paths",
-    relativePath: "docs/miscellaneous/absolute-paths.mdx",
-    compId: "articles-docs-miscellaneous-absolute-paths",
+    id: "miscellaneous/render-in-browser",
+    title: "Can I render videos in the browser?",
+    relativePath: "docs/miscellaneous/render-in-browser.mdx",
+    compId: "articles-docs-miscellaneous-render-in-browser",
     crumb: "FAQ",
   },
   {
@@ -1686,6 +1549,13 @@ export const articles = [
     compId: "articles-docs-miscellaneous-automatic-duration",
     crumb: "FAQ",
   },
+  {
+    id: "typescript-aliases",
+    title: "TypeScript aliases",
+    relativePath: "docs/miscellaneous/ts-aliases.mdx",
+    compId: "articles-docs-miscellaneous-ts-aliases",
+    crumb: "How to",
+  },
   {
     id: "miscellaneous/changing-temp-dir",
     title: "Changing the temporary directory",
@@ -1694,10 +1564,17 @@ export const articles = [
     crumb: "Advanced configuration",
   },
   {
-    id: "miscellaneous/chrome-headless-shell",
-    title: "Chrome Headless Shell",
-    relativePath: "docs/miscellaneous/chrome-headless-shell.mdx",
-    compId: "articles-docs-miscellaneous-chrome-headless-shell",
+    id: "embed-studio",
+    title: "Can I embed the Remotion Studio?",
+    relativePath: "docs/miscellaneous/embed-remotion-studio.mdx",
+    compId: "articles-docs-miscellaneous-embed-remotion-studio",
+    crumb: "FAQ",
+  },
+  {
+    id: "miscellaneous/render-on-edge",
+    title: "Can I render videos on the edge?",
+    relativePath: "docs/miscellaneous/render-on-edge.mdx",
+    compId: "articles-docs-miscellaneous-render-on-edge",
     crumb: "FAQ",
   },
   {
@@ -1715,18 +1592,18 @@ export const articles = [
     crumb: "FAQ",
   },
   {
-    id: "embed-studio",
-    title: "Can I embed the Remotion Studio?",
-    relativePath: "docs/miscellaneous/embed-remotion-studio.mdx",
-    compId: "articles-docs-miscellaneous-embed-remotion-studio",
+    id: "miscellaneous/linux-dependencies",
+    title: "Linux Dependencies",
+    relativePath: "docs/miscellaneous/linux-dependencies.mdx",
+    compId: "articles-docs-miscellaneous-linux-dependencies",
     crumb: "FAQ",
   },
   {
-    id: "miscellaneous/linux-single-process",
-    title: "Multiple cores on Linux",
-    relativePath: "docs/miscellaneous/linux-single-process.mdx",
-    compId: "articles-docs-miscellaneous-linux-single-process",
-    crumb: "Server-side rendering",
+    id: "miscellaneous/video-formats",
+    title: "Which video formats does Remotion support?",
+    relativePath: "docs/miscellaneous/video-formats.mdx",
+    compId: "articles-docs-miscellaneous-video-formats",
+    crumb: "FAQ",
   },
   {
     id: "miscellaneous/live-streaming",
@@ -1736,503 +1613,494 @@ export const articles = [
     crumb: "FAQ",
   },
   {
-    id: "miscellaneous/render-in-browser",
-    title: "Can I render videos in the browser?",
-    relativePath: "docs/miscellaneous/render-in-browser.mdx",
-    compId: "articles-docs-miscellaneous-render-in-browser",
-    crumb: "FAQ",
-  },
-  {
-    id: "miscellaneous/render-on-edge",
-    title: "Can I render videos on the edge?",
-    relativePath: "docs/miscellaneous/render-on-edge.mdx",
-    compId: "articles-docs-miscellaneous-render-on-edge",
+    id: "miscellaneous/absolute-paths",
+    title: "Files with absolute paths",
+    relativePath: "docs/miscellaneous/absolute-paths.mdx",
+    compId: "articles-docs-miscellaneous-absolute-paths",
     crumb: "FAQ",
   },
   {
-    id: "miscellaneous/snippets/accelerated-video",
-    title: "Change the speed of a video over time",
-    relativePath: "docs/miscellaneous/snippets/accelerated-video.mdx",
-    compId: "articles-docs-miscellaneous-snippets-accelerated-video",
-    crumb: "Snippets",
-  },
-  {
-    id: "miscellaneous/snippets/align-duration",
-    title: "How do I make the composition the same duration as my video?",
-    relativePath: "docs/miscellaneous/snippets/align-duration.mdx",
-    compId: "articles-docs-miscellaneous-snippets-align-duration",
-    crumb: "FAQ",
+    id: "use-audio-data",
+    title: "useAudioData()",
+    relativePath: "docs/use-audio-data.mdx",
+    compId: "articles-docs-use-audio-data",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "miscellaneous/snippets/combine-compositions",
-    title: "How do I combine compositions?",
-    relativePath: "docs/miscellaneous/snippets/combine-compositions.mdx",
-    compId: "articles-docs-miscellaneous-snippets-combine-compositions",
-    crumb: "FAQ",
+    id: "calculate-metadata",
+    title: "calculateMetadata()",
+    relativePath: "docs/calculate-metadata.mdx",
+    compId: "articles-docs-calculate-metadata",
+    crumb: "API",
   },
   {
-    id: "miscellaneous/snippets/fps-converter",
-    title: "FPS converter",
-    relativePath: "docs/miscellaneous/snippets/fps-converter.mdx",
-    compId: "articles-docs-miscellaneous-snippets-fps-converter",
-    crumb: "Snippets",
+    id: "passing-props",
+    title: "Passing props to a composition",
+    relativePath: "docs/passing-props.mdx",
+    compId: "articles-docs-passing-props",
+    crumb: "How To",
   },
   {
-    id: "hls",
-    title: "HLS support (HTTP Live Streaming)",
-    relativePath: "docs/miscellaneous/snippets/hls.mdx",
-    compId: "articles-docs-miscellaneous-snippets-hls",
-    crumb: "Video",
+    id: "standalone",
+    title: "Useful functions outside of Remotion",
+    relativePath: "docs/standalone.mdx",
+    compId: "articles-docs-standalone",
+    crumb: null,
   },
   {
-    id: "miscellaneous/snippets/offthread-video-while-rendering",
-    title: "<OffthreadVideo /> while rendering",
-    relativePath:
-      "docs/miscellaneous/snippets/offthread-video-while-rendering.mdx",
-    compId:
-      "articles-docs-miscellaneous-snippets-offthread-video-while-rendering",
-    crumb: "Snippets",
+    id: "ssr-node",
+    title: "Rendering using SSR APIs",
+    relativePath: "docs/ssr-node.mdx",
+    compId: "articles-docs-ssr-node",
+    crumb: "Server-side rendering",
   },
   {
-    id: "miscellaneous/snippets/player-in-iframe",
-    title: "Embedding a <Player> into an <iframe>",
-    relativePath: "docs/miscellaneous/snippets/player-in-iframe.mdx",
-    compId: "articles-docs-miscellaneous-snippets-player-in-iframe",
-    crumb: "Snippets",
+    id: "tailwind",
+    title: "@remotion/tailwind",
+    relativePath: "docs/tailwind/overview.mdx",
+    compId: "articles-docs-tailwind-overview",
+    crumb: null,
   },
   {
-    id: "miscellaneous/snippets/use-delay-render",
-    title: "useDelayRender()",
-    relativePath: "docs/miscellaneous/snippets/use-delay-render.mdx",
-    compId: "articles-docs-miscellaneous-snippets-use-delay-render",
-    crumb: "Snippets",
+    id: "tailwind/enable-tailwind",
+    title: "enableTailwind()",
+    relativePath: "docs/tailwind/enable-tailwind.mdx",
+    compId: "articles-docs-tailwind-enable-tailwind",
+    crumb: "@remotion/tailwind",
   },
   {
-    id: "typescript-aliases",
-    title: "TypeScript aliases",
-    relativePath: "docs/miscellaneous/ts-aliases.mdx",
-    compId: "articles-docs-miscellaneous-ts-aliases",
-    crumb: "How to",
+    id: "upgrading",
+    title: "Upgrading Remotion",
+    relativePath: "docs/upgrading.mdx",
+    compId: "articles-docs-upgrading",
+    crumb: "upgrade remotion",
   },
   {
-    id: "miscellaneous/vercel-functions",
-    title: "Can I render videos using Vercel Serverless functions?",
-    relativePath: "docs/miscellaneous/vercel-functions.mdx",
-    compId: "articles-docs-miscellaneous-vercel-functions",
-    crumb: "FAQ",
+    id: "getregions",
+    title: "getRegions()",
+    relativePath: "docs/cloudrun/getregions.mdx",
+    compId: "articles-docs-cloudrun-getregions",
+    crumb: "Cloud Run API",
   },
   {
-    id: "miscellaneous/video-formats",
-    title: "Which video formats does Remotion support?",
-    relativePath: "docs/miscellaneous/video-formats.mdx",
-    compId: "articles-docs-miscellaneous-video-formats",
-    crumb: "FAQ",
+    id: "cloudrun/multiple-buckets",
+    title: "Multiple buckets in Remotion Cloud Run",
+    relativePath: "docs/cloudrun/multiple-buckets.mdx",
+    compId: "articles-docs-cloudrun-multiple-buckets",
+    crumb: "@remotion/cloudrun",
   },
   {
-    id: "motion-blur/camera-motion-blur",
-    title: "<CameraMotionBlur>",
-    relativePath: "docs/motion-blur/camera-motion-blur.mdx",
-    compId: "articles-docs-motion-blur-camera-motion-blur",
-    crumb: "Realistic camera effect",
+    id: "instancecount",
+    title: "Instance Count",
+    relativePath: "docs/cloudrun/instancecount.mdx",
+    compId: "articles-docs-cloudrun-instancecount",
+    crumb: "Cloud Run",
   },
   {
-    id: "motion-blur/index",
-    title: "@remotion/motion-blur",
-    relativePath: "docs/motion-blur/index.mdx",
-    compId: "articles-docs-motion-blur-index",
+    id: "cli",
+    title: "@remotion/cloudrun - CLI",
+    relativePath: "docs/cloudrun/cli.mdx",
+    compId: "articles-docs-cloudrun-cli",
     crumb: null,
   },
   {
-    id: "motion-blur/motion-blur",
-    title: "<MotionBlur>",
-    relativePath: "docs/motion-blur/motion-blur.mdx",
-    compId: "articles-docs-motion-blur-motion-blur",
-    crumb: null,
+    id: "deployservice",
+    title: "deployService()",
+    relativePath: "docs/cloudrun/deployservice.mdx",
+    compId: "articles-docs-cloudrun-deployservice",
+    crumb: "Cloud Run API",
   },
   {
-    id: "motion-blur/trail",
-    title: "<Trail>",
-    relativePath: "docs/motion-blur/trail.mdx",
-    compId: "articles-docs-motion-blur-trail",
-    crumb: "Motion blur",
+    id: "getsites",
+    title: "getSites()",
+    relativePath: "docs/cloudrun/getsites.mdx",
+    compId: "articles-docs-cloudrun-getsites",
+    crumb: "Cloud Run API",
   },
   {
-    id: "noise/index",
-    title: "@remotion/noise",
-    relativePath: "docs/noise/index.mdx",
-    compId: "articles-docs-noise-index",
-    crumb: null,
+    id: "deleteservice",
+    title: "deleteService()",
+    relativePath: "docs/cloudrun/deleteservice.mdx",
+    compId: "articles-docs-cloudrun-deleteservice",
+    crumb: "Cloud Run API",
   },
   {
-    id: "noise/noise-2d",
-    title: "noise2D()",
-    relativePath: "docs/noise/noise-2d.mdx",
-    compId: "articles-docs-noise-noise-2d",
-    crumb: "Make some",
+    id: "generate-env",
+    title: "Generate .env File",
+    relativePath: "docs/cloudrun/generate-env.mdx",
+    compId: "articles-docs-cloudrun-generate-env",
+    crumb: "Cloud Run",
   },
   {
-    id: "noise/noise-3d",
-    title: "noise3D()",
-    relativePath: "docs/noise/noise-3d.mdx",
-    compId: "articles-docs-noise-noise-3d",
-    crumb: "Make some",
+    id: "cloudrun/permissions",
+    title: "Permissions",
+    relativePath: "docs/cloudrun/permissions.mdx",
+    compId: "articles-docs-cloudrun-permissions",
+    crumb: "Cloud Run",
   },
   {
-    id: "noise/noise-4d",
-    title: "noise4D()",
-    relativePath: "docs/noise/noise-4d.mdx",
-    compId: "articles-docs-noise-noise-4d",
-    crumb: "Make some",
+    id: "renderstilloncloudrun",
+    title: "renderStillOnCloudrun()",
+    relativePath: "docs/cloudrun/renderstilloncloudrun.mdx",
+    compId: "articles-docs-cloudrun-renderstilloncloudrun",
+    crumb: "Cloud Run API",
   },
   {
-    id: "noise-visualization",
-    title: "Noise visualization",
-    relativePath: "docs/noise-visualization.mdx",
-    compId: "articles-docs-noise-visualization",
-    crumb: "Techniques",
+    id: "getorcreatebucket",
+    title: "getOrCreateBucket()",
+    relativePath: "docs/cloudrun/getorcreatebucket.mdx",
+    compId: "articles-docs-cloudrun-getorcreatebucket",
+    crumb: "Cloud Run API",
   },
   {
-    id: "non-seekable-media",
-    title: "Non-seekable media",
-    relativePath: "docs/non-seekable-media.mdx",
-    compId: "articles-docs-non-seekable-media",
-    crumb: "Troubleshooting",
+    id: "testpermissions",
+    title: "testPermissions()",
+    relativePath: "docs/cloudrun/testpermissions.mdx",
+    compId: "articles-docs-cloudrun-testpermissions",
+    crumb: "Cloud Run API",
   },
   {
-    id: '"null"',
-    title: "<Experimental.Null>",
-    relativePath: "docs/null.mdx",
-    compId: "articles-docs-null",
-    crumb: "Experimental API",
+    id: "cloudrun/light-client",
+    title: "Light client",
+    relativePath: "docs/cloudrun/light-client.mdx",
+    compId: "articles-docs-cloudrun-light-client",
+    crumb: "Cloud Run",
   },
   {
-    id: "offthreadvideo",
-    title: "<OffthreadVideo>",
-    relativePath: "docs/offthreadvideo.mdx",
-    compId: "articles-docs-offthreadvideo",
-    crumb: "API",
+    id: "services",
+    title: "npx remotion cloudrun services",
+    relativePath: "docs/cloudrun/cli/services.mdx",
+    compId: "articles-docs-cloudrun-cli-services",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "gl-options",
-    title: "--gl flag",
-    relativePath: "docs/open-gl.mdx",
-    compId: "articles-docs-open-gl",
-    crumb: "Flags",
+    id: "permissions",
+    title: "npx remotion cloudrun permissions",
+    relativePath: "docs/cloudrun/cli/permissions.mdx",
+    compId: "articles-docs-cloudrun-cli-permissions",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "overlay",
-    title: "Creating overlays",
-    relativePath: "docs/overlay.mdx",
-    compId: "articles-docs-overlay",
-    crumb: null,
+    id: "sites",
+    title: "npx remotion cloudrun sites",
+    relativePath: "docs/cloudrun/cli/sites.mdx",
+    compId: "articles-docs-cloudrun-cli-sites",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "webpack",
-    title: "Custom Webpack config",
-    relativePath: "docs/overwriting-webpack-config.mdx",
-    compId: "articles-docs-overwriting-webpack-config",
-    crumb: "How To",
+    id: "render",
+    title: "npx remotion cloudrun render",
+    relativePath: "docs/cloudrun/cli/render.mdx",
+    compId: "articles-docs-cloudrun-cli-render",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "parameterized-rendering",
-    title: "Parameterized videos",
-    relativePath: "docs/parameterized-rendering.mdx",
-    compId: "articles-docs-parameterized-rendering",
-    crumb: "How To",
+    id: "regions",
+    title: "npx remotion cloudrun regions",
+    relativePath: "docs/cloudrun/cli/regions.mdx",
+    compId: "articles-docs-cloudrun-cli-regions",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "passing-props",
-    title: "Passing props to a composition",
-    relativePath: "docs/passing-props.mdx",
-    compId: "articles-docs-passing-props",
-    crumb: "How To",
+    id: "still",
+    title: "npx remotion cloudrun still",
+    relativePath: "docs/cloudrun/cli/still.mdx",
+    compId: "articles-docs-cloudrun-cli-still",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "paths/evolve-path",
-    title: "evolvePath()",
-    relativePath: "docs/paths/evolve-path.mdx",
-    compId: "articles-docs-paths-evolve-path",
-    crumb: "@remotion/paths",
+    id: "upgrading",
+    title: "Upgrading Cloud Run",
+    relativePath: "docs/cloudrun/upgrading.mdx",
+    compId: "articles-docs-cloudrun-upgrading",
+    crumb: null,
   },
   {
-    id: "paths/extend-viewbox",
-    title: "extendViewBox()",
-    relativePath: "docs/paths/extend-viewbox.mdx",
-    compId: "articles-docs-paths-extend-viewbox",
-    crumb: "@remotion/paths",
+    id: "region-selection",
+    title: "Region selection",
+    relativePath: "docs/cloudrun/region-selection.mdx",
+    compId: "articles-docs-cloudrun-region-selection",
+    crumb: "Cloud Run",
   },
   {
-    id: "paths/get-bounding-box",
-    title: "getBoundingBox()",
-    relativePath: "docs/paths/get-bounding-box.mdx",
-    compId: "articles-docs-paths-get-bounding-box",
-    crumb: "@remotion/paths",
+    id: "getservices",
+    title: "getServices()",
+    relativePath: "docs/cloudrun/getservices.mdx",
+    compId: "articles-docs-cloudrun-getservices",
+    crumb: "Cloud Run API",
   },
   {
-    id: "paths/get-instruction-index-at-length",
-    title: "getInstructionIndexAtLength()",
-    relativePath: "docs/paths/get-instruction-index-at-length.mdx",
-    compId: "articles-docs-paths-get-instruction-index-at-length",
-    crumb: "@remotion/paths",
+    id: "getserviceinfo",
+    title: "getServiceInfo()",
+    relativePath: "docs/cloudrun/getServiceinfo.mdx",
+    compId: "articles-docs-cloudrun-getServiceinfo",
+    crumb: "Cloud Run API",
   },
   {
-    id: "paths/get-length",
-    title: "getLength()",
-    relativePath: "docs/paths/get-length.mdx",
-    compId: "articles-docs-paths-get-length",
-    crumb: "@remotion/paths",
+    id: "checklist",
+    title: "Production Checklist",
+    relativePath: "docs/cloudrun/checklist.mdx",
+    compId: "articles-docs-cloudrun-checklist",
+    crumb: "Cloud Run",
   },
   {
-    id: "paths/get-parts",
-    title: "getParts()",
-    relativePath: "docs/paths/get-parts.mdx",
-    compId: "articles-docs-paths-get-parts",
-    crumb: "@remotion/paths",
+    id: "cloudrun/api",
+    title: "@remotion/cloudrun",
+    relativePath: "docs/cloudrun/api.mdx",
+    compId: "articles-docs-cloudrun-api",
+    crumb: "Render videos without servers on GCP",
   },
   {
-    id: "paths/get-point-at-length",
-    title: "getPointAtLength()",
-    relativePath: "docs/paths/get-point-at-length.mdx",
-    compId: "articles-docs-paths-get-point-at-length",
-    crumb: "@remotion/paths",
+    id: "setup",
+    title: "Setup",
+    relativePath: "docs/cloudrun/setup.mdx",
+    compId: "articles-docs-cloudrun-setup",
+    crumb: "Cloud Run",
   },
   {
-    id: "paths/get-subpaths",
-    title: "getSubpaths()",
-    relativePath: "docs/paths/get-subpaths.mdx",
-    compId: "articles-docs-paths-get-subpaths",
-    crumb: "@remotion/paths",
+    id: "rendermediaoncloudrun",
+    title: "renderMediaOnCloudrun()",
+    relativePath: "docs/cloudrun/rendermediaoncloudrun.mdx",
+    compId: "articles-docs-cloudrun-rendermediaoncloudrun",
+    crumb: "Cloud Run API",
   },
   {
-    id: "paths/get-tangent-at-length",
-    title: "getTangentAtLength()",
-    relativePath: "docs/paths/get-tangent-at-length.mdx",
-    compId: "articles-docs-paths-get-tangent-at-length",
-    crumb: "@remotion/paths",
+    id: "speculateservicename",
+    title: "speculateServiceName()",
+    relativePath: "docs/cloudrun/speculateservicename.mdx",
+    compId: "articles-docs-cloudrun-speculateservicename",
+    crumb: "Cloud Run API",
   },
   {
-    id: "paths/index",
-    title: "@remotion/paths",
-    relativePath: "docs/paths/index.mdx",
-    compId: "articles-docs-paths-index",
-    crumb: "SVG",
+    id: "cloudrun/limits",
+    title: "Cloud Run Limits",
+    relativePath: "docs/cloudrun/limits.mdx",
+    compId: "articles-docs-cloudrun-limits",
+    crumb: "Cloud Run",
   },
   {
-    id: "paths/interpolate-path",
-    title: "interpolatePath()",
-    relativePath: "docs/paths/interpolate-path.mdx",
-    compId: "articles-docs-paths-interpolate-path",
-    crumb: "@remotion/paths",
+    id: "deploysite",
+    title: "deploySite()",
+    relativePath: "docs/cloudrun/deploysite.mdx",
+    compId: "articles-docs-cloudrun-deploysite",
+    crumb: "Cloud Run API",
   },
   {
-    id: "paths/normalize-path",
-    title: "normalizePath()",
-    relativePath: "docs/paths/normalize-path.mdx",
-    compId: "articles-docs-paths-normalize-path",
-    crumb: "@remotion/paths",
+    id: "uninstall",
+    title: "Uninstall Cloud Run",
+    relativePath: "docs/cloudrun/uninstall.mdx",
+    compId: "articles-docs-cloudrun-uninstall",
+    crumb: null,
   },
   {
-    id: "paths/parse-path",
-    title: "parsePath()",
-    relativePath: "docs/paths/parse-path.mdx",
-    compId: "articles-docs-paths-parse-path",
-    crumb: "@remotion/paths",
+    id: "deletesite",
+    title: "deleteSite()",
+    relativePath: "docs/cloudrun/deletesite.mdx",
+    compId: "articles-docs-cloudrun-deletesite",
+    crumb: "Cloud Run API",
+  },
+  {
+    id: "reusability",
+    title: "Making components reusable",
+    relativePath: "docs/sequences.mdx",
+    compId: "articles-docs-sequences",
+    crumb: "The power of React",
   },
   {
-    id: "paths/reduce-instructions",
-    title: "reduceInstructions()",
-    relativePath: "docs/paths/reduce-instructions.mdx",
-    compId: "articles-docs-paths-reduce-instructions",
-    crumb: "@remotion/paths",
+    id: "three-canvas",
+    title: "<ThreeCanvas>",
+    relativePath: "docs/three-canvas.mdx",
+    compId: "articles-docs-three-canvas",
+    crumb: "@remotion/three",
   },
   {
-    id: "paths/reset-path",
-    title: "resetPath()",
-    relativePath: "docs/paths/reset-path.mdx",
-    compId: "articles-docs-paths-reset-path",
-    crumb: "@remotion/paths",
+    id: "version-mismatch",
+    title: "Version mismatch",
+    relativePath: "docs/version-mismatch.mdx",
+    compId: "articles-docs-version-mismatch",
+    crumb: "How to fix a",
   },
   {
-    id: "paths/reverse-path",
-    title: "reversePath()",
-    relativePath: "docs/paths/reverse-path.mdx",
-    compId: "articles-docs-paths-reverse-path",
-    crumb: "@remotion/paths",
+    id: "iframe",
+    title: "<IFrame>",
+    relativePath: "docs/iframe.mdx",
+    compId: "articles-docs-iframe",
+    crumb: "API",
   },
   {
-    id: "paths/scale-path",
-    title: "scalePath()",
-    relativePath: "docs/paths/scale-path.mdx",
-    compId: "articles-docs-paths-scale-path",
-    crumb: "@remotion/paths",
+    id: "cloudrun",
+    title: "@remotion/cloudrun",
+    relativePath: "docs/cloudrun.mdx",
+    compId: "articles-docs-cloudrun",
+    crumb: null,
   },
   {
-    id: "paths/serialize-instructions",
-    title: "serializeInstructions()",
-    relativePath: "docs/paths/serialize-instructions.mdx",
-    compId: "articles-docs-paths-serialize-instructions",
-    crumb: "@remotion/paths",
+    id: "transitioning",
+    title: "Transitions",
+    relativePath: "docs/transitions.mdx",
+    compId: "articles-docs-transitions",
+    crumb: "Techniques",
   },
   {
-    id: "paths/translate-path",
-    title: "translatePath()",
-    relativePath: "docs/paths/translate-path.mdx",
-    compId: "articles-docs-paths-translate-path",
-    crumb: "@remotion/paths",
+    id: "gl-options",
+    title: "--gl flag",
+    relativePath: "docs/open-gl.mdx",
+    compId: "articles-docs-open-gl",
+    crumb: "Flags",
   },
   {
-    id: "paths/warp-path",
-    title: "warpPath()",
-    relativePath: "docs/paths/warp-path.mdx",
-    compId: "articles-docs-paths-warp-path",
-    crumb: "@remotion/paths",
+    id: "interpolate-colors",
+    title: "interpolateColors()",
+    relativePath: "docs/interpolate-colors.mdx",
+    compId: "articles-docs-interpolate-colors",
+    crumb: "API",
   },
   {
-    id: "performance",
-    title: "Performance Tips",
-    relativePath: "docs/performance.mdx",
-    compId: "articles-docs-performance",
-    crumb: "Need for Speed",
+    id: "using-randomness",
+    title: "Using randomness",
+    relativePath: "docs/using-randomness.mdx",
+    compId: "articles-docs-using-randomness",
+    crumb: "Roll the dice",
   },
   {
-    id: "player/api",
-    title: "<Player>",
-    relativePath: "docs/player/api.mdx",
-    compId: "articles-docs-player-api",
-    crumb: "@remotion/player",
+    id: "5-0-migration",
+    title: "v5.0 Migration",
+    relativePath: "docs/5-0-migration.mdx",
+    compId: "articles-docs-5-0-migration",
+    crumb: "Version Upgrade",
   },
   {
-    id: "autoplay",
-    title: "Combatting autoplay issues",
-    relativePath: "docs/player/autoplay.mdx",
-    compId: "articles-docs-player-autoplay",
-    crumb: "@remotion/player",
+    id: "get-input-props",
+    title: "getInputProps()",
+    relativePath: "docs/get-input-props.mdx",
+    compId: "articles-docs-get-input-props",
+    crumb: "API",
   },
   {
-    id: "best-practices",
-    title: "Player - Best practices",
-    relativePath: "docs/player/best-practices.mdx",
-    compId: "articles-docs-player-best-practices",
-    crumb: "@remotion/player",
+    id: "measuring",
+    title: "Measuring DOM nodes",
+    relativePath: "docs/measuring.mdx",
+    compId: "articles-docs-measuring",
+    crumb: "How To",
   },
   {
-    id: "buffer-state",
-    title: "The Player buffer state",
-    relativePath: "docs/player/buffer-state.mdx",
-    compId: "articles-docs-player-buffer-state",
-    crumb: "Best practices",
+    id: "2-0-migration",
+    title: "v2.0 Migration",
+    relativePath: "docs/2-0-migration.mdx",
+    compId: "articles-docs-2-0-migration",
+    crumb: "Version Upgrade",
   },
   {
-    id: "current-time",
-    title: "Displaying the current time",
-    relativePath: "docs/player/current-time.mdx",
-    compId: "articles-docs-player-current-time",
-    crumb: "@remotion/player",
+    id: "getting-started",
+    title: "Creating a new project",
+    relativePath: "docs/getting-started.mdx",
+    compId: "articles-docs-getting-started",
+    crumb: "Let's begin!",
   },
   {
-    id: "player/index",
-    title: "@remotion/player",
-    relativePath: "docs/player/index.mdx",
-    compId: "articles-docs-player-index",
+    id: "google-fonts",
+    title: "@remotion/google-fonts",
+    relativePath: "docs/google-fonts/index.mdx",
+    compId: "articles-docs-google-fonts-index",
     crumb: null,
   },
   {
-    id: "installation",
-    title: "Installation",
-    relativePath: "docs/player/installation.mdx",
-    compId: "articles-docs-player-installation",
-    crumb: "@remotion/player",
+    id: "google-fonts/get-available-fonts",
+    title: "getAvailableFonts()",
+    relativePath: "docs/google-fonts/get-available-fonts.mdx",
+    compId: "articles-docs-google-fonts-get-available-fonts",
+    crumb: "@remotion/google-fonts",
   },
   {
-    id: "examples",
-    title: "Examples for @remotion/player",
-    relativePath: "docs/player/player-examples.mdx",
-    compId: "articles-docs-player-player-examples",
-    crumb: "@remotion/player",
+    id: "google-fonts/get-info",
+    title: "getInfo()",
+    relativePath: "docs/google-fonts/get-info.mdx",
+    compId: "articles-docs-google-fonts-get-info",
+    crumb: "@remotion/google-fonts",
   },
   {
-    id: "integration",
-    title: "Code sharing",
-    relativePath: "docs/player/player-integration.mdx",
-    compId: "articles-docs-player-player-integration",
-    crumb: "@remotion/player",
+    id: "google-fonts/load-font",
+    title: "loadFont()",
+    relativePath: "docs/google-fonts/load-font.mdx",
+    compId: "articles-docs-google-fonts-load-font",
+    crumb: "@remotion/google-fonts",
   },
   {
-    id: "player/preloading",
-    title: "Preloading assets",
-    relativePath: "docs/player/preloading.mdx",
-    compId: "articles-docs-player-preloading",
-    crumb: "@remotion/player",
+    id: "media-utils/index",
+    title: "@remotion/media-utils",
+    relativePath: "docs/media-utils/index.mdx",
+    compId: "articles-docs-media-utils-index",
+    crumb: null,
   },
   {
-    id: "player/premounting",
-    title: "Premounting",
-    relativePath: "docs/player/premounting.mdx",
-    compId: "articles-docs-player-premounting",
-    crumb: "@remotion/player",
+    id: "noise/noise-4d",
+    title: "noise4D()",
+    relativePath: "docs/noise/noise-4d.mdx",
+    compId: "articles-docs-noise-noise-4d",
+    crumb: "Make some",
   },
   {
-    id: "scaling",
-    title: "Sizing",
-    relativePath: "docs/player/scaling.mdx",
-    compId: "articles-docs-player-scaling",
-    crumb: "@remotion/player",
+    id: "noise/noise-3d",
+    title: "noise3D()",
+    relativePath: "docs/noise/noise-3d.mdx",
+    compId: "articles-docs-noise-noise-3d",
+    crumb: "Make some",
   },
   {
-    id: "thumbnail",
-    title: "<Thumbnail>",
-    relativePath: "docs/player/thumbnail.mdx",
-    compId: "articles-docs-player-thumbnail",
-    crumb: "@remotion/player",
+    id: "noise/index",
+    title: "@remotion/noise",
+    relativePath: "docs/noise/index.mdx",
+    compId: "articles-docs-noise-index",
+    crumb: null,
   },
   {
-    id: "prefetch",
-    title: "prefetch()",
-    relativePath: "docs/prefetch.mdx",
-    compId: "articles-docs-prefetch",
-    crumb: "API",
+    id: "noise/noise-2d",
+    title: "noise2D()",
+    relativePath: "docs/noise/noise-2d.mdx",
+    compId: "articles-docs-noise-noise-2d",
+    crumb: "Make some",
   },
   {
-    id: "preload-audio",
-    title: "preloadAudio()",
-    relativePath: "docs/preload/preload-audio.mdx",
-    compId: "articles-docs-preload-preload-audio",
-    crumb: "@remotion/preload",
+    id: "security",
+    title: "Security Best Practices",
+    relativePath: "docs/security.mdx",
+    compId: "articles-docs-security",
+    crumb: "FAQ",
   },
   {
-    id: "preload-font",
-    title: "preloadFont()",
-    relativePath: "docs/preload/preload-font.mdx",
-    compId: "articles-docs-preload-preload-font",
-    crumb: "@remotion/preload",
+    id: "delay-render",
+    title: "delayRender() and continueRender()",
+    relativePath: "docs/delay-render.mdx",
+    compId: "articles-docs-delay-render",
+    crumb: "How to",
   },
   {
-    id: "preload-image",
-    title: "preloadImage()",
-    relativePath: "docs/preload/preload-image.mdx",
-    compId: "articles-docs-preload-preload-image",
-    crumb: "@remotion/preload",
+    id: "loop",
+    title: "<Loop>",
+    relativePath: "docs/loop.mdx",
+    compId: "articles-docs-loop",
+    crumb: "API",
   },
-  {
-    id: "preload-video",
-    title: "preloadVideo()",
-    relativePath: "docs/preload/preload-video.mdx",
-    compId: "articles-docs-preload-preload-video",
-    crumb: "@remotion/player",
+  {
+    id: "render",
+    title: "Render your video",
+    relativePath: "docs/render.mdx",
+    compId: "articles-docs-render",
+    crumb: "How To",
   },
   {
-    id: "preload",
-    title: "@remotion/preload",
-    relativePath: "docs/preload/preload.mdx",
-    compId: "articles-docs-preload-preload",
-    crumb: null,
+    id: "ssr-legacy",
+    title: "Server-Side Rendering (v1 and v2)",
+    relativePath: "docs/ssr-legacy.mdx",
+    compId: "articles-docs-ssr-legacy",
+    crumb: "Legacy docs",
   },
   {
-    id: "prereleases",
-    title: "Testing prereleases",
-    relativePath: "docs/prereleases.mdx",
-    compId: "articles-docs-prereleases",
-    crumb: "Only the brave",
+    id: "continue-render",
+    title: "continueRender()",
+    relativePath: "docs/continue-render.mdx",
+    compId: "articles-docs-continue-render",
+    crumb: "API",
   },
   {
     id: "presigned-urls",
@@ -2242,975 +2110,1010 @@ export const articles = [
     crumb: "Building video apps",
   },
   {
-    id: "preview",
-    title: "Preview your video",
-    relativePath: "docs/preview.mdx",
-    compId: "articles-docs-preview",
-    crumb: "Timeline basics",
+    id: "tailwind-legacy",
+    title: "TailwindCSS v2 (Legacy)",
+    relativePath: "docs/tailwind-2.mdx",
+    compId: "articles-docs-tailwind-2",
+    crumb: "Legacy docs",
   },
   {
-    id: "quality",
-    title: "Quality Guide",
-    relativePath: "docs/quality.mdx",
-    compId: "articles-docs-quality",
-    crumb: "Make it look good",
+    id: "performance",
+    title: "Performance Tips",
+    relativePath: "docs/performance.mdx",
+    compId: "articles-docs-performance",
+    crumb: "Need for Speed",
   },
   {
-    id: "random",
-    title: "random()",
-    relativePath: "docs/random.mdx",
-    compId: "articles-docs-random",
-    crumb: "API",
+    id: "getregions",
+    title: "getRegions()",
+    relativePath: "docs/lambda/getregions.mdx",
+    compId: "articles-docs-lambda-getregions",
+    crumb: "Lambda API",
   },
   {
-    id: "react-18",
-    title: "Upgrade to React 18",
-    relativePath: "docs/react-18.mdx",
-    compId: "articles-docs-react-18",
-    crumb: "The new and shiny",
+    id: "lambda/feb-2023-incident",
+    title: "Upgrade your Lambda functions to prevent breakage",
+    relativePath: "docs/lambda/feb-2023-incident.mdx",
+    compId: "articles-docs-lambda-feb-2023-incident",
+    crumb: "DevOps advisory",
   },
   {
-    id: "react-native",
-    title: "React Native",
-    relativePath: "docs/react-native.mdx",
-    compId: "articles-docs-react-native",
+    id: "lambda/multiple-buckets",
+    title: "Multiple buckets in Remotion Lambda",
+    relativePath: "docs/lambda/multiple-buckets.mdx",
+    compId: "articles-docs-lambda-multiple-buckets",
+    crumb: "@remotion/lambda",
+  },
+  {
+    id: "cli",
+    title: "@remotion/lambda - CLI",
+    relativePath: "docs/lambda/cli.mdx",
+    compId: "articles-docs-lambda-cli",
     crumb: null,
   },
   {
-    id: "recorder/before-you-buy",
-    title: "Before you buy",
-    relativePath: "docs/recorder/before-you-buy.mdx",
-    compId: "articles-docs-recorder-before-you-buy",
-    crumb: "Recorder",
+    id: "getuserpolicy",
+    title: "getUserPolicy()",
+    relativePath: "docs/lambda/getuserpolicy.mdx",
+    compId: "articles-docs-lambda-getuserpolicy",
+    crumb: "Lambda API",
   },
   {
-    id: "recorder/buy",
-    title: "Buy",
-    relativePath: "docs/recorder/buy.mdx",
-    compId: "articles-docs-recorder-buy",
-    crumb: "Recorder",
+    id: "concurrency",
+    title: "Concurrency",
+    relativePath: "docs/lambda/concurrency.mdx",
+    compId: "articles-docs-lambda-concurrency",
+    crumb: "Lambda",
   },
   {
-    id: "recorder/captions",
-    title: "Generate captions",
-    relativePath: "docs/recorder/captions.mdx",
-    compId: "articles-docs-recorder-captions",
-    crumb: "Recorder",
+    id: "autodelete",
+    title: "Auto-delete renders",
+    relativePath: "docs/lambda/autodelete.mdx",
+    compId: "articles-docs-lambda-autodelete",
+    crumb: "Lambda API",
   },
   {
-    id: "recorder/create",
-    title: "Create a new video",
-    relativePath: "docs/recorder/create.mdx",
-    compId: "articles-docs-recorder-create",
-    crumb: "Recorder",
+    id: "optimizing-speed",
+    title: "Optimizing for speed",
+    relativePath: "docs/lambda/speed.mdx",
+    compId: "articles-docs-lambda-speed",
+    crumb: "Lambda",
   },
   {
-    id: "recorder/editing/b-roll",
-    title: "B-Roll",
-    relativePath: "docs/recorder/editing/b-roll.mdx",
-    compId: "articles-docs-recorder-editing-b-roll",
-    crumb: "Recorder",
+    id: "rendervideoonlambda",
+    title: "renderVideoOnLambda()",
+    relativePath: "docs/lambda/rendervideoonlambda.mdx",
+    compId: "articles-docs-lambda-rendervideoonlambda",
+    crumb: "Lambda API",
   },
   {
-    id: "recorder/editing/captions",
-    title: "Edit captions",
-    relativePath: "docs/recorder/editing/captions.mdx",
-    compId: "articles-docs-recorder-editing-captions",
-    crumb: "Recorder",
+    id: "getsites",
+    title: "getSites()",
+    relativePath: "docs/lambda/getsites.mdx",
+    compId: "articles-docs-lambda-getsites",
+    crumb: "Lambda API",
   },
   {
-    id: "recorder/editing/chapters",
-    title: "Chapters",
-    relativePath: "docs/recorder/editing/chapters.mdx",
-    compId: "articles-docs-recorder-editing-chapters",
-    crumb: "Recorder",
+    id: "speculatefunctionname",
+    title: "speculateFunctionName()",
+    relativePath: "docs/lambda/speculateFunctionName.mdx",
+    compId: "articles-docs-lambda-speculateFunctionName",
+    crumb: "Lambda API",
   },
   {
-    id: "recorder/editing/cutting-clips",
-    title: "Cutting clips",
-    relativePath: "docs/recorder/editing/cutting-clips.mdx",
-    compId: "articles-docs-recorder-editing-cutting-clips",
-    crumb: "Recorder",
+    id: "lambda/permissions",
+    title: "Permissions",
+    relativePath: "docs/lambda/permissions.mdx",
+    compId: "articles-docs-lambda-permissions",
+    crumb: "Lambda",
   },
   {
-    id: "recorder/editing/editing",
-    title: "Start editing",
-    relativePath: "docs/recorder/editing/editing.mdx",
-    compId: "articles-docs-recorder-editing-editing",
-    crumb: "Recorder",
+    id: "deployfunction",
+    title: "deployFunction()",
+    relativePath: "docs/lambda/deployfunction.mdx",
+    compId: "articles-docs-lambda-deployfunction",
+    crumb: "Lambda API",
   },
   {
-    id: "recorder/editing/endcard",
-    title: "Endcard",
-    relativePath: "docs/recorder/editing/endcard.mdx",
-    compId: "articles-docs-recorder-editing-endcard",
-    crumb: "Recorder",
+    id: "feb-2022-outage",
+    title: "February 2022 Outage",
+    relativePath: "docs/lambda/feb-2022-outage.mdx",
+    compId: "articles-docs-lambda-feb-2022-outage",
+    crumb: null,
   },
   {
-    id: "recorder/editing/layout",
-    title: "Layout",
-    relativePath: "docs/recorder/editing/layout.mdx",
-    compId: "articles-docs-recorder-editing-layout",
-    crumb: "Recorder",
+    id: "getrenderprogress",
+    title: "getRenderProgress()",
+    relativePath: "docs/lambda/getrenderprogress.mdx",
+    compId: "articles-docs-lambda-getrenderprogress",
+    crumb: "Lambda API",
   },
   {
-    id: "recorder/editing/music",
-    title: "Adding music",
-    relativePath: "docs/recorder/editing/music.mdx",
-    compId: "articles-docs-recorder-editing-music",
-    crumb: "Recorder",
+    id: "authentication",
+    title: "Authentication",
+    relativePath: "docs/lambda/authentication.mdx",
+    compId: "articles-docs-lambda-authentication",
+    crumb: "Lambda",
   },
   {
-    id: "recorder/editing/scenes",
-    title: "Scenes",
-    relativePath: "docs/recorder/editing/scenes.mdx",
-    compId: "articles-docs-recorder-editing-scenes",
-    crumb: "Recorder",
+    id: "lambda/python",
+    title: "Triggering renders from Python",
+    relativePath: "docs/lambda/python.mdx",
+    compId: "articles-docs-lambda-python",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "recorder/editing/silence-removal",
-    title: "Silence removal",
-    relativePath: "docs/recorder/editing/silence-removal.mdx",
-    compId: "articles-docs-recorder-editing-silence-removal",
-    crumb: "Recorder",
+    id: "lambda/webhooks",
+    title: "Webhooks",
+    relativePath: "docs/lambda/webhooks.mdx",
+    compId: "articles-docs-lambda-webhooks",
+    crumb: "Lambda",
   },
   {
-    id: "recorder/editing/transitions",
-    title: "Transitions",
-    relativePath: "docs/recorder/editing/transitions.mdx",
-    compId: "articles-docs-recorder-editing-transitions",
-    crumb: "Recorder",
+    id: "getorcreatebucket",
+    title: "getOrCreateBucket()",
+    relativePath: "docs/lambda/getorcreatebucket.mdx",
+    compId: "articles-docs-lambda-getorcreatebucket",
+    crumb: "Lambda API",
   },
   {
-    id: "recorder/exporting",
-    title: "Exporting a video",
-    relativePath: "docs/recorder/exporting.mdx",
-    compId: "articles-docs-recorder-exporting",
-    crumb: "Recorder",
+    id: "custom-destination",
+    title: "Customizing Lambda output destination",
+    relativePath: "docs/lambda/custom-destination.mdx",
+    compId: "articles-docs-lambda-custom-destination",
+    crumb: "Lambda",
   },
   {
-    id: "recorder/external-recordings",
-    title: "External recordings",
-    relativePath: "docs/recorder/external-recordings.mdx",
-    compId: "articles-docs-recorder-external-recordings",
-    crumb: "Recorder",
+    id: "deletefunction",
+    title: "deleteFunction()",
+    relativePath: "docs/lambda/deletefunction.mdx",
+    compId: "articles-docs-lambda-deletefunction",
+    crumb: "Lambda API",
+  },
+  {
+    id: "estimateprice",
+    title: "estimatePrice()",
+    relativePath: "docs/lambda/estimateprice.mdx",
+    compId: "articles-docs-lambda-estimateprice",
+    crumb: "Lambda API",
   },
   {
-    id: "recorder/gear",
-    title: "Our Gear",
-    relativePath: "docs/recorder/gear.mdx",
-    compId: "articles-docs-recorder-gear",
-    crumb: "Recorder",
+    id: "getrolepolicy",
+    title: "getRolePolicy()",
+    relativePath: "docs/lambda/getrolepolicy.mdx",
+    compId: "articles-docs-lambda-getrolepolicy",
+    crumb: "Lambda API",
   },
   {
-    id: "recorder/index",
-    title: "Remotion Recorder",
-    relativePath: "docs/recorder/index.mdx",
-    compId: "articles-docs-recorder-index",
-    crumb: "Templates",
+    id: "lambda/light-client",
+    title: "Light client",
+    relativePath: "docs/lambda/light-client.mdx",
+    compId: "articles-docs-lambda-light-client",
+    crumb: "Lambda",
   },
   {
-    id: "recorder/lambda-rendering",
-    title: "Render on Lambda",
-    relativePath: "docs/recorder/lambda-rendering.mdx",
-    compId: "articles-docs-recorder-lambda-rendering",
-    crumb: "Recorder",
+    id: "debug",
+    title: "Debugging failed Lambda renders",
+    relativePath: "docs/lambda/troubleshooting/debug.mdx",
+    compId: "articles-docs-lambda-troubleshooting-debug",
+    crumb: "Lambda",
   },
   {
-    id: "recorder/our-recorder",
-    title: "Our Recorder",
-    relativePath: "docs/recorder/our-recorder.mdx",
-    compId: "articles-docs-recorder-our-recorder",
-    crumb: "Recorder",
+    id: "unrecognizedclientexception",
+    title: "UnrecognizedClientException",
+    relativePath: "docs/lambda/troubleshooting/unrecognizedclientexception.mdx",
+    compId: "articles-docs-lambda-troubleshooting-unrecognizedclientexception",
+    crumb: "Lambda Troubleshooting",
   },
   {
-    id: "recorder/record",
-    title: "Record a scene",
-    relativePath: "docs/recorder/record.mdx",
-    compId: "articles-docs-recorder-record",
-    crumb: "Recorder",
+    id: "bucket-disallows-acl",
+    title: "The bucket does not allow ACLs",
+    relativePath: "docs/lambda/troubleshooting/bucket-disallows-acl.mdx",
+    compId: "articles-docs-lambda-troubleshooting-bucket-disallows-acl",
+    crumb: "Lambda Troubleshooting",
   },
   {
-    id: "recorder/roadmap",
-    title: "Roadmap",
-    relativePath: "docs/recorder/roadmap.mdx",
-    compId: "articles-docs-recorder-roadmap",
-    crumb: "Recorder",
+    id: "permissions",
+    title: "AWS Permissions Troubleshooting",
+    relativePath: "docs/lambda/troubleshooting/permissions.mdx",
+    compId: "articles-docs-lambda-troubleshooting-permissions",
+    crumb: "Lambda Troubleshooting",
   },
   {
-    id: "recorder/setup",
-    title: "Setup",
-    relativePath: "docs/recorder/setup.mdx",
-    compId: "articles-docs-recorder-setup",
-    crumb: "Recorder",
+    id: "lambda/troubleshooting/security-token",
+    title: "'\"The security token included in the request is invalid\"'",
+    relativePath: "docs/lambda/troubleshooting/security-token.mdx",
+    compId: "articles-docs-lambda-troubleshooting-security-token",
+    crumb: "Lambda",
   },
   {
-    id: "recorder/source-control",
-    title: "Source Control",
-    relativePath: "docs/recorder/source-control.mdx",
-    compId: "articles-docs-recorder-source-control",
-    crumb: "Recorder",
+    id: "rate-limit",
+    title: "AWS Rate Limit Troubleshooting",
+    relativePath: "docs/lambda/troubleshooting/rate-limit.mdx",
+    compId: "articles-docs-lambda-troubleshooting-rate-limit",
+    crumb: "Lambda Troubleshooting",
   },
   {
-    id: "recorder/support",
-    title: "Get Help",
-    relativePath: "docs/recorder/support.mdx",
-    compId: "articles-docs-recorder-support",
-    crumb: "Recorder",
+    id: "policies",
+    title: "npx remotion lambda policies",
+    relativePath: "docs/lambda/cli/policies.mdx",
+    compId: "articles-docs-lambda-cli-policies",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "recorder/troubleshooting/cannot-read-properties-of-undefined",
-    title: "Cannot read properties of undefined (reading 'decode')",
-    relativePath:
-      "docs/recorder/troubleshooting/cannot-read-properties-of-undefined.mdx",
-    compId:
-      "articles-docs-recorder-troubleshooting-cannot-read-properties-of-undefined",
-    crumb: "Recorder",
+    id: "quotas",
+    title: "npx remotion lambda quotas",
+    relativePath: "docs/lambda/cli/quotas.mdx",
+    compId: "articles-docs-lambda-cli-quotas",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "recorder/troubleshooting/failed-to-execute-get-video-metadata",
-    title: "Failed to execute getVideoMetadata()",
-    relativePath:
-      "docs/recorder/troubleshooting/failed-to-execute-get-video-metadata.mdx",
-    compId:
-      "articles-docs-recorder-troubleshooting-failed-to-execute-get-video-metadata",
-    crumb: "Recorder",
+    id: "functions",
+    title: "npx remotion lambda functions",
+    relativePath: "docs/lambda/cli/functions.mdx",
+    compId: "articles-docs-lambda-cli-functions",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "recorder/upgrading",
-    title: "Upgrading",
-    relativePath: "docs/recorder/upgrading.mdx",
-    compId: "articles-docs-recorder-upgrading",
-    crumb: "Recorder",
+    id: "sites",
+    title: "npx remotion lambda sites",
+    relativePath: "docs/lambda/cli/sites.mdx",
+    compId: "articles-docs-lambda-cli-sites",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "register-root",
-    title: "registerRoot()",
-    relativePath: "docs/register-root.mdx",
-    compId: "articles-docs-register-root",
-    crumb: "API",
+    id: "render",
+    title: "npx remotion lambda render",
+    relativePath: "docs/lambda/cli/render.mdx",
+    compId: "articles-docs-lambda-cli-render",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "remotion",
-    title: "remotion",
-    relativePath: "docs/remotion.mdx",
-    compId: "articles-docs-remotion",
-    crumb: "Core API Reference",
+    id: "regions",
+    title: "npx remotion lambda regions",
+    relativePath: "docs/lambda/cli/regions.mdx",
+    compId: "articles-docs-lambda-cli-regions",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "render-all",
-    title: "Render all compositions",
-    relativePath: "docs/render-all.mdx",
-    compId: "articles-docs-render-all",
-    crumb: "Techniques",
+    id: "lambda/cli/compositions",
+    title: "npx remotion lambda compositions",
+    relativePath: "docs/lambda/cli/compositions.mdx",
+    compId: "articles-docs-lambda-cli-compositions",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "render-as-gif",
-    title: "Rendering GIFs",
-    relativePath: "docs/render-as-gif.mdx",
-    compId: "articles-docs-render-as-gif",
-    crumb: "Techniques",
+    id: "still",
+    title: "npx remotion lambda still",
+    relativePath: "docs/lambda/cli/still.mdx",
+    compId: "articles-docs-lambda-cli-still",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "render",
-    title: "Render your video",
-    relativePath: "docs/render.mdx",
-    compId: "articles-docs-render",
-    crumb: "How To",
+    id: "deleterender",
+    title: "deleteRender()",
+    relativePath: "docs/lambda/deleterender.mdx",
+    compId: "articles-docs-lambda-deleterender",
+    crumb: "Lambda API",
   },
   {
-    id: "ensure-browser",
-    title: "ensureBrowser()",
-    relativePath: "docs/renderer/ensure-browser.mdx",
-    compId: "articles-docs-renderer-ensure-browser",
-    crumb: "@remotion/renderer",
+    id: "runtime",
+    title: "Runtime",
+    relativePath: "docs/lambda/runtime.mdx",
+    compId: "articles-docs-lambda-runtime",
+    crumb: "Lambda",
   },
   {
-    id: "ensure-ffmpeg",
-    title: "ensureFfmpeg()",
-    relativePath: "docs/renderer/ensure-ffmpeg.mdx",
-    compId: "articles-docs-renderer-ensure-ffmpeg",
-    crumb: "@remotion/renderer",
+    id: "upgrading",
+    title: "Upgrading Lambda",
+    relativePath: "docs/lambda/upgrading.mdx",
+    compId: "articles-docs-lambda-upgrading",
+    crumb: null,
   },
   {
-    id: "ensure-ffprobe",
-    title: "ensureFfprobe()",
-    relativePath: "docs/renderer/ensure-ffprobe.mdx",
-    compId: "articles-docs-renderer-ensure-ffprobe",
-    crumb: "@remotion/renderer",
+    id: "region-selection",
+    title: "Region selection",
+    relativePath: "docs/lambda/region-selection.mdx",
+    compId: "articles-docs-lambda-region-selection",
+    crumb: "Lambda",
   },
   {
-    id: "extract-audio",
-    title: "extractAudio()",
-    relativePath: "docs/renderer/extract-audio.mdx",
-    compId: "articles-docs-renderer-extract-audio",
-    crumb: "@remotion/renderer",
+    id: "renderstillonlambda",
+    title: "renderStillOnLambda()",
+    relativePath: "docs/lambda/renderstillonlambda.mdx",
+    compId: "articles-docs-lambda-renderstillonlambda",
+    crumb: "Lambda API",
   },
   {
-    id: "get-can-extract-frames-fast",
-    title: "getCanExtractFramesFast()",
-    relativePath: "docs/renderer/get-can-extract-frames-fast.mdx",
-    compId: "articles-docs-renderer-get-can-extract-frames-fast",
-    crumb: "@remotion/renderer",
+    id: "lambda/custom-layers",
+    title: "Custom Layers",
+    relativePath: "docs/lambda/custom-layers.mdx",
+    compId: "articles-docs-lambda-custom-layers",
+    crumb: "Lambda",
   },
   {
-    id: "get-compositions",
-    title: "getCompositions()",
-    relativePath: "docs/renderer/get-compositions.mdx",
-    compId: "articles-docs-renderer-get-compositions",
-    crumb: "@remotion/renderer",
+    id: "changelog",
+    title: "Prerelease Changelog",
+    relativePath: "docs/lambda/changelog.mdx",
+    compId: "articles-docs-lambda-changelog",
+    crumb: null,
   },
   {
-    id: "get-silent-parts",
-    title: "getSilentParts()",
-    relativePath: "docs/renderer/get-silent-parts.mdx",
-    compId: "articles-docs-renderer-get-silent-parts",
-    crumb: "@remotion/renderer",
+    id: "optimizing-cost",
+    title: "Optimizing for cost",
+    relativePath: "docs/lambda/cost.mdx",
+    compId: "articles-docs-lambda-cost",
+    crumb: "Lambda",
   },
   {
-    id: "get-video-metadata",
-    title: "getVideoMetadata()",
-    relativePath: "docs/renderer/get-video-metadata.mdx",
-    compId: "articles-docs-renderer-get-video-metadata",
-    crumb: "@remotion/renderer",
+    id: "lambda/serverless-framework-integration",
+    title: "Using the Serverless Framework with Remotion Lambda",
+    relativePath: "docs/lambda/serverless-framework-integration.mdx",
+    compId: "articles-docs-lambda-serverless-framework-integration",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "make-cancel-signal",
-    title: "makeCancelSignal()",
-    relativePath: "docs/renderer/make-cancel-signal.mdx",
-    compId: "articles-docs-renderer-make-cancel-signal",
-    crumb: "@remotion/renderer",
+    id: "disk-size",
+    title: "Disk size",
+    relativePath: "docs/lambda/disk-size.mdx",
+    compId: "articles-docs-lambda-disk-size",
+    crumb: "Lambda",
   },
   {
-    id: "open-browser",
-    title: "openBrowser()",
-    relativePath: "docs/renderer/open-browser.mdx",
-    compId: "articles-docs-renderer-open-browser",
-    crumb: "@remotion/renderer",
+    id: "lambda/without-iam/ec2",
+    title: "Authenticating Lambda with EC2",
+    relativePath: "docs/lambda/without-iam/ec2.mdx",
+    compId: "articles-docs-lambda-without-iam-ec2",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "render-frames",
-    title: "renderFrames()",
-    relativePath: "docs/renderer/render-frames.mdx",
-    compId: "articles-docs-renderer-render-frames",
-    crumb: "@remotion/renderer",
+    id: "lambda/without-iam/index",
+    title: "Using Lambda with IAM roles",
+    relativePath: "docs/lambda/without-iam/index.mdx",
+    compId: "articles-docs-lambda-without-iam-index",
+    crumb: "Lambda without IAM",
   },
   {
-    id: "render-media",
-    title: "renderMedia()",
-    relativePath: "docs/renderer/render-media.mdx",
-    compId: "articles-docs-renderer-render-media",
-    crumb: "@remotion/renderer",
+    id: "lambda/without-iam/example",
+    title: "Example setup without IAM user",
+    relativePath: "docs/lambda/without-iam/example.mdx",
+    compId: "articles-docs-lambda-without-iam-example",
+    crumb: "IAM Roles Example",
   },
   {
-    id: "render-still",
-    title: "renderStill()",
-    relativePath: "docs/renderer/render-still.mdx",
-    compId: "articles-docs-renderer-render-still",
-    crumb: "@remotion/renderer",
+    id: "downloadvideo",
+    title: "downloadVideo()",
+    relativePath: "docs/lambda/downloadvideo.mdx",
+    compId: "articles-docs-lambda-downloadvideo",
+    crumb: "Lambda API",
   },
   {
-    id: "select-composition",
-    title: "selectComposition()",
-    relativePath: "docs/renderer/select-composition.mdx",
-    compId: "articles-docs-renderer-select-composition",
-    crumb: "@remotion/renderer",
+    id: "getawsclient",
+    title: "getAwsClient()",
+    relativePath: "docs/lambda/getawsclient.mdx",
+    compId: "articles-docs-lambda-getawsclient",
+    crumb: "Lambda API",
   },
   {
-    id: "stitch-frames-to-video",
-    title: "stitchFramesToVideo()",
-    relativePath: "docs/renderer/stitch-frames-to-video.mdx",
-    compId: "articles-docs-renderer-stitch-frames-to-video",
-    crumb: "@remotion/renderer",
+    id: "lambda/php",
+    title: "Triggering renders from PHP",
+    relativePath: "docs/lambda/php.mdx",
+    compId: "articles-docs-lambda-php",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "renderer",
-    title: "@remotion/renderer",
-    relativePath: "docs/renderer.mdx",
-    compId: "articles-docs-renderer",
-    crumb: null,
+    id: "rendermediaonlambda",
+    title: "renderMediaOnLambda()",
+    relativePath: "docs/lambda/rendermediaonlambda.mdx",
+    compId: "articles-docs-lambda-rendermediaonlambda",
+    crumb: "Lambda API",
   },
   {
-    id: "resources",
-    title: "List of resources",
-    relativePath: "docs/resources.mdx",
-    compId: "articles-docs-resources",
-    crumb: "Ecosystem",
+    id: "simulatepermissions",
+    title: "simulatePermissions()",
+    relativePath: "docs/lambda/simulatepermissions.mdx",
+    compId: "articles-docs-lambda-simulatepermissions",
+    crumb: "Lambda API",
   },
   {
-    id: "rive/index",
-    title: "@remotion/rive",
-    relativePath: "docs/rive/index.mdx",
-    compId: "articles-docs-rive-index",
-    crumb: "Integrations",
+    id: "checklist",
+    title: "Production Checklist",
+    relativePath: "docs/lambda/checklist.mdx",
+    compId: "articles-docs-lambda-checklist",
+    crumb: "Lambda",
   },
   {
-    id: "rive/remotionrivecanvas",
-    title: "<RemotionRiveCanvas>",
-    relativePath: "docs/rive/remotionrivecanvas.mdx",
-    compId: "articles-docs-rive-remotionrivecanvas",
-    crumb: "@remotion/rive",
+    id: "how-lambda-works",
+    title: "How Remotion Lambda works",
+    relativePath: "docs/lambda/how-lambda-works.mdx",
+    compId: "articles-docs-lambda-how-lambda-works",
+    crumb: "Lambda",
   },
   {
-    id: "scaling",
-    title: "Output scaling",
-    relativePath: "docs/scaling.mdx",
-    compId: "articles-docs-scaling",
-    crumb: "How To",
+    id: "presignurl",
+    title: "presignUrl()",
+    relativePath: "docs/lambda/presignurl.mdx",
+    compId: "articles-docs-lambda-presignurl",
+    crumb: "Lambda API",
   },
   {
-    id: "schemas",
-    title: "Defining a schema for your props",
-    relativePath: "docs/schemas.mdx",
-    compId: "articles-docs-schemas",
-    crumb: "How To",
+    id: "lambda/faq",
+    title: "FAQ",
+    relativePath: "docs/lambda/faq.mdx",
+    compId: "articles-docs-lambda-faq",
+    crumb: "Lambda",
   },
   {
-    id: "security",
-    title: "Security Best Practices",
-    relativePath: "docs/security.mdx",
-    compId: "articles-docs-security",
-    crumb: "FAQ",
+    id: "lambda/api",
+    title: "@remotion/lambda",
+    relativePath: "docs/lambda/api.mdx",
+    compId: "articles-docs-lambda-api",
+    crumb: "Render videos without servers on AWS",
   },
   {
-    id: "sequence",
-    title: "<Sequence>",
-    relativePath: "docs/sequence.mdx",
-    compId: "articles-docs-sequence",
-    crumb: "API",
+    id: "validatewebhooksignature",
+    title: "validateWebhookSignature()",
+    relativePath: "docs/lambda/validatewebhooksignature.mdx",
+    compId: "articles-docs-lambda-validatewebhooksignature",
+    crumb: "Lambda API",
   },
   {
-    id: "reusability",
-    title: "Making components reusable",
-    relativePath: "docs/sequences.mdx",
-    compId: "articles-docs-sequences",
-    crumb: "The power of React",
+    id: "getfunctioninfo",
+    title: "getFunctionInfo()",
+    relativePath: "docs/lambda/getfunctioninfo.mdx",
+    compId: "articles-docs-lambda-getfunctioninfo",
+    crumb: "Lambda API",
   },
   {
-    id: "series",
-    title: "<Series>",
-    relativePath: "docs/series.mdx",
-    compId: "articles-docs-series",
-    crumb: "API",
+    id: "setup",
+    title: "Setup",
+    relativePath: "docs/lambda/setup.mdx",
+    compId: "articles-docs-lambda-setup",
+    crumb: "Lambda",
   },
   {
-    id: "shapes/circle",
-    title: "<Circle />",
-    relativePath: "docs/shapes/circle.mdx",
-    compId: "articles-docs-shapes-circle",
-    crumb: "@remotion/shapes",
+    id: "getfunctions",
+    title: "getFunctions()",
+    relativePath: "docs/lambda/getfunctions.mdx",
+    compId: "articles-docs-lambda-getfunctions",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/ellipse",
-    title: "<Ellipse />",
-    relativePath: "docs/shapes/ellipse.mdx",
-    compId: "articles-docs-shapes-ellipse",
-    crumb: "@remotion/shapes",
+    id: "getcompositionsonlambda",
+    title: "getCompositionsOnLambda()",
+    relativePath: "docs/lambda/getcompositionsonlambda.mdx",
+    compId: "articles-docs-lambda-getcompositionsonlambda",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/index",
-    title: "@remotion/shapes",
-    relativePath: "docs/shapes/index.mdx",
-    compId: "articles-docs-shapes-index",
-    crumb: "SVG component library",
+    id: "downloadmedia",
+    title: "downloadMedia()",
+    relativePath: "docs/lambda/downloadmedia.mdx",
+    compId: "articles-docs-lambda-downloadmedia",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/make-circle",
-    title: "makeCircle()",
-    relativePath: "docs/shapes/make-circle.mdx",
-    compId: "articles-docs-shapes-make-circle",
-    crumb: "@remotion/shapes",
+    id: "lambda/s3-public-access",
+    title: "Cannot create a S3 bucket using Remotion",
+    relativePath: "docs/lambda/s3-public-access.mdx",
+    compId: "articles-docs-lambda-s3-public-access",
+    crumb: "DevOps advisory",
   },
   {
-    id: "shapes/make-ellipse",
-    title: "makeEllipse()",
-    relativePath: "docs/shapes/make-ellipse.mdx",
-    compId: "articles-docs-shapes-make-ellipse",
-    crumb: "@remotion/shapes",
+    id: "lambda/limits",
+    title: "Lambda Limits",
+    relativePath: "docs/lambda/limits.mdx",
+    compId: "articles-docs-lambda-limits",
+    crumb: "Lambda",
   },
   {
-    id: "shapes/make-pie",
-    title: "makePie()",
-    relativePath: "docs/shapes/make-pie.mdx",
-    compId: "articles-docs-shapes-make-pie",
-    crumb: "@remotion/shapes",
+    id: "lambda/sqs",
+    title: "Using Lambda with SQS",
+    relativePath: "docs/lambda/sqs.mdx",
+    compId: "articles-docs-lambda-sqs",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "shapes/make-polygon",
-    title: "makePolygon()",
-    relativePath: "docs/shapes/make-polygon.mdx",
-    compId: "articles-docs-shapes-make-polygon",
-    crumb: "@remotion/shapes",
+    id: "lambda/go",
+    title: "Triggering renders from Go",
+    relativePath: "docs/lambda/go.mdx",
+    compId: "articles-docs-lambda-go",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "shapes/make-rect",
-    title: "makeRect()",
-    relativePath: "docs/shapes/make-rect.mdx",
-    compId: "articles-docs-shapes-make-rect",
-    crumb: "@remotion/shapes",
+    id: "deploysite",
+    title: "deploySite()",
+    relativePath: "docs/lambda/deploysite.mdx",
+    compId: "articles-docs-lambda-deploysite",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/make-star",
-    title: "makeStar()",
-    relativePath: "docs/shapes/make-star.mdx",
-    compId: "articles-docs-shapes-make-star",
-    crumb: "@remotion/shapes",
+    id: "uninstall",
+    title: "Uninstall Lambda",
+    relativePath: "docs/lambda/uninstall.mdx",
+    compId: "articles-docs-lambda-uninstall",
+    crumb: null,
   },
   {
-    id: "shapes/make-triangle",
-    title: "makeTriangle()",
-    relativePath: "docs/shapes/make-triangle.mdx",
-    compId: "articles-docs-shapes-make-triangle",
-    crumb: "@remotion/shapes",
+    id: "lambda/bucket-naming",
+    title: "Bucket names in Remotion Lambda",
+    relativePath: "docs/lambda/bucket-naming.mdx",
+    compId: "articles-docs-lambda-bucket-naming",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "shapes/pie",
-    title: "<Pie />",
-    relativePath: "docs/shapes/pie.mdx",
-    compId: "articles-docs-shapes-pie",
-    crumb: "@remotion/shapes",
+    id: "naming-convention",
+    title: "Function naming convention",
+    relativePath: "docs/lambda/naming-convention.mdx",
+    compId: "articles-docs-lambda-naming-convention",
+    crumb: "Lambda",
   },
   {
-    id: "shapes/polygon",
-    title: "<Polygon />",
-    relativePath: "docs/shapes/polygon.mdx",
-    compId: "articles-docs-shapes-polygon",
-    crumb: "@remotion/shapes",
+    id: "lambda/insights",
+    title: "Enable Lambda Insights",
+    relativePath: "docs/lambda/insights.mdx",
+    compId: "articles-docs-lambda-insights",
+    crumb: "Lambda",
   },
   {
-    id: "shapes/rect",
-    title: "<Rect />",
-    relativePath: "docs/shapes/rect.mdx",
-    compId: "articles-docs-shapes-rect",
-    crumb: "@remotion/shapes",
+    id: "deletesite",
+    title: "deleteSite()",
+    relativePath: "docs/lambda/deletesite.mdx",
+    compId: "articles-docs-lambda-deletesite",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/star",
-    title: "<Star />",
-    relativePath: "docs/shapes/star.mdx",
-    compId: "articles-docs-shapes-star",
-    crumb: "@remotion/shapes",
+    id: "compare/motion-canvas",
+    title: "How does Remotion compare to Motion Canvas?",
+    relativePath: "docs/compare/motion-canvas.mdx",
+    compId: "articles-docs-compare-motion-canvas",
+    crumb: "FAQ",
   },
   {
-    id: "shapes/triangle",
-    title: "<Triangle />",
-    relativePath: "docs/shapes/triangle.mdx",
-    compId: "articles-docs-shapes-triangle",
-    crumb: "@remotion/shapes",
+    id: "api",
+    title: "API overview",
+    relativePath: "docs/api.mdx",
+    compId: "articles-docs-api",
+    crumb: null,
   },
   {
-    id: "skia/enable-skia",
-    title: "enableSkia()",
-    relativePath: "docs/skia/enable-skia.mdx",
-    compId: "articles-docs-skia-enable-skia",
-    crumb: "@remotion/skia",
+    id: "staticfile",
+    title: "staticFile()",
+    relativePath: "docs/staticfile.mdx",
+    compId: "articles-docs-staticfile",
+    crumb: "API",
   },
   {
-    id: "skia/skia-canvas",
-    title: "<SkiaCanvas />",
-    relativePath: "docs/skia/skia-canvas.mdx",
-    compId: "articles-docs-skia-skia-canvas",
-    crumb: "@remotion/skia",
+    id: "lottie-staticfile",
+    title: "Loading Lottie animations from staticFile()",
+    relativePath: "docs/lottie/lottie-staticfile.mdx",
+    compId: "articles-docs-lottie-lottie-staticfile",
+    crumb: "@remotion/lottie",
   },
   {
-    id: "skia",
-    title: "@remotion/skia",
-    relativePath: "docs/skia/skia.mdx",
-    compId: "articles-docs-skia-skia",
+    id: "lottie-index",
+    title: "@remotion/lottie",
+    relativePath: "docs/lottie/index.mdx",
+    compId: "articles-docs-lottie-index",
     crumb: null,
   },
   {
-    id: "slow-method-to-extract-frame",
-    title: "Slow method to extract frame",
-    relativePath: "docs/slow-method-to-extract-frame.mdx",
-    compId: "articles-docs-slow-method-to-extract-frame",
-    crumb: "Troubleshooting",
+    id: "getlottiemetadata",
+    title: "getLottieMetadata()",
+    relativePath: "docs/lottie/getlottiemetadata.mdx",
+    compId: "articles-docs-lottie-getlottiemetadata",
+    crumb: "@remotion/lottie",
   },
   {
-    id: "spline",
-    title: "Import from Spline",
-    relativePath: "docs/spline.mdx",
-    compId: "articles-docs-spline",
-    crumb: "Integrations",
+    id: "lottie-remote",
+    title: "Loading Lottie animations from a URL",
+    relativePath: "docs/lottie/lottie-remote.mdx",
+    compId: "articles-docs-lottie-lottie-remote",
+    crumb: "@remotion/lottie",
   },
   {
-    id: "spring",
-    title: "spring()",
-    relativePath: "docs/spring.mdx",
-    compId: "articles-docs-spring",
-    crumb: "API",
+    id: "lottie-comp",
+    title: "<Lottie>",
+    relativePath: "docs/lottie/lottie-comp.mdx",
+    compId: "articles-docs-lottie-lottie-comp",
+    crumb: "@remotion/lottie",
   },
   {
-    id: "ssr-legacy",
-    title: "Server-Side Rendering (v1 and v2)",
-    relativePath: "docs/ssr-legacy.mdx",
-    compId: "articles-docs-ssr-legacy",
-    crumb: "Legacy docs",
+    id: "lottie-lottiefiles",
+    title: "Finding Lottie files to use",
+    relativePath: "docs/lottie/lottie-lottiefiles.mdx",
+    compId: "articles-docs-lottie-lottie-lottiefiles",
+    crumb: "Resources",
   },
   {
-    id: "ssr-node",
-    title: "Rendering using SSR APIs",
-    relativePath: "docs/ssr-node.mdx",
-    compId: "articles-docs-ssr-node",
-    crumb: "Server-side rendering",
+    id: "overlay",
+    title: "Creating overlays",
+    relativePath: "docs/overlay.mdx",
+    compId: "articles-docs-overlay",
+    crumb: null,
   },
   {
-    id: "ssr",
-    title: "Server-Side Rendering",
-    relativePath: "docs/ssr.mdx",
-    compId: "articles-docs-ssr",
-    crumb: "The power of",
+    id: "transparent-videos",
+    title: "Transparent videos",
+    relativePath: "docs/transparent-videos.mdx",
+    compId: "articles-docs-transparent-videos",
+    crumb: "Techniques",
   },
   {
-    id: "standalone",
-    title: "Useful functions outside of Remotion",
-    relativePath: "docs/standalone.mdx",
-    compId: "articles-docs-standalone",
-    crumb: null,
+    id: "recorder/create",
+    title: "Create a new video",
+    relativePath: "docs/recorder/create.mdx",
+    compId: "articles-docs-recorder-create",
+    crumb: "Recorder",
   },
   {
-    id: "staticfile-relative-paths",
-    title: "staticFile() does not support relative paths",
-    relativePath: "docs/static-file-relative-paths.mdx",
-    compId: "articles-docs-static-file-relative-paths",
-    crumb: "Troubleshooting",
+    id: "recorder/lambda-rendering",
+    title: "Render on Lambda",
+    relativePath: "docs/recorder/lambda-rendering.mdx",
+    compId: "articles-docs-recorder-lambda-rendering",
+    crumb: "Recorder",
   },
   {
-    id: "staticfile-remote-urls",
-    title: "staticFile() does not support remote URLs",
-    relativePath: "docs/static-file-remote-urls.mdx",
-    compId: "articles-docs-static-file-remote-urls",
-    crumb: "Troubleshooting",
+    id: "recorder/exporting",
+    title: "Exporting a video",
+    relativePath: "docs/recorder/exporting.mdx",
+    compId: "articles-docs-recorder-exporting",
+    crumb: "Recorder",
   },
   {
-    id: "staticfile",
-    title: "staticFile()",
-    relativePath: "docs/staticfile.mdx",
-    compId: "articles-docs-staticfile",
-    crumb: "API",
+    id: "recorder/external-recordings",
+    title: "External recordings",
+    relativePath: "docs/recorder/external-recordings.mdx",
+    compId: "articles-docs-recorder-external-recordings",
+    crumb: "Recorder",
   },
   {
-    id: "still",
-    title: "<Still>",
-    relativePath: "docs/still.mdx",
-    compId: "articles-docs-still",
-    crumb: "API",
+    id: "recorder/captions",
+    title: "Generate captions",
+    relativePath: "docs/recorder/captions.mdx",
+    compId: "articles-docs-recorder-captions",
+    crumb: "Recorder",
   },
   {
-    id: "stills",
-    title: "Still images",
-    relativePath: "docs/stills.mdx",
-    compId: "articles-docs-stills",
-    crumb: "How to generate",
+    id: "recorder/index",
+    title: "Remotion Recorder",
+    relativePath: "docs/recorder/index.mdx",
+    compId: "articles-docs-recorder-index",
+    crumb: "Templates",
   },
   {
-    id: "studio/api",
-    title: "@remotion/studio",
-    relativePath: "docs/studio/api.mdx",
-    compId: "articles-docs-studio-api",
-    crumb: null,
+    id: "recorder/support",
+    title: "Get Help",
+    relativePath: "docs/recorder/support.mdx",
+    compId: "articles-docs-recorder-support",
+    crumb: "Recorder",
   },
   {
-    id: "studio/delete-static-file",
-    title: "deleteStaticFile()",
-    relativePath: "docs/studio/delete-static-file.mdx",
-    compId: "articles-docs-studio-delete-static-file",
-    crumb: "@remotion/studio",
+    id: "recorder/source-control",
+    title: "Source Control",
+    relativePath: "docs/recorder/source-control.mdx",
+    compId: "articles-docs-recorder-source-control",
+    crumb: "Recorder",
   },
   {
-    id: "studio/deploy-server",
-    title: "Deploy the Remotion Studio on a VPS",
-    relativePath: "docs/studio/deploy-server.mdx",
-    compId: "articles-docs-studio-deploy-server",
-    crumb: "Remotion Studio",
+    id: "recorder/buy",
+    title: "Buy",
+    relativePath: "docs/recorder/buy.mdx",
+    compId: "articles-docs-recorder-buy",
+    crumb: "Recorder",
   },
   {
-    id: "studio/deploy-static",
-    title: "Deploy Remotion Studio as static site",
-    relativePath: "docs/studio/deploy-static.mdx",
-    compId: "articles-docs-studio-deploy-static",
-    crumb: "Remotion Studio",
+    id: "recorder/troubleshooting/failed-to-execute-get-video-metadata",
+    title: "Failed to execute getVideoMetadata()",
+    relativePath:
+      "docs/recorder/troubleshooting/failed-to-execute-get-video-metadata.mdx",
+    compId:
+      "articles-docs-recorder-troubleshooting-failed-to-execute-get-video-metadata",
+    crumb: "Recorder",
+  },
+  {
+    id: "recorder/troubleshooting/cannot-read-properties-of-undefined",
+    title: "Cannot read properties of undefined (reading 'decode')",
+    relativePath:
+      "docs/recorder/troubleshooting/cannot-read-properties-of-undefined.mdx",
+    compId:
+      "articles-docs-recorder-troubleshooting-cannot-read-properties-of-undefined",
+    crumb: "Recorder",
   },
   {
-    id: "studio/focus-default-props-path",
-    title: "focusDefaultPropsPath()",
-    relativePath: "docs/studio/focus-default-props-path.mdx",
-    compId: "articles-docs-studio-focus-default-props-path",
-    crumb: "@remotion/studio",
+    id: "recorder/record",
+    title: "Record a scene",
+    relativePath: "docs/recorder/record.mdx",
+    compId: "articles-docs-recorder-record",
+    crumb: "Recorder",
   },
   {
-    id: "studio/get-static-files",
-    title: "getStaticFiles()",
-    relativePath: "docs/studio/get-static-files.mdx",
-    compId: "articles-docs-studio-get-static-files",
-    crumb: "@remotion/studio",
+    id: "recorder/gear",
+    title: "Our Gear",
+    relativePath: "docs/recorder/gear.mdx",
+    compId: "articles-docs-recorder-gear",
+    crumb: "Recorder",
   },
   {
-    id: "studio/quick-switcher",
-    title: "Quick Switcher",
-    relativePath: "docs/studio/quick-switcher.mdx",
-    compId: "articles-docs-studio-quick-switcher",
-    crumb: "Remotion Studio",
+    id: "recorder/roadmap",
+    title: "Roadmap",
+    relativePath: "docs/recorder/roadmap.mdx",
+    compId: "articles-docs-recorder-roadmap",
+    crumb: "Recorder",
   },
   {
-    id: "studio/reevaluate-composition",
-    title: "reevaluateComposition()",
-    relativePath: "docs/studio/reevaluate-composition.mdx",
-    compId: "articles-docs-studio-reevaluate-composition",
-    crumb: "@remotion/studio",
+    id: "recorder/upgrading",
+    title: "Upgrading",
+    relativePath: "docs/recorder/upgrading.mdx",
+    compId: "articles-docs-recorder-upgrading",
+    crumb: "Recorder",
   },
   {
-    id: "studio/restart-studio",
-    title: "restartStudio()",
-    relativePath: "docs/studio/restart-studio.mdx",
-    compId: "articles-docs-studio-restart-studio",
-    crumb: "@remotion/studio",
+    id: "recorder/editing/scenes",
+    title: "Scenes",
+    relativePath: "docs/recorder/editing/scenes.mdx",
+    compId: "articles-docs-recorder-editing-scenes",
+    crumb: "Recorder",
   },
   {
-    id: "studio/save-default-props",
-    title: "saveDefaultProps()",
-    relativePath: "docs/studio/save-default-props.mdx",
-    compId: "articles-docs-studio-save-default-props",
-    crumb: "@remotion/studio",
+    id: "recorder/editing/b-roll",
+    title: "B-Roll",
+    relativePath: "docs/recorder/editing/b-roll.mdx",
+    compId: "articles-docs-recorder-editing-b-roll",
+    crumb: "Recorder",
   },
   {
-    id: "studio/shortcuts",
-    title: "Keyboard navigation",
-    relativePath: "docs/studio/shortcuts.mdx",
-    compId: "articles-docs-studio-shortcuts",
-    crumb: "Remotion Studio",
+    id: "recorder/editing/music",
+    title: "Adding music",
+    relativePath: "docs/recorder/editing/music.mdx",
+    compId: "articles-docs-recorder-editing-music",
+    crumb: "Recorder",
   },
   {
-    id: "studio/studio",
-    title: "Starting the Studio",
-    relativePath: "docs/studio/studio.mdx",
-    compId: "articles-docs-studio-studio",
-    crumb: "Timeline basics",
+    id: "recorder/editing/chapters",
+    title: "Chapters",
+    relativePath: "docs/recorder/editing/chapters.mdx",
+    compId: "articles-docs-recorder-editing-chapters",
+    crumb: "Recorder",
   },
   {
-    id: "studio/update-default-props",
-    title: "updateDefaultProps()",
-    relativePath: "docs/studio/update-default-props.mdx",
-    compId: "articles-docs-studio-update-default-props",
-    crumb: "@remotion/studio",
+    id: "recorder/editing/captions",
+    title: "Edit captions",
+    relativePath: "docs/recorder/editing/captions.mdx",
+    compId: "articles-docs-recorder-editing-captions",
+    crumb: "Recorder",
   },
   {
-    id: "studio/watch-public-folder",
-    title: "watchPublicFolder()",
-    relativePath: "docs/studio/watch-public-folder.mdx",
-    compId: "articles-docs-studio-watch-public-folder",
-    crumb: "@remotion/studio",
+    id: "recorder/editing/cutting-clips",
+    title: "Cutting clips",
+    relativePath: "docs/recorder/editing/cutting-clips.mdx",
+    compId: "articles-docs-recorder-editing-cutting-clips",
+    crumb: "Recorder",
   },
   {
-    id: "studio/watch-static-file",
-    title: "watchStaticFile()",
-    relativePath: "docs/studio/watch-static-file.mdx",
-    compId: "articles-docs-studio-watch-static-file",
-    crumb: "@remotion/studio",
+    id: "recorder/editing/editing",
+    title: "Start editing",
+    relativePath: "docs/recorder/editing/editing.mdx",
+    compId: "articles-docs-recorder-editing-editing",
+    crumb: "Recorder",
   },
   {
-    id: "studio/write-static-file",
-    title: "writeStaticFile()",
-    relativePath: "docs/studio/write-static-file.mdx",
-    compId: "articles-docs-studio-write-static-file",
-    crumb: "@remotion/studio",
+    id: "recorder/editing/endcard",
+    title: "Endcard",
+    relativePath: "docs/recorder/editing/endcard.mdx",
+    compId: "articles-docs-recorder-editing-endcard",
+    crumb: "Recorder",
   },
   {
-    id: "support",
-    title: "Support Policy",
-    relativePath: "docs/support.mdx",
-    compId: "articles-docs-support",
-    crumb: "Let us help you",
+    id: "recorder/editing/transitions",
+    title: "Transitions",
+    relativePath: "docs/recorder/editing/transitions.mdx",
+    compId: "articles-docs-recorder-editing-transitions",
+    crumb: "Recorder",
   },
   {
-    id: "tailwind/enable-tailwind",
-    title: "enableTailwind()",
-    relativePath: "docs/tailwind/enable-tailwind.mdx",
-    compId: "articles-docs-tailwind-enable-tailwind",
-    crumb: "@remotion/tailwind",
+    id: "recorder/editing/silence-removal",
+    title: "Silence removal",
+    relativePath: "docs/recorder/editing/silence-removal.mdx",
+    compId: "articles-docs-recorder-editing-silence-removal",
+    crumb: "Recorder",
   },
   {
-    id: "tailwind",
-    title: "@remotion/tailwind",
-    relativePath: "docs/tailwind/overview.mdx",
-    compId: "articles-docs-tailwind-overview",
-    crumb: null,
+    id: "recorder/editing/layout",
+    title: "Layout",
+    relativePath: "docs/recorder/editing/layout.mdx",
+    compId: "articles-docs-recorder-editing-layout",
+    crumb: "Recorder",
   },
   {
-    id: "tailwind-legacy",
-    title: "TailwindCSS v2 (Legacy)",
-    relativePath: "docs/tailwind-2.mdx",
-    compId: "articles-docs-tailwind-2",
-    crumb: "Legacy docs",
+    id: "recorder/our-recorder",
+    title: "Our Recorder",
+    relativePath: "docs/recorder/our-recorder.mdx",
+    compId: "articles-docs-recorder-our-recorder",
+    crumb: "Recorder",
   },
   {
-    id: "tailwind",
-    title: "TailwindCSS",
-    relativePath: "docs/tailwind.mdx",
-    compId: "articles-docs-tailwind",
-    crumb: "text-lg font-bold",
+    id: "recorder/before-you-buy",
+    title: "Before you buy",
+    relativePath: "docs/recorder/before-you-buy.mdx",
+    compId: "articles-docs-recorder-before-you-buy",
+    crumb: "Recorder",
   },
   {
-    id: "target-closed",
-    title: "'Target closed' error message",
-    relativePath: "docs/target-closed.mdx",
-    compId: "articles-docs-target-closed",
-    crumb: "Troubleshooting",
+    id: "recorder/setup",
+    title: "Setup",
+    relativePath: "docs/recorder/setup.mdx",
+    compId: "articles-docs-recorder-setup",
+    crumb: "Recorder",
   },
   {
-    id: "terminology/bundle",
-    title: "Remotion Bundle",
-    relativePath: "docs/terminology/bundle.mdx",
-    compId: "articles-docs-terminology-bundle",
-    crumb: "Terminology",
+    id: "fonts-api",
+    title: "@remotion/fonts",
+    relativePath: "docs/fonts-api/index.mdx",
+    compId: "articles-docs-fonts-api-index",
+    crumb: null,
   },
   {
-    id: "terminology/cloud-run-url",
-    title: "Cloud Run URL",
-    relativePath: "docs/terminology/cloud-run-url.mdx",
-    compId: "articles-docs-terminology-cloud-run-url",
-    crumb: "Terminology",
+    id: "fonts-api/load-font",
+    title: "loadFont()",
+    relativePath: "docs/fonts-api/load-font.mdx",
+    compId: "articles-docs-fonts-api-load-font",
+    crumb: "@remotion/fonts",
   },
   {
-    id: "terminology/composition",
-    title: "Composition",
-    relativePath: "docs/terminology/composition.mdx",
-    compId: "articles-docs-terminology-composition",
-    crumb: "Terminology",
+    id: "authoring-packages",
+    title: "Authoring a Remotion library",
+    relativePath: "docs/creating-a-library.mdx",
+    compId: "articles-docs-creating-a-library",
+    crumb: "How to",
   },
   {
-    id: "terminology/concurrency",
-    title: "Concurrency",
-    relativePath: "docs/terminology/concurrency.mdx",
-    compId: "articles-docs-terminology-concurrency",
-    crumb: "Terminology",
+    id: "use-buffer-state",
+    title: "useBufferState()",
+    relativePath: "docs/use-buffer-state.mdx",
+    compId: "articles-docs-use-buffer-state",
+    crumb: "API",
   },
   {
-    id: "terminology/entry-point",
-    title: "Entry point",
-    relativePath: "docs/terminology/entry-point.mdx",
-    compId: "articles-docs-terminology-entry-point",
-    crumb: "Terminology",
+    id: "transforms",
+    title: "Transforms",
+    relativePath: "docs/transforms.mdx",
+    compId: "articles-docs-transforms",
+    crumb: "The fundamentals",
   },
   {
-    id: "terminology/input-props",
-    title: "Input Props",
-    relativePath: "docs/terminology/input-props.mdx",
-    compId: "articles-docs-terminology-input-props",
-    crumb: "Terminology",
+    id: "use-current-frame",
+    title: "useCurrentFrame()",
+    relativePath: "docs/use-current-frame.mdx",
+    compId: "articles-docs-use-current-frame",
+    crumb: "API",
   },
   {
-    id: "terminology/player",
-    title: "Remotion Player",
-    relativePath: "docs/terminology/player.mdx",
-    compId: "articles-docs-terminology-player",
-    crumb: "Terminology",
+    id: "enable-scss/overview",
+    title: "@remotion/enable-scss",
+    relativePath: "docs/enable-scss/overview.mdx",
+    compId: "articles-docs-enable-scss-overview",
+    crumb: null,
   },
   {
-    id: "terminology/public-dir",
-    title: "Public directory",
-    relativePath: "docs/terminology/public-dir.mdx",
-    compId: "articles-docs-terminology-public-dir",
-    crumb: "Terminology",
+    id: "enable-scss/enable-scss",
+    title: "enableScss()",
+    relativePath: "docs/enable-scss/enable-scss.mdx",
+    compId: "articles-docs-enable-scss-enable-scss",
+    crumb: "@remotion/enable-scss",
   },
   {
-    id: "terminology/remotion-root",
-    title: "Remotion Root",
-    relativePath: "docs/terminology/remotion-root.mdx",
-    compId: "articles-docs-terminology-remotion-root",
-    crumb: "Terminology",
+    id: "3-0-migration",
+    title: "v3.0 Migration",
+    relativePath: "docs/3-0-migration.mdx",
+    compId: "articles-docs-3-0-migration",
+    crumb: "Version Upgrade",
   },
   {
-    id: "terminology/root-file",
-    title: "Root file",
-    relativePath: "docs/terminology/root-file.mdx",
-    compId: "articles-docs-terminology-root-file",
-    crumb: "Terminology",
+    id: "quality",
+    title: "Quality Guide",
+    relativePath: "docs/quality.mdx",
+    compId: "articles-docs-quality",
+    crumb: "Make it look good",
   },
   {
-    id: "terminology/sequence",
-    title: "Sequence",
-    relativePath: "docs/terminology/sequence.mdx",
-    compId: "articles-docs-terminology-sequence",
-    crumb: "Terminology",
+    id: "composition",
+    title: "<Composition>",
+    relativePath: "docs/composition.mdx",
+    compId: "articles-docs-composition",
+    crumb: "API",
   },
   {
-    id: "terminology/serve-url",
-    title: "Serve URL",
-    relativePath: "docs/terminology/serve-url.mdx",
-    compId: "articles-docs-terminology-serve-url",
-    crumb: "Terminology",
+    id: "audio",
+    title: "<Audio>",
+    relativePath: "docs/audio.mdx",
+    compId: "articles-docs-audio",
+    crumb: "How to",
   },
   {
-    id: "terminology/service-name",
-    title: "Service Name",
-    relativePath: "docs/terminology/service-name.mdx",
-    compId: "articles-docs-terminology-service-name",
-    crumb: "Terminology",
+    id: "visualize-audio",
+    title: "visualizeAudio()",
+    relativePath: "docs/visualize-audio.mdx",
+    compId: "articles-docs-visualize-audio",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "terminology/studio",
-    title: "Remotion Studio",
-    relativePath: "docs/terminology/studio.mdx",
-    compId: "articles-docs-terminology-studio",
-    crumb: "Terminology",
+    id: "react-18",
+    title: "Upgrade to React 18",
+    relativePath: "docs/react-18.mdx",
+    compId: "articles-docs-react-18",
+    crumb: "The new and shiny",
   },
   {
-    id: "terminology",
-    title: "Terminology",
-    relativePath: "docs/terminology.mdx",
-    compId: "articles-docs-terminology",
-    crumb: "The Remotion dictionary",
+    id: "cloudrun-alpha",
+    title: "Cloud Run Alpha",
+    relativePath: "docs/cloudrun-alpha.mdx",
+    compId: "articles-docs-cloudrun-alpha",
+    crumb: "Version Upgrade",
   },
   {
-    id: "the-fundamentals",
-    title: "The fundamentals",
-    relativePath: "docs/the-fundamentals.mdx",
-    compId: "articles-docs-the-fundamentals",
-    crumb: "Getting started",
+    id: "easing",
+    title: "Easing",
+    relativePath: "docs/easing.mdx",
+    compId: "articles-docs-easing",
+    crumb: "API",
   },
   {
-    id: "third-party",
-    title: "Third party integrations",
-    relativePath: "docs/third-party.mdx",
-    compId: "articles-docs-third-party",
-    crumb: "Integrations",
+    id: "resources",
+    title: "List of resources",
+    relativePath: "docs/resources.mdx",
+    compId: "articles-docs-resources",
+    crumb: "Ecosystem",
   },
   {
-    id: "three-canvas",
-    title: "<ThreeCanvas>",
-    relativePath: "docs/three-canvas.mdx",
-    compId: "articles-docs-three-canvas",
-    crumb: "@remotion/three",
+    id: "schemas",
+    title: "Defining a schema for your props",
+    relativePath: "docs/schemas.mdx",
+    compId: "articles-docs-schemas",
+    crumb: "How To",
   },
   {
-    id: "three",
-    title: "@remotion/three",
-    relativePath: "docs/three.mdx",
-    compId: "articles-docs-three",
+    id: "slow-method-to-extract-frame",
+    title: "Slow method to extract frame",
+    relativePath: "docs/slow-method-to-extract-frame.mdx",
+    compId: "articles-docs-slow-method-to-extract-frame",
+    crumb: "Troubleshooting",
+  },
+  {
+    id: "lambda",
+    title: "@remotion/lambda",
+    relativePath: "docs/lambda.mdx",
+    compId: "articles-docs-lambda",
     crumb: null,
   },
   {
-    id: "timeout",
-    title: "Debugging timeouts",
-    relativePath: "docs/timeout.mdx",
-    compId: "articles-docs-timeout",
-    crumb: "Troubleshooting",
+    id: "transitions/timings/springtiming",
+    title: "springTiming()",
+    relativePath: "docs/transitions/timings/springtiming.mdx",
+    compId: "articles-docs-transitions-timings-springtiming",
+    crumb: "@remotion/transitions - Timings",
   },
   {
-    id: "transforms",
-    title: "Transforms",
-    relativePath: "docs/transforms.mdx",
-    compId: "articles-docs-transforms",
-    crumb: "The fundamentals",
+    id: "transitions/timings/index",
+    title: "Timings",
+    relativePath: "docs/transitions/timings/index.mdx",
+    compId: "articles-docs-transitions-timings-index",
+    crumb: "@remotion/transitions",
   },
   {
-    id: "transitions/audio-transitions",
-    title: "Transitioning with audio",
-    relativePath: "docs/transitions/audio-transitions.mdx",
-    compId: "articles-docs-transitions-audio-transitions",
+    id: "transitions/timings/custom",
+    title: "Custom timings",
+    relativePath: "docs/transitions/timings/custom.mdx",
+    compId: "articles-docs-transitions-timings-custom",
     crumb: "@remotion/transitions",
   },
+  {
+    id: "transitions/timings/lineartiming",
+    title: "linearTiming()",
+    relativePath: "docs/transitions/timings/lineartiming.mdx",
+    compId: "articles-docs-transitions-timings-lineartiming",
+    crumb: "@remotion/transitions - Timings",
+  },
   {
     id: "transitions/index",
     title: "@remotion/transitions",
@@ -3225,6 +3128,20 @@ export const articles = [
     compId: "articles-docs-transitions-presentations-clock-wipe",
     crumb: "@remotion/transitions - Presentations",
   },
+  {
+    id: "transitions/presentations/index",
+    title: "Presentations",
+    relativePath: "docs/transitions/presentations/index.mdx",
+    compId: "articles-docs-transitions-presentations-index",
+    crumb: "@remotion/transitions",
+  },
+  {
+    id: "transitions/presentations/custom",
+    title: "Custom presentations",
+    relativePath: "docs/transitions/presentations/custom.mdx",
+    compId: "articles-docs-transitions-presentations-custom",
+    crumb: "@remotion/transitions",
+  },
   {
     id: "transitions/presentations/cube",
     title: "cube()",
@@ -3233,313 +3150,410 @@ export const articles = [
     crumb: "@remotion/transitions - Presentations",
   },
   {
-    id: "transitions/presentations/custom",
-    title: "Custom presentations",
-    relativePath: "docs/transitions/presentations/custom.mdx",
-    compId: "articles-docs-transitions-presentations-custom",
-    crumb: "@remotion/transitions",
+    id: "transitions/presentations/wipe",
+    title: "wipe()",
+    relativePath: "docs/transitions/presentations/wipe.mdx",
+    compId: "articles-docs-transitions-presentations-wipe",
+    crumb: "@remotion/transitions - Presentations",
+  },
+  {
+    id: "transitions/presentations/fade",
+    title: "fade()",
+    relativePath: "docs/transitions/presentations/fade.mdx",
+    compId: "articles-docs-transitions-presentations-fade",
+    crumb: "@remotion/transitions - Presentations",
+  },
+  {
+    id: "transitions/presentations/slide",
+    title: "slide()",
+    relativePath: "docs/transitions/presentations/slide.mdx",
+    compId: "articles-docs-transitions-presentations-slide",
+    crumb: "@remotion/transitions - Presentations",
+  },
+  {
+    id: "transitions/presentations/flip",
+    title: "flip()",
+    relativePath: "docs/transitions/presentations/flip.mdx",
+    compId: "articles-docs-transitions-presentations-flip",
+    crumb: "@remotion/transitions - Presentations",
+  },
+  {
+    id: "transitions/transitionseries",
+    title: "<TransitionSeries>",
+    relativePath: "docs/transitions/transitionseries.mdx",
+    compId: "articles-docs-transitions-transitionseries",
+    crumb: "@remotion/transitions",
+  },
+  {
+    id: "transitions/audio-transitions",
+    title: "Transitioning with audio",
+    relativePath: "docs/transitions/audio-transitions.mdx",
+    compId: "articles-docs-transitions-audio-transitions",
+    crumb: "@remotion/transitions",
+  },
+  {
+    id: "video-vs-offthreadvideo",
+    title: "<Video> vs. <OffthreadVideo>",
+    relativePath: "docs/video-vs-offthreadvideo.mdx",
+    compId: "articles-docs-video-vs-offthreadvideo",
+    crumb: "Comparison",
+  },
+  {
+    id: "ffmpeg",
+    title: "Installing FFmpeg",
+    relativePath: "docs/ffmpeg.mdx",
+    compId: "articles-docs-ffmpeg",
+    crumb: "(you don't have to)",
+  },
+  {
+    id: "video",
+    title: "<Video>",
+    relativePath: "docs/video.mdx",
+    compId: "articles-docs-video",
+    crumb: "API",
+  },
+  {
+    id: "measure-spring",
+    title: "measureSpring()",
+    relativePath: "docs/measure-spring.mdx",
+    compId: "articles-docs-measure-spring",
+    crumb: "API",
+  },
+  {
+    id: "javascript",
+    title: "Plain JavaScript",
+    relativePath: "docs/jsx-support.mdx",
+    compId: "articles-docs-jsx-support",
+    crumb: "How To",
+  },
+  {
+    id: "stills",
+    title: "Still images",
+    relativePath: "docs/stills.mdx",
+    compId: "articles-docs-stills",
+    crumb: "How to generate",
+  },
+  {
+    id: "get-help",
+    title: "Get help",
+    relativePath: "docs/ask-for-help.mdx",
+    compId: "articles-docs-ask-for-help",
+    crumb: null,
   },
   {
-    id: "transitions/presentations/fade",
-    title: "fade()",
-    relativePath: "docs/transitions/presentations/fade.mdx",
-    compId: "articles-docs-transitions-presentations-fade",
-    crumb: "@remotion/transitions - Presentations",
+    id: "still",
+    title: "<Still>",
+    relativePath: "docs/still.mdx",
+    compId: "articles-docs-still",
+    crumb: "API",
   },
   {
-    id: "transitions/presentations/flip",
-    title: "flip()",
-    relativePath: "docs/transitions/presentations/flip.mdx",
-    compId: "articles-docs-transitions-presentations-flip",
-    crumb: "@remotion/transitions - Presentations",
+    id: "preload-video",
+    title: "preloadVideo()",
+    relativePath: "docs/preload/preload-video.mdx",
+    compId: "articles-docs-preload-preload-video",
+    crumb: "@remotion/player",
   },
   {
-    id: "transitions/presentations/index",
-    title: "Presentations",
-    relativePath: "docs/transitions/presentations/index.mdx",
-    compId: "articles-docs-transitions-presentations-index",
-    crumb: "@remotion/transitions",
+    id: "preload-audio",
+    title: "preloadAudio()",
+    relativePath: "docs/preload/preload-audio.mdx",
+    compId: "articles-docs-preload-preload-audio",
+    crumb: "@remotion/preload",
   },
   {
-    id: "transitions/presentations/slide",
-    title: "slide()",
-    relativePath: "docs/transitions/presentations/slide.mdx",
-    compId: "articles-docs-transitions-presentations-slide",
-    crumb: "@remotion/transitions - Presentations",
+    id: "preload-image",
+    title: "preloadImage()",
+    relativePath: "docs/preload/preload-image.mdx",
+    compId: "articles-docs-preload-preload-image",
+    crumb: "@remotion/preload",
   },
   {
-    id: "transitions/presentations/wipe",
-    title: "wipe()",
-    relativePath: "docs/transitions/presentations/wipe.mdx",
-    compId: "articles-docs-transitions-presentations-wipe",
-    crumb: "@remotion/transitions - Presentations",
+    id: "preload-font",
+    title: "preloadFont()",
+    relativePath: "docs/preload/preload-font.mdx",
+    compId: "articles-docs-preload-preload-font",
+    crumb: "@remotion/preload",
   },
   {
-    id: "transitions/timings/custom",
-    title: "Custom timings",
-    relativePath: "docs/transitions/timings/custom.mdx",
-    compId: "articles-docs-transitions-timings-custom",
-    crumb: "@remotion/transitions",
+    id: "preload",
+    title: "@remotion/preload",
+    relativePath: "docs/preload/preload.mdx",
+    compId: "articles-docs-preload-preload",
+    crumb: null,
   },
   {
-    id: "transitions/timings/index",
-    title: "Timings",
-    relativePath: "docs/transitions/timings/index.mdx",
-    compId: "articles-docs-transitions-timings-index",
-    crumb: "@remotion/transitions",
+    id: "version",
+    title: "VERSION",
+    relativePath: "docs/version.mdx",
+    compId: "articles-docs-version",
+    crumb: "API",
   },
   {
-    id: "transitions/timings/lineartiming",
-    title: "linearTiming()",
-    relativePath: "docs/transitions/timings/lineartiming.mdx",
-    compId: "articles-docs-transitions-timings-lineartiming",
-    crumb: "@remotion/transitions - Timings",
+    id: "prereleases",
+    title: "Testing prereleases",
+    relativePath: "docs/prereleases.mdx",
+    compId: "articles-docs-prereleases",
+    crumb: "Only the brave",
   },
   {
-    id: "transitions/timings/springtiming",
-    title: "springTiming()",
-    relativePath: "docs/transitions/timings/springtiming.mdx",
-    compId: "articles-docs-transitions-timings-springtiming",
-    crumb: "@remotion/transitions - Timings",
+    id: "artifact",
+    title: "<Artifact>",
+    relativePath: "docs/artifact.mdx",
+    compId: "articles-docs-artifact",
+    crumb: "API",
   },
   {
-    id: "transitions/transitionseries",
-    title: "<TransitionSeries>",
-    relativePath: "docs/transitions/transitionseries.mdx",
-    compId: "articles-docs-transitions-transitionseries",
-    crumb: "@remotion/transitions",
+    id: "license",
+    title: "License & Pricing",
+    relativePath: "docs/license.mdx",
+    compId: "articles-docs-license",
+    crumb: null,
   },
   {
-    id: "transitioning",
-    title: "Transitions",
-    relativePath: "docs/transitions.mdx",
-    compId: "articles-docs-transitions",
+    id: "fonts",
+    title: "Using fonts",
+    relativePath: "docs/fonts.mdx",
+    compId: "articles-docs-fonts",
     crumb: "Techniques",
   },
   {
-    id: "transparent-videos",
-    title: "Transparent videos",
-    relativePath: "docs/transparent-videos.mdx",
-    compId: "articles-docs-transparent-videos",
-    crumb: "Techniques",
+    id: "enametoolong",
+    title: "ENAMETOOLONG",
+    relativePath: "docs/enametoolong.mdx",
+    compId: "articles-docs-enametoolong",
+    crumb: "Troubleshooting",
   },
   {
-    id: "troubleshooting/background-image",
-    title: "Flickering when using background-image or mask-image",
-    relativePath: "docs/troubleshooting/background-image.mdx",
-    compId: "articles-docs-troubleshooting-background-image",
-    crumb: "Common mistakes",
+    id: "get-waveform-portion",
+    title: "getWaveformPortion()",
+    relativePath: "docs/get-waveform-portion.mdx",
+    compId: "articles-docs-get-waveform-portion",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "broken-fast-refresh",
-    title: "Fast Refresh not working",
-    relativePath: "docs/troubleshooting/broken-fast-refresh.mdx",
-    compId: "articles-docs-troubleshooting-broken-fast-refresh",
-    crumb: "Troubleshooting",
+    id: "webpack",
+    title: "Custom Webpack config",
+    relativePath: "docs/overwriting-webpack-config.mdx",
+    compId: "articles-docs-overwriting-webpack-config",
+    crumb: "How To",
   },
   {
-    id: "troubleshooting/browser-launch",
-    title: "Failed to launch the browser process",
-    relativePath: "docs/troubleshooting/browser-launch.mdx",
-    compId: "articles-docs-troubleshooting-browser-launch",
-    crumb: "Troubleshooting",
+    id: '"null"',
+    title: "<Experimental.Null>",
+    relativePath: "docs/null.mdx",
+    compId: "articles-docs-null",
+    crumb: "Experimental API",
   },
   {
-    id: "troubleshooting/bundling-bundle",
-    title: "Calling bundle() in bundled code",
-    relativePath: "docs/troubleshooting/bundling-bundle.mdx",
-    compId: "articles-docs-troubleshooting-bundling-bundle",
-    crumb: "Troubleshooting",
+    id: "parameterized-rendering",
+    title: "Parameterized videos",
+    relativePath: "docs/parameterized-rendering.mdx",
+    compId: "articles-docs-parameterized-rendering",
+    crumb: "How To",
   },
   {
-    id: "troubleshooting/could-not-be-parsed-as-a-value-list",
-    title: "The source provided could not be parsed as a value list",
-    relativePath:
-      "docs/troubleshooting/could-not-be-parsed-as-a-value-list.mdx",
-    compId: "articles-docs-troubleshooting-could-not-be-parsed-as-a-value-list",
-    crumb: "Browser quirks",
+    id: "get-audio-data",
+    title: "getAudioData()",
+    relativePath: "docs/get-audio-data.mdx",
+    compId: "articles-docs-get-audio-data",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "troubleshooting/debug-failed-render",
-    title: "Debugging render failures",
-    relativePath: "docs/troubleshooting/debug-failed-render.mdx",
-    compId: "articles-docs-troubleshooting-debug-failed-render",
-    crumb: "Troubleshooting",
+    id: "clipper",
+    title: "<Experimental.Clipper>",
+    relativePath: "docs/clipper.mdx",
+    compId: "articles-docs-clipper",
+    crumb: "Experimental API",
   },
   {
-    id: "defaultprops-too-big",
-    title: "defaultProps too big - could not serialize",
-    relativePath: "docs/troubleshooting/defaultprops-too-big.mdx",
-    compId: "articles-docs-troubleshooting-defaultprops-too-big",
-    crumb: "Troubleshooting",
+    id: "random",
+    title: "random()",
+    relativePath: "docs/random.mdx",
+    compId: "articles-docs-random",
+    crumb: "API",
   },
   {
-    id: "troubleshooting/delay-render-proxy",
-    title: "Loading <Img> with src http://localhost:3000/proxy",
-    relativePath: "docs/troubleshooting/delay-render-proxy.mdx",
-    compId: "articles-docs-troubleshooting-delay-render-proxy",
-    crumb: "Troubleshooting",
+    id: "remotion",
+    title: "remotion",
+    relativePath: "docs/remotion.mdx",
+    compId: "articles-docs-remotion",
+    crumb: "Core API Reference",
   },
   {
-    id: "loading-root-component",
-    title: "Root component Timeout",
-    relativePath: "docs/troubleshooting/loading-root-component.mdx",
-    compId: "articles-docs-troubleshooting-loading-root-component",
-    crumb: "Troubleshooting",
+    id: "folder",
+    title: "<Folder>",
+    relativePath: "docs/folder.mdx",
+    compId: "articles-docs-folder",
+    crumb: "API",
   },
   {
-    id: "troubleshooting/nextjs-image",
-    title: "Flickering when using Next.js <Image> tag",
-    relativePath: "docs/troubleshooting/nextjs-image.mdx",
-    compId: "articles-docs-troubleshooting-nextjs-image",
-    crumb: "Common mistakes",
+    id: "video-manipulation",
+    title: "Video manipulation",
+    relativePath: "docs/video-manipulation.mdx",
+    compId: "articles-docs-video-manipulation",
+    crumb: "How To",
   },
   {
-    id: "rosetta",
-    title: "Apple Silicon under Rosetta",
-    relativePath: "docs/troubleshooting/rosetta.mdx",
-    compId: "articles-docs-troubleshooting-rosetta",
-    crumb: "Troubleshooting",
+    id: "use-offthread-video-texture",
+    title: "useOffthreadVideoTexture()",
+    relativePath: "docs/use-offthread-video-texture.mdx",
+    compId: "articles-docs-use-offthread-video-texture",
+    crumb: "@remotion/three",
   },
   {
-    id: "troubleshooting/sigkill",
-    title: "Process quit with signal SIGKILL",
-    relativePath: "docs/troubleshooting/sigkill.mdx",
-    compId: "articles-docs-troubleshooting-sigkill",
-    crumb: "Troubleshooting",
+    id: "animation-math",
+    title: "Animation math",
+    relativePath: "docs/animation-math.mdx",
+    compId: "articles-docs-animation-math",
+    crumb: "Techniques",
+  },
+  {
+    id: "render-all",
+    title: "Render all compositions",
+    relativePath: "docs/render-all.mdx",
+    compId: "articles-docs-render-all",
+    crumb: "Techniques",
+  },
+  {
+    id: "layout-utils/index",
+    title: "@remotion/layout-utils",
+    relativePath: "docs/layout-utils/index.mdx",
+    compId: "articles-docs-layout-utils-index",
+    crumb: null,
   },
   {
-    id: "troubleshooting/timed-out-page-function",
-    title: "Timed out evaluating page function",
-    relativePath: "docs/troubleshooting/timed-out-page-function.mdx",
-    compId: "articles-docs-troubleshooting-timed-out-page-function",
-    crumb: "Troubleshooting",
+    id: "layout-utils/best-practices",
+    title: "Best practices for @remotion/layout-utils",
+    relativePath: "docs/layout-utils/best-practices.mdx",
+    compId: "articles-docs-layout-utils-best-practices",
+    crumb: "@remotion/layout-utils",
   },
   {
-    id: "player-flicker",
-    title: "Avoiding flickering in <Player>",
-    relativePath: "docs/troubleshooting/video-flicker.mdx",
-    compId: "articles-docs-troubleshooting-video-flicker",
-    crumb: "Frame-perfection",
+    id: "layout-utils/measure-text",
+    title: "measureText()",
+    relativePath: "docs/layout-utils/measure-text.mdx",
+    compId: "articles-docs-layout-utils-measure-text",
+    crumb: "@remotion/layout-utils",
   },
   {
-    id: "upgrading",
-    title: "Upgrading Remotion",
-    relativePath: "docs/upgrading.mdx",
-    compId: "articles-docs-upgrading",
-    crumb: "upgrade remotion",
+    id: "layout-utils/fill-text-box",
+    title: "fillTextBox()",
+    relativePath: "docs/layout-utils/fill-text-box.mdx",
+    compId: "articles-docs-layout-utils-fill-text-box",
+    crumb: "@remotion/layout-utils",
   },
   {
-    id: "use-audio-data",
-    title: "useAudioData()",
-    relativePath: "docs/use-audio-data.mdx",
-    compId: "articles-docs-use-audio-data",
-    crumb: "@remotion/media-utils",
+    id: "layout-utils/fit-text",
+    title: "fitText()",
+    relativePath: "docs/layout-utils/fit-text.mdx",
+    compId: "articles-docs-layout-utils-fit-text",
+    crumb: "@remotion/layout-utils",
   },
   {
-    id: "use-buffer-state",
-    title: "useBufferState()",
-    relativePath: "docs/use-buffer-state.mdx",
-    compId: "articles-docs-use-buffer-state",
-    crumb: "API",
+    id: "buffer-state",
+    title: "Display a buffer state",
+    relativePath: "docs/buffer-state.mdx",
+    compId: "articles-docs-buffer-state",
+    crumb: "Building video apps",
   },
   {
-    id: "use-current-frame",
-    title: "useCurrentFrame()",
-    relativePath: "docs/use-current-frame.mdx",
-    compId: "articles-docs-use-current-frame",
-    crumb: "API",
+    id: "staticfile-remote-urls",
+    title: "staticFile() does not support remote URLs",
+    relativePath: "docs/static-file-remote-urls.mdx",
+    compId: "articles-docs-static-file-remote-urls",
+    crumb: "Troubleshooting",
   },
   {
-    id: "use-current-scale",
-    title: "useCurrentScale()",
-    relativePath: "docs/use-current-scale.mdx",
-    compId: "articles-docs-use-current-scale",
-    crumb: "API",
+    id: "player/index",
+    title: "@remotion/player",
+    relativePath: "docs/player/index.mdx",
+    compId: "articles-docs-player-index",
+    crumb: null,
   },
   {
-    id: "use-img-and-iframe",
-    title: "<Img>, <Video> and <Audio>",
-    relativePath: "docs/use-img-and-iframe.mdx",
-    compId: "articles-docs-use-img-and-iframe",
-    crumb: "Best practices",
+    id: "integration",
+    title: "Code sharing",
+    relativePath: "docs/player/player-integration.mdx",
+    compId: "articles-docs-player-player-integration",
+    crumb: "@remotion/player",
   },
   {
-    id: "use-offthread-video-texture",
-    title: "useOffthreadVideoTexture()",
-    relativePath: "docs/use-offthread-video-texture.mdx",
-    compId: "articles-docs-use-offthread-video-texture",
-    crumb: "@remotion/three",
+    id: "player/preloading",
+    title: "Preloading assets",
+    relativePath: "docs/player/preloading.mdx",
+    compId: "articles-docs-player-preloading",
+    crumb: "@remotion/player",
   },
   {
-    id: "use-video-config",
-    title: "useVideoConfig()",
-    relativePath: "docs/use-video-config.mdx",
-    compId: "articles-docs-use-video-config",
-    crumb: "API",
+    id: "best-practices",
+    title: "Player - Best practices",
+    relativePath: "docs/player/best-practices.mdx",
+    compId: "articles-docs-player-best-practices",
+    crumb: "@remotion/player",
   },
   {
-    id: "use-video-texture",
-    title: "useVideoTexture()",
-    relativePath: "docs/use-video-texture.mdx",
-    compId: "articles-docs-use-video-texture",
-    crumb: "@remotion/three",
+    id: "autoplay",
+    title: "Combatting autoplay issues",
+    relativePath: "docs/player/autoplay.mdx",
+    compId: "articles-docs-player-autoplay",
+    crumb: "@remotion/player",
   },
   {
-    id: "using-audio",
-    title: "Using audio",
-    relativePath: "docs/using-audio.mdx",
-    compId: "articles-docs-using-audio",
-    crumb: "Techniques",
+    id: "player/premounting",
+    title: "Premounting",
+    relativePath: "docs/player/premounting.mdx",
+    compId: "articles-docs-player-premounting",
+    crumb: "@remotion/player",
   },
   {
-    id: "using-randomness",
-    title: "Using randomness",
-    relativePath: "docs/using-randomness.mdx",
-    compId: "articles-docs-using-randomness",
-    crumb: "Roll the dice",
+    id: "player/api",
+    title: "<Player>",
+    relativePath: "docs/player/api.mdx",
+    compId: "articles-docs-player-api",
+    crumb: "@remotion/player",
   },
   {
-    id: "version-mismatch",
-    title: "Version mismatch",
-    relativePath: "docs/version-mismatch.mdx",
-    compId: "articles-docs-version-mismatch",
-    crumb: "How to fix a",
+    id: "thumbnail",
+    title: "<Thumbnail>",
+    relativePath: "docs/player/thumbnail.mdx",
+    compId: "articles-docs-player-thumbnail",
+    crumb: "@remotion/player",
   },
   {
-    id: "version",
-    title: "VERSION",
-    relativePath: "docs/version.mdx",
-    compId: "articles-docs-version",
-    crumb: "API",
+    id: "current-time",
+    title: "Displaying the current time",
+    relativePath: "docs/player/current-time.mdx",
+    compId: "articles-docs-player-current-time",
+    crumb: "@remotion/player",
   },
   {
-    id: "video-manipulation",
-    title: "Video manipulation",
-    relativePath: "docs/video-manipulation.mdx",
-    compId: "articles-docs-video-manipulation",
-    crumb: "How To",
+    id: "examples",
+    title: "Examples for @remotion/player",
+    relativePath: "docs/player/player-examples.mdx",
+    compId: "articles-docs-player-player-examples",
+    crumb: "@remotion/player",
   },
   {
-    id: "video-uploads",
-    title: "Handling user video uploads",
-    relativePath: "docs/video-uploads.mdx",
-    compId: "articles-docs-video-uploads",
-    crumb: "Building video apps",
+    id: "installation",
+    title: "Installation",
+    relativePath: "docs/player/installation.mdx",
+    compId: "articles-docs-player-installation",
+    crumb: "@remotion/player",
   },
   {
-    id: "video-vs-offthreadvideo",
-    title: "<Video> vs. <OffthreadVideo>",
-    relativePath: "docs/video-vs-offthreadvideo.mdx",
-    compId: "articles-docs-video-vs-offthreadvideo",
-    crumb: "Comparison",
+    id: "buffer-state",
+    title: "The Player buffer state",
+    relativePath: "docs/player/buffer-state.mdx",
+    compId: "articles-docs-player-buffer-state",
+    crumb: "Best practices",
   },
   {
-    id: "video",
-    title: "<Video>",
-    relativePath: "docs/video.mdx",
-    compId: "articles-docs-video",
-    crumb: "API",
+    id: "scaling",
+    title: "Sizing",
+    relativePath: "docs/player/scaling.mdx",
+    compId: "articles-docs-player-scaling",
+    crumb: "@remotion/player",
   },
   {
     id: "visual-editing",
@@ -3549,45 +3563,45 @@ export const articles = [
     crumb: "How To",
   },
   {
-    id: "visualize-audio",
-    title: "visualizeAudio()",
-    relativePath: "docs/visualize-audio.mdx",
-    compId: "articles-docs-visualize-audio",
-    crumb: "@remotion/media-utils",
+    id: "4-0-migration",
+    title: "v4.0 Migration",
+    relativePath: "docs/4-0-migration.mdx",
+    compId: "articles-docs-4-0-migration",
+    crumb: "Version Upgrade",
   },
   {
-    id: "watchstaticfile",
-    title: "watchStaticFile()",
-    relativePath: "docs/watch-static-file.mdx",
-    compId: "articles-docs-watch-static-file",
-    crumb: "API",
+    id: "get-gif-duration-in-seconds",
+    title: "getGifDurationInSeconds()",
+    relativePath: "docs/gif/get-gif-duration-in-seconds.mdx",
+    compId: "articles-docs-gif-get-gif-duration-in-seconds",
+    crumb: "@remotion/gif",
   },
   {
-    id: "wrong-composition-mount",
-    title: "Wrongly mounted <Composition>",
-    relativePath: "docs/wrong-composition-mount.mdx",
-    compId: "articles-docs-wrong-composition-mount",
-    crumb: "Troubleshooting",
+    id: "gif/preload-gif",
+    title: "preloadGif()",
+    relativePath: "docs/gif/preload-gif.mdx",
+    compId: "articles-docs-gif-preload-gif",
+    crumb: "@remotion/gif",
   },
   {
-    id: "zod-types/index",
-    title: "@remotion/zod-types",
-    relativePath: "docs/zod-types/index.mdx",
-    compId: "articles-docs-zod-types-index",
-    crumb: "Schema",
+    id: "gif/index",
+    title: "@remotion/gif",
+    relativePath: "docs/gif/index.mdx",
+    compId: "articles-docs-gif-index",
+    crumb: null,
   },
   {
-    id: "zod-types/z-color",
-    title: "zColor()",
-    relativePath: "docs/zod-types/z-color.mdx",
-    compId: "articles-docs-zod-types-z-color",
-    crumb: null,
+    id: "gif/gif",
+    title: "<Gif>",
+    relativePath: "docs/gif/gif.mdx",
+    compId: "articles-docs-gif-gif",
+    crumb: "@remotion/gif",
   },
   {
-    id: "zod-types/z-textarea",
-    title: "zTextarea()",
-    relativePath: "docs/zod-types/z-textarea.mdx",
-    compId: "articles-docs-zod-types-z-textarea",
-    crumb: null,
+    id: "scaling",
+    title: "Output scaling",
+    relativePath: "docs/scaling.mdx",
+    compId: "articles-docs-scaling",
+    crumb: "How To",
   },
 ];
diff --git a/packages/docs/static/generated/articles-docs-artifact.png b/packages/docs/static/generated/articles-docs-artifact.png
new file mode 100644
index 0000000000000000000000000000000000000000..252834e9a8e518e6f7017fce56038dfe226857c5
GIT binary patch
literal 32218
zcmeFZWmuH$-Zv`4AVUZPD4^6R5+Wg?gv3ayG}4U-NO$KbihzJ3Azjj~bW5j5Bi%@M
z*L%)duJs7-vyWqc*`M}(f8h3DnCm+K{rlJHD<>my1@{K-rAwEtJb5gtaOn~>@6siR
z0}KLwQ*&z>5Bv|qRzX7eQqB*`g-e$Zm!60|Qgp&t9(51t?VUWcK242|b#dW8i7l#*
zeQP8t7waHT^hhjC&Q$5@Qv)Kg>|3ol3h91fK06rD^jEJ4<<=9@(!A6$uF0WZc{L}!
zLNGyIy=oWi{4tX%&i3o}r;!M0!{CuUYT2lk+uVCq7KRprw?A=p9f(}QfWndg{UN6i
z?;D(*(gj0!U4s124?fg~nE(FGzy4ZI(F>XOvZ=@H?{6c$kj;YUz6fJvLLw9kri~x`
z^)wg)iFiTtKQBf9>*_TGoM~u-i{jjI5b&>r=TG8?3x{1>af%H1`*<)o>=*W5RDcJH
zVZprp9QZrL&m9})y^Q%kRDpkaB|@OWVh>{tY0n)SD!hB{97v=*1|%VoO7<Jkxno0a
z9-TV}B0lXBO0Fv~_1C$#Tta<tKX(rJKkP$`>mT<0qkWKnjQ1bk2ZG2yq2r&>0b=k!
zk@p|q0)_vB`~HE=0Qdg`oBzMbI+jatN9=l}=Lb&(_a`vJcP)mCvK`mPib7@D$@%Pl
zyYGz_J8ev1ViOFuJ)!?}a=1exspB;HX4l~J>;I-Za2X7U9?#Jm^_w63Yr=Ire$_AJ
z78LX*mlThC3>(wUJ3Ee8ONfcRDJ>WpQr}AKh`i5P-NyCbED-q__&L2*s>QcR2MD3i
zaUO4!@4_cpLEZ={L#Df#$iXj9K8;nnswgS>cEh}1y~6oVo{6x5Kx>=ruU`{KBq0ZK
zw8UTqQp_4f%snQ*F4xjaM~=8{btG~@65@3s|4HHAMPEYQv(XZNLu5rdn6K|c2aVJT
z3kl)LO<CGSI(=Q})O_cB_%lI790jeF%4?VSPpVeShD6|d(#VTIzd%<@=c#RVDemcd
z9T<F~mu9X$A$g+?nKTI3q95zFuS`iX{PopfK+`rLx!_=Hp2D99)}bl&-=wmO7y%d0
ztWINr{D3Uj9dcJ61n_>V-1_lQz-9Yhr&^vaYPxk5C6{H>B}Nt@Ng?D>u|bNGgR`Ul
z!){R-FC=*>Yf>$Y2bQJwtueY^M(|+f@bFNA+PR+Jw#EyCz;uZ9dSUd<tC=C;;Zc(1
z8;ukJ#ej3~{uj>GJkp(M{y10gz-9ZT*1+D{xMl1w!0+6u!`7;=GuLbJ*eZT%4)vx#
z3rK`{J1ETkHwBj`0(2y&rZGUSW0{zmM%_0xlrdea+>6aGn9b;tpmFcj_H#vKWo2o8
zebJ(GcBo=|ARWo2R^?VIe--sprj_!)Qe*;BOxwP?BZ^3>38s^5H(r_fBrB7w(Adps
z*nY2CNmJ8|t$`R8#bw;xzHOvE%FiP6fEc#&rTx)=q?33HiFp51R$dm$fs0SgKS<fv
z`2IRAX`#;jGS@wejU0A8Lhre5%2XP+^+)nBl$_kX|Am`ea2|){&vf4cs2)1_E_B-5
zU#Rybp~$i<Pk*1xb<_qlaA07-U-<t)1LILO)n_M`u7<V|JsC=wmzRu4V1OsPxt=GP
zIXOQxA*i2O-~X%ga28-O1&97amv8;F6TTIH21&Q56Ydl`a|_>}-|s(KtF~mVvuSzx
z&9LK#@jkcH`h@4{R$QmR-pFK~PsiOGJqLu|81|$Mx6q{uMn+wlfzPkKOaNl)bBW&N
zgF(4ZdCl4f)O6f7-ssQR#;K{Ro8-KqgB!XKBsgE&;zHdkIq?bpSN-A@>Aj$rQnc|Y
zpjgr2k=l0!`ijErh8evV*|Ss``%aI*wOF^!mX;R^iatp45dvh(zp5EG$>2#hNnnh3
zK}Dx%$#@?JHPM(sG7K}!5b+_HYjzy9AIP>p)BwVE&Ig7Fj8dxi-T#a4>zcHMv4njt
z)~kCfHM}rdUhF&>988>l8yuL*>)!_!W<`jL(8J}FLD9)<pl6)~@;yL_p@^uaH^y<y
z_=i*VL5F4cf?di=d*uDL3M*isOn2`FyutDQzH%w#J=%!iHYllS`Yj?k3GfIRou2OZ
z=to@V{`H$%A-&hqV~fJJk>a64mSj3M0p&fm5nv8jc%<ZQB(T@R7>w7^t^_dyVHIJp
zycz!vu<c`DN`wXa5f&QeBd(ds*<Ydj;Y(rx(rP(3sDPJXk2^>C%aNDuL$LZsz>0me
zzg!8p3bV*EdC{P$Ov7pTo)=!G!)6kqUu{y5k@0k3$8pTLNmH>XC?+j4lQVZLON}$!
z<Y-gb>O}0{g2=$x=eQ>igM1_f)>w9wRv`rWe#PUoH%Q?IE_6WW-vq};BI}6Ws=(41
zfw=MqjK7H?O1^h~y{Ub83$E!Y&w=*DKzpk2rLMk0egV$-<EkLjS`gCeb)3*nQpj7#
z_Z>32e>t!;`kz+blh+_jp!BA+Y(Hd11hZKGX7(<;3ij9Ayx1^F-XP${K*zt72els2
z!bc|NWltEB6Oex$oQqP=>5Z2kP}*y*F%L9=-W@YjOeM4Z{kDm=8qu3DQFXwph1I0Z
zQ=r-V2hW6_8Z-WV@GBDJ+Bf49fhZcZQ#(je6PJbB-a0M#|NXXq*aZ3i6`K?(j$g9<
zff(eFFcFCMt@wDLA>U<ETa+;_i~Wf{a?fg9q-so|y(9>D#=v~RAbtEC{Bh==(r%4|
zTn#=n1boIcFv~T_T4VL@PZT!69Sp)f=X29|q1`5bo@&LztV;^<!vXc_C9BA#+kbzL
zhl=bTAx0q-=54aWoo#<r&KCBi?@x(%!b!JA3K{|s0#n~8SHGIqB>WF>>Ah+t$f3&$
zVFJ%oS~MSue1z<{`9b*)szAOK!8D|e{{ZMq_>qzx0VOB+Ljh76CDnAfT>?o3;3J>A
zv?M3)-v@1WvZ|qgG13CRkdkTq(h%dV$bT7w*y_SqL&W=mhla+c#IQn42v7w#{+vgX
zfY1pKLkv*iXUElHN21Fk`X?CQ#~KMFbRxvKkEU>&Gu!UkISO68YNB9_H^B=Nql~|l
zU#h$LM@VURCOo`r%3*1sBcPZNZk*)f6)A$q{!h_9NCY}2j>G^|Kw_jwD*vVsBUc|f
zCV@BwFk*j?on`wxOPxQ|Kw$OSz=IsIrl7_k_&$G_9iQU0Cl8TCz#V~~8gc!+`vmqk
zYd-Ku=@LPovjSw{jNQY3{cqMF=Xg*C@OQ-kOOBmLE`5RaQbLUH{~_ct6=NP8k`Ekm
zW0|XQboAdOA{@WeD8TB~z-i{klFft9@$@#~HC22RIUXFQKT^sit^Bw2KL{x6@<OLT
zynSyu-pyf*ao-22{-4H2H#Q#gJER$K-1#f&H|W{^R<~vvmb}{#YtT|@EHpA!AwBoT
zuRNN%^3dlX!F+Gr)SYGaH&;<+fn=(rC@0|Qr+?)ZFx@@(N;Rr`dG{dsSHWYyY8Wdz
z{LNORg9JWv0Fn}%$9h?5C?xrBL86!mud7m`$iZXv2h+LkX#CAr#IXM5FbRYQT9P|=
zSX$N3DS1Ekl`JphGSI91C50Oym47q#!-+hmDFV)o7Ntwb?hN8@Ge-E-T~>aCFa(eN
zY|q%yBK9|Dn`cW<_V9P%K$hAkPfTry|5n!zJt)k(H{o}|W3$RWHO|o2SbfGu(b3Rx
z(@3?P6)zwc!}sVn<?`QIOcg&P?zDJPcE+u*HQGtPue<NRS?_F{X`!RMwH!86#`faj
zMqkhpt%+nI5fXU^H4;dviihF><~-dSRX`_!kt|;IimPZVDU^4+BgV3i^h?pl!L-{8
zJN~9OdZH<GvU#<mL<UYup6Wm7C<7Xd^nlu&1?o#fr_k6}Qnou#95!RrEbn|5uZZ6E
zcU+}J4^vWUu@(n>Y+F4Lm3Oq<3Vy}A9qD{+kA9sM=qmg&##S+D4=n8tFt~5y#`c(~
zd-#txazw<mFs%;As^ip&EF+>d$X+5S_Z(<t@FHEf=lk<-fp31`#^?zr{q*04UXdEY
zGAS^U;>NmNsM^EKh3iX;y`o!Y_ExC8oozowec3CggDtVyx@g}KKLJq-c*^`9$&6Ol
zW#t>8G05){Nlq#g*_AuLc6^K%M6PvJnQW8I`VI)@=o~q>&Q`lB&ul(xnSf00*4=qV
z$YsJY_Rv9Joopnq>$7@3@JqN{i>860MDN}J8Iq1ZroF)cr2jci`OHi>F~|BK5is>2
z-2|?2{nVy#zN&Q9Uy&e+fDt~n=@MY{W|piuLCY_TcqA|ZhUgHjk-#`u;|dc+<N$3c
z*=1g76|QNrev`-yp(S-@xV4w#;-S&4VCb!V*>QwM4#EF<eZi!QhVQ<xK<IqN<zoGr
z(wk-kC~{ay(4z`}FI+Ubafx`~0;|{M$1^8+Kw~y?@JK5+tw&^-uhS0;JVO-0pUywI
zA>XoTE=yxT2D50A3k!Km`{5fhOvj8l5Qz`?#J;Q;8Iq0skp#P2h8OJ?Co~^**qLiH
zF86Z$%4&(wJ0y?QBYoL<bwQVemGD<8%q3Jw6Vgc#n5az6W<!G{^eV7ZrojcSUrg}Q
zuDeXvs)kcj$tntq1(~FadO1s`PZ5wwb4deF<mdjKR=$%ZrsA_i4-tf<%Ha`5302*=
zi^2^E6HGM-$0@+e1(39J7)?41RU}`>?QWC}I@o$$P!@9f4afHtSHedMaH6xBBVx<t
z-4f4*q0VRX9UW6Mi@kjco&Ij_Dkr(CK`$J+zH+;33V=s9Jyp00@q-N#;C@DVfplJY
zwk-PcFxJ)UTbm>8q=ZU?=_|HjZXpGo1)Zga^7gSyYi)Ch_9JXt{4PI^?v&{7cfY`W
zgeZXQ68Z4L(&&LEXEzz=zDB;3NPn!doWGYW!+qlL^p^b6&pTmiG?dM=zwoiJ-zy{B
z++I6|R~}j`vW>Is|Ip1r7C7MBc0`B+-xD_ZHXP(s;8A%!#$S~X9(5Yi8jAeoHZ2k@
zlWg-I+N+O4_jVFpgFMbg4#FLr;%-YV9X^@uDwkqcZ#v;Xt#4eh4;U|Jy!o4I8fY<=
z#wRkNs0OS$J6uRB?nNGkP+_fHIh(EY_02SCsQYc0)wc1w`e^wn#kb8?dXxXm0@S_V
zR4SBaxa}f%r*J*S(!JWM;1ut$WLzfza=CU~Hk-#$W_A`Y!%|hjwkfL6u}bOTb9wu-
z10^aW#bY-G?T05{Hbv(p?qaj1Rhb*3y$gYB`e7CWllxi6i=Pi08#<M*#}!W(9!^s%
zdhF@GsV>J&z6oc*@cHhm24h4sHLK1z=Mysea#8d!$HYR${kT+xE1iq8lrRIC!-cZb
zr4}LTC7SRO6T42rR1#YY-MwF-tY;_HcYa8;SG(rbTC;pqh?JJ%VbyJ?tnMz?8zxNE
zeEM6kd~-V@G%#DxJw?O6tTX5MMA0;<s{C3&Taos!1^0~I(3mIT#P5qKO-<vR7d$h<
zR&&mdhwWkpzA+IheIfG<q1=4FI-j|s!QE+mAlHDo*1yXCeDthioYYuLNIjcvB$i)i
zRZswHggsK`@jE1AWZq6@Z(lAH>t*AM8>nWScR@JkQrPAIZ9(OIM?&Rxm+IAF{2+>E
zUbwQ$8M*ZMNypxR|BW-2kyR=&JFQ{{O8Hku^(}kO{5B`)(hNV|#6C>je2tktU06m(
z9XF_N=iZPibQ+SAJW^@fzBG4FV42QG<Up#DzEw75+mM7JTAL!jd7j~lpLqmB>~OH%
zLZFW9>FKRWlXL>^@WYes1V1}pk~eL0BjY!A6?PK*-k#w;A5E!STKsa_9XQjncR#Pt
zEY0iTTYUsqq<l<o+;|xOtlm=fRaK@=Nq`=$_CIjqVKktznLSU3J+Yk03`s!cVd!GE
zj&pQm3k&@8pp=G9#kN+EeJ%DH11pAr#ww`T*i?Eealm5_A2$TnQPYcKI=G+cM|N~t
zpViakWM6YPl5)B%Ji1%gb_}g8j5Dgh{8_oyOP}l|yUehge$v!4YV<Jfej&WH;&_@<
zDb6#>{wkMBN|AQ8xv_ML^i``SD;)2{(>Q9$@uGsp%{8oup;|ZS6bnL-ObQX71u*8X
zoYYAM=#)UOn!4j6?KL(t$^PEBmsM?w7AUJe!L{z5GK&MPuy&;+UZE`{J^Zx#&NYs`
zhxbch-mK*T`UtZJ>>HEc#f`ql6{&}J`%`?x(Fu7-d(zWCa_TvrZ&&Wg8*PQ>3>X$7
zwIJy@5O?6D-%~g%2~1s7vSx+khbt@%4N8;ZjFU5w1#fdkOpdmm_~y5n?iw6?*{6i)
zee=bMc!s1ovwJJeC9z3fxs_B(S>bZI);a3alrmq=!?qwxVwhIQGHQMP6LER9aqj}p
z(VWTBPEL?PC1<o^>^eWY#dz3Jv>%^VlwpVWN0@h_mJd_+Y^I5)bMCNM-c(X66v9pd
zr-7a;Zm?4fGj}W;yqJx0T{I9L!uc4NVgvK$u8N%8dnXiXe0)Y_8C#;=o3^ed`*_sU
z?z?jFc&geEt2Q_prtk6;E-FvY^MR8Ap|}5S3zeRDSSi^x`@>F;!|NHpO~ZZ9ToE_7
zxUQB4aH}r9EIb{)bER9c0Fu!4Sy5eQ%Q@rBjas=Y{;t+8<Cvch@--nR8(dBS6ir?f
z_xyef*&y#`&ff1k@lDTMB2Y05x4lvOn-^Nk;#0wfC$&^IT?&iH6elSxOHSrs6TnPJ
zo{VL~@n#hbL-I@*r;mq}UH!eOtV~Gz@felXHm5nprx`)I7i-RUlc)j7sP0&x?zwzY
zCRIEZdQhK_4<RCky9%B&S6F}shZ0pu{Nc$7D|a)0msMxF8Rlf}YB*n4(RALHw1oB`
zy*=<u?QMtnmr*|lZE<a$MN~>q?Bo)!x-Jyo|9Xa8uNU{#7%#JIU((u=4!T7J3wWDi
z@7|Ws8;VD@YIS!@teyiVDps@fspAM%uLD5)nPAGBScv@ff{~?PQwe^ao*%N-7vcwa
zNS6;@I8=8r>CB!C3TO>9y(r<HjtG*}x_>wqOG9%oLGauo9QpF)j1au&Naf8ucHT$4
zW`6Cp?lY)Um_lzgtNm23qaNnuTB>r^K35Z7gP3i-s6ou>a@Y%vRM+{>LIzT_aCwaU
zOiq;7R}dr(%46Tg!QcHRH&i?_A|d9kQs3GbsEYCC{qR_5-6DIIG(K3AV{b!tIQES$
ztL9ur*U;gz2XDWf@GIm?#Jmvv^awNkTUA*OP5RU7{Bn;UDP?MF11txOcRZcvw}vY`
zl@DjF#|04#RXoutbLOY{9B;08i#u{heI`E{j|>WQ(rH-fz3Ss!Ui~EK)KXfxdYH?z
z48+c82*DBGH@KV>P!Uz19!&<66@h$g;o_m{Y;aI;;aHH9mB}}AZ9rIU(F=>cb*_vd
z<yFm}VIn_(cYVXp=Gd3k{^Ta7?!RzjO{dpF8TIHqd^O}TZA-{l=K{yXGn(`drEIH>
zY@@qrDI~<<Y-vR+YZjc{WgaCQl_okT;4K;i$ul(KiAydVJJWPo!1uSiR<WOyW8XE*
z%G_Fd%V&S*q%dg7-_XGEz^hWZ{So3e#6kzV!pG|d;OqUv2<0+d?-x?RS&dI-eO;A%
z5A7RiL;D9jVsDhx_{8A|6h2wB>HEsfA=p;F;O#x5loUna^x_hpVG+t|Jb$fIS=u9a
zx2&O$mX!C{@q4xBG1W9%D@h<8Ip@5`gt<+Hvg4iaPHh`c9C`UapYA=ot68*@Q;&ID
zh%eX%{efF@e*69Q@Kc8AAYo6T!o(j-$Nk?$hq_0@!sDI@nbT!2f!J;;gy}Gyd0!$~
zWL8bqEiMGNL|--1(!+UfIyBcqb8Vpd7S-b~O}SYD>Cvt>x8UX&1DMb;Q6vsXory=K
zXh*Tyv8D84PfvRN9QXQ;Y|F{D#$CDG0dA;XU(;Qs@THwoRNos(l=|@`c_)iNWENA;
zAZB%1i|cLma=_PB|K-~3K{12)uL>>zLb<uSiNvINwR;;yXJ&1c+-;9?<(GLi&|4lV
z$FH&IlU|mi(!Wp{+v?Q61fbA~%|Oi9QQ*!;CWkbnBmU9cvhCYy&6DpKo>h_x#_oMp
zhmD$=y1CT0@a9ynBO=J!t@u|UGQ81F3801?P&(cCmhB(6GucQ!-v3+HJj3+h!w&CI
zK)PxVj&H2&Qz{2b!QWIKq-LRqeGw;5-m*%6!1o47V24P^03xs&B#N+GxY|;jlDqRR
zJ-t8!5nVc_Z$CYpkfMFI^CIXQWp$a5JWIZ1&T0EmiUewvJUFk>(sa$eDX#6x*Gk%a
zH##+9R6G_x0g)301{MgexON=U+r%)Jyp7D`MrbWSz;m&OTl0n$4{|?-YHoh4|M)d$
zd^>V@CsCZLtSzSd*GWLrn^$wBPcGMz6}yhd)@x?X+r>AX_(5ysbNfm>UyWi;vX&mP
zNPBc16=?V1;6yAX3b5&+9pdl6$x@9Wvfn&OQ>82cWfJVDpE<43X|NK{I*`gSfw3v2
z-v&d!Z^A<#-St6(cq*S~+?AyJ0T`zItV>7vc-rGi)%!_b<~^yYc6)}aUFCMoVqYM(
zPreo-gO2n`U>bsoT}$b$k2+O?T(S*oTZfv)a$P+%Hai4%miAHw`GPl(H)16b$G^P2
z+YLVUJ*oU98r1W;vAEtc)$I&Vd+)5Q^M})D`hEtET@zfRMQ^IJQz`qDIee)b$D8-%
zx2KPx9yb*BJ)ymz4v{guSD*<4Mz)zHu8dlj6LT4gEE|{k$k)CzQg8%$9lLj$9Q||G
zdTR2l<uq2B1760>>EnnABxr#+-kGWYoP6I@NLJ~o){lk!wi0Nq?fhe{7JjkSZC!$g
z$P^{WC_9I_qQ@z)yJEJ?U$$KSM)q*ntg{-28H8Ggy&}L10xTViFM{3$$J^YhRgtCz
z&9O<gBddX*Qhj-yI$Na!0f@>nMDZS>QnU7bCK6cYCkr95Z^i0{GC*D*@w}^Jqn!YE
z<itqXMGVo%W`H^PFcmk#)`eq~rCKWf;aj)+{NemdzE4|HE#NQ#b~TWqaEx;@zeZMo
zbZ%o+N1cHVJ|y3&z}|v<S-@*)1oISpvY5f29^343v&Em^05aKS0!i4zDK}f=VA`Gh
z&Za1-trESWJwXgxQ<J~@EOKTiF_9%84(5!Q!PlEE|4e-AHe!qAI|r;@1H`Gd(`0)E
z5T}a2;(Cj>ZLL$(e&yz0J-FG2UCO#WLYh8Y1BV$nY$B*1cG(F}0l<>G7JL+7EK`n>
zM1&a2XD>aw`(ngKmA(M=VH`g9Gb)SN9u-~Xq>6)51r-bZz8EfgZd4k+Aum)IgLp(s
z+>!cDJJ9nU;zEVg9hf)A!%``?)?NI`P6oO_#RIp1w86{(oNy$g?Uu#?UXTGkT{w;g
zVum#-n5yY{{?$CU<aJ^UEWjCD2R`W{Tu}pWQz^t%^W8mO@9LOf*siWl@53$8YUi=e
zZUnq_dFBzc_8=-bRwB66wQ}4V#~b9Df}H6#2>ydAXy?t4gO_<y6<8J32C5KL2`Hyc
zkJS~(3L}_AMspb$NG)U!cSN#4F~qS#?Z|&qeDtl|rmI4ASMYD|5i)1npgr3f95e-M
zs60?OaNgg8gd_KtN!NHX=ID~@baGB7yxM7bzX*QTsD8TR)_tT$!SV2B8{&f}MtWuR
z8~D3IP=)9pm7Jb|ZVDZ9+4v8z<m@g9cK}3n<ZLqWigN9#OZ++m%sv9s1(MLtI`4{T
zUo6G4p;7ip)`U#bE>&25N?Q6#cr%1OL^DM${C(_!(`+Wqz<^5}x4l(S=V6g}TxEjH
zsdaoX5A$1OEY|YT16#P9G>EzCK|R{oDE=S;4|AC)Wp<-yF*DMWRFR`r%Kr+vUuL8q
z2GB<zJ{GIvz};=#VyA!#M^ye8M=~DZBe!Ipg?9%HEb36pTAnZ!PW2pu#I>itH+#RA
zwhCBT`FI%LYxlY71HtmpdF1|o0%}uqdO_9PR<BoTC0k$EuO-#Xs8@KOtKWPUM@oZ$
z6L}(&Nddd+#%1yX>oRNF*nYC5iYEw9G)~34DtoRvPx;vP<9zP_PHks<;1OZNeH&hO
zM80-N0i&Zih+jG^V^ct5o3j)}9_7tl1M1>@dmQhRc@={(Q<(}*d;6H_6e=SE&r+BA
zTg7KDVog5BAqUN#Ki~E=72I4alEo%j0C{tD#lTV9_m6=QFz;s*^^O?NU}@-yn!<VG
z;wOk`5yhgy!)k2ep8kCYVnp_m27&W2>*Klazsqvg4*4n%bU`gdke|^WuSZc`sAZ?=
z1K`<c3*ecpPp6{#*)%6LLnO#nHy$Fx(eJL^e$>gM_AK|zEm*PnV`=^;5YHcBqjPlC
z4P$qO#hy<e#VX{(yLHBu*huUQ95}-sA<Q7pRg54Q20;H?9R#ij-Gmo?Pye{XYiZHv
zC%fd%tIwma)yT;(-x12U7c8|P6os%)i%IVb9w-ga(*Bh04Umu?fj2mK^jLc@YXovN
z_7e-;5d}=>bm-_U2O2S?zdo5AOQN0yas+=ot{AufrCdrN*ja^JWnt8vdLVZZjiAi6
z8?4po;d2%GTrnR$;N-C0n93d<{~~c#obr)IPXfd67MvO*zwR6cg>~l5=u3OZ$CB?k
ztej3$WA`vzjZOi2tl&#=xOBKzec$9o(wqmvUgyK;cnQ@TA6|It47Nnw2kcs0Uym(*
zw!Jo2lGxPJ5zNU=*$Ud|oF)4^l??z8X5k*6ZY9U2?XL>14Ue@#Yoi_CH)7X<>Qn^b
zr|eW$FW7Z@_`3qHp5IOU&|c6n$><v|=$A)y(?%H0J`_?f-A@7#^O_xwcQ6)#>z$^f
za6g9_!EfF91IwIRs}AII$h4)y2{IdbTxY~KzD-$<>#5?-$;p_CKpA$e$JQ4a191A{
z%#$tK>$y4t?uG_2j#^dYcbh6#+=rh1i%Bz-u%5Eh@2K7nWVXL38nDA~ptl%wX{66}
zg+wj3TN7%9qCS)?6yAu)+;2xlKqf=+l5^@zkAcIKq|3b#Q}tjPE9VWacb#_dt8Pyo
zcwF_tQIyHh8AWwBudX*goI1SZ`#cE+r-ND#&iEO9e+9Q;q>QTo_pQu_8$ZV6_Kwo8
z{CIz|cv_D)92}fFB;fhOWVssv7?6wQW^ON-E_n!)%BwieuUFPH!Mq<KdK~f2HNW-9
zKWr-dgb^yWk4J};M#~QDNTIAM>zyfda>(wh-p;)tp!jhkqe~d87DKizHnz3I@pQeB
z%98IzEs)~q_+x`Kh6nvIZgKa0@HORP`e<4AsHNFX8G!_5?mqk!#<Mrd_SmRdAmHZ+
z*8QEsCY{t`%BSvH$&&Y{Q$tnU7Tqgf0ssf_-~saU)1|wD*+P~Uja!M%N7~OtU{~cP
zQiIseg1pK=(6&E{4Z}l00!6S!wkOCkgm@;bm##M<ofMpF^$X)(P<U3RP11Vy{a)aV
zp<z@>L#lE~?z#-bwMU<MPG$VYsx97odg27gQ&b;6kv^>KTpS-&sa{hpQ&_Bs=M2eN
z>a>(p7gQFC3rX88d1l<${ncG$Q#l%N5)}2eEPH?@9pMeQZmE+y`yw^5zMg#k#t+#5
z&B4Z8S!4Nrh&9#Bi=myR7GH_}K7%rnBz?rT8*6kIhKClLNrCWYMwd~sYMocUO9Et)
zZ7tMg88y#J(SYP}C~exp6wrtnhOk*U4q|UUi+Cdw<#0@XxlA9}>yXPyXiUqGAilG5
zUwx$rU*s~D%~6IP3v}u>sQW*&><**EdM-57!j(5(k>R+fMx#7Q4s(3MFH_S5Mi5#h
zweLr!^-)o1q#+0GgoWp7Wjd+2{ZM(o>b+-TeE6f{`XHb=u;5+qCu`8DYFNmdZ)Fe@
z+lNy;;eg+SESa`iENd8HX>_svIz52JsYC*gdw#Awk#WU?bwwbZpWVYCd;cIz0Dijq
z-H*g7=NX#lUggt_F1*u~_19^jPN^cad(%gh9sU3~m-ibvNAmrahPd9Ks#!!CTrT^v
zfB5(`W5CSQ7u5BEJ8LrCNqx{~+;d}uzG;@is~9dPhd7euKi}h-*q{G3GxaMJFwP17
zYr9ZEXnk|fMLwx0SS6sUv}rPj8Vle1Y!4~CX^q8NZXt>}c_#4TLxuZC$>DGAZFKNc
zH-+cp`B|$(w1pc5IXYE{rB9aMSIEJc$^?~5K@9Ydv)^wb>g*K6L&QgV_0FG<@1Vzg
zqi0OynnF`gfG2fjC9u9IlO?L08!WmAGrpVFd&9)Cx>lnqv^QW;GLP*%6n8h=b1tb>
zED*!0%cp<0?9maZJYA^^j*?2=$<%4#yK<x45&7`#$+tQ%9A!oH;W+ObIfNbENG!26
zj?D6E<N2;(i24!KI>y;E4y4Q1taIOF+(i#l4;avj4~2dHb34q0jdxfr!`)?W&w+NI
zH5q-0ot&gbzyEmkJ8sMP_p;t5rzJVDvg!BYS^?MV1NBbZIY1*Z77`n0M~X!3g0{z?
zBm0$Y(sfD})f%{yg@13y(Q9;x$qS^r6xC8{WmyydQW?;idJ)^VT2d{}hZgF~139rD
z)#H*&ha-d@ZXPFNNn;Fq{Fn*(mLpYJDeU2!^v@0}I$8`#+)JMLu04EfFh5TRftBF#
zcr0;{U?hW#>Zv_<fIX=d4U>}!>~X9L&J++O0T3m1@u=zc7n`*vA<Arrl77X4(Qw~5
z>Bmy4YUP_fhaf~%XCIu(+#Wla^KtKIhqr20xhgy$jC6H-y+<etJodV%=#%IrvH?n|
z$3^>1L5tYf3g%~h6IVw-DuFX_*NP4^!TR8L!F_v)%$RCYj*jf?fw)0c$4z%Z(MOYW
zYk@cY48Q>43n+*CU__7?AKDWgMur(){c!!B>SYY58_~-Rxd!m{qrha-#h5KC^UBlF
zg?ektfm4^SaS3h7o0zdVk;S~FM^Sbhb#E$LLQW5g7eL)Ga4*mPPDC*nCpv~)CnRvf
z#4rF{Xtle4rGpuMI_Y=g7F-3TGaARU<l73geEvuJvFEcJ(fG`dKSfDP6~t;=kBwxY
zKj_GwLS4aHz<S_^6vX17p;{+^WT2D61>>QGTOT@6KIS+7z0-ztf?n+0g9t7v{bykg
z5eKnoI$sQ#v7OM<_|q{5;s0VSp_Y?bp5)KS!2h1K50d`ri+fAdOv6#dL~>xiBMr&W
zBg%$a$CvDd{+TG)yfb0{zUEw=4W#^ke|rmo!h*l61n_!SOA50R>%zM)HuEo>+_w%&
z`Uo+H?g2aA>-a=;fo_f_oO9XP1+IM|xT*?6siJ%Hqm^7TG6?=U;`@K*4^eKo%~W^t
zu0pKQrdsM~9Q6e$h)rGe=FhCiB+&<cMO}92b7tVNt>?SB)GiQ?aLuZ*fz_*k#BIDP
z*`OO;>-dXs<e@GKi|Ac8AiPLRvTo)g;m2JNNuVj9N3H1^=3sF1!qNY)CjWW9As;m1
zG5aI=(Rt~zih5}1g|jy=dZFGBy-Nmf4;y`&^p)fS<9<=G=W#)-(USoAD#p*kE*u`w
zCUTin0`V5`o!o&*x>flC=U%wB*KES-WdZHgg2`fPc<052$|Ag>ih&wLXPpDdT#T+4
zSoeSR{Vx!2!(*m<4f-7Pu->crbVqAKFA&e#bR`QU<-qJV7G>2xeY!~eLJcYu{w@>{
zuRQuh-{vCmrBu9mq!4QWKO57zjU@vw65k|(tt^5NM-y*PFWsVaf%qAh<r>f$26Kqy
zaaCXFcrOwkO-QXugc<~<tltyLb=&bG@kd>lrnlf^Xvw<-J8%gj;xGw^h5tQ>gIb4i
zO|7Gph~D{v)6-Ku;k-c$kJa;y{4>D|JjD&$<C-#9y$E=HkjIy60mRMoHFZV*@df;l
z06s5W1L$+m7kMwn-Tkf+$J<CXk^YY}_#lgFq<WP10AdYRG#V4gj74a%HhI_cSpIke
zSP+ty@TMvi=!Jvh|McZb$cIc`4@<-O;|^w?#pgG{vx1P@K=ZoRMAU!2M7a_0o8kTO
z2eq_6f4_#|A`zQ_d(DnFue_{4I=!VcdG*Jt4z9jDmWR?I;P!z0Ue^zYM~DlucjI{<
z4};!9Pw+TlE4T|?ki8d86Hy2XS%7~1s*uT={{`8v&mvz5gA@aBuX|Kt_T-|+IHi0d
zhYOJhHZNYfmsT!&LH0GNA~=4J5Vhd+Fh^>oJ;@8Q-?wLe#0wKe8~tci(p(kJdBw*V
zC!lA9p_;**4DGPJ;)u`%*-wmjLFsP7l>qUIu7NH`j2DULkB(=A-U6BK`<G4f=0X>p
zMqCp~$QvXVn)qdIla<Vi#P4{$s)fJ%0*DVD6;D37cERdTspaKvK;(hdi{}rdee1kP
z{JaRRp9ta=ns|pBO1l~tiFcQJ<O~x<TYaTqQe)Kx#m5*YWMYJa7A+utz(8@(^CIzQ
z@>q1Y;P=szcX@w!`J&_@JX!INp|{Xhuj(T|int(oB)sWbh%d4LSbf(bn+e6c3&g`_
zYF-Hwy$b=vD~yU6yuK*;@04nCOb~fs_2TKpX{N{v#3SNG@chIO!f4{{NtLcVy+AzF
z-t18+OcZVPF;$5vs0)%u)ex{UlB1e|+0gbVDRS|k)@xaRj{Ij9;9SUrk91+t(Za8x
zwZO&C<uYUZhR+}Sc!~>ka(dJS6BPyT`52fO`3RSh7t;Omk9q!l#a|U~22Dg8x44je
z8xo70@PB3ok%adcDN&EnN9dAP<WM~~`}Lh#PmT{F56oUXAv{gL=G^R8jSu<NAS4v9
zmgx8G4Dy0=vtRK+?2I(1br41C#wB|1S)H5xT7QlIzzg{sXk^0LEhh4cbKjf@;uwTL
z^vDqKx0OC=I@IST)%Yn5^KhWTz|irXvE?h>&du>PH(6gEMj}RlG_3Xr3)}q9jg5t4
zUU~2enTCGpDm7C$%{f93c)NA)!o1PbdUIn57S6<fPdRSdrF}Pq!}5WW)W?YQZPJ~a
zIMZpmVTO%b2i99R%-zdud5%Y81P=z`kOWW*e_Ldlrh0mAd~jy%<!hFkaAhD;RoAyJ
zv-0PL)utyeM+qao0AfBk;P<tro@0_5wIy)BwP2nL->6zHvjX=X<*k3?KCeiKv%rL|
zKFk}mZ^>IN5)=;3jb(lqrzI$QizEkp&|AK?7-DozGMzq@V5SKUh!GjQqj+ch+zjES
zd;9lYI4lnE!OdMbVDsFN?9560>npe5e1MqWW!|{6I_C!ZPJ{RcZ$b1x2K_c$B25<K
zoKPW@4tY<Y!oY^{)qLb)gwF|;vECYcSQMd&7U~Xm;hlGX3svp|CLut=X+Q@P773W1
zk)IRl8{SRbOqll*z=xEvcvh@)LdCWF{oRlV_7M;x8uLgA_nc7U<(jC?$Us~LLhT{z
z{i6GCp@K;^^<s<BeElX?t+J~p>jT*u#Xn*mIxY|9yC3bE4;2`Lbs%O9=_Gjb(|BEM
z8Z&a#Pw)tlo@<I<tiUiFB{O;}!CNQZpe-zh&z@=baA~N}v?-8kxX4_R2j4SJq0S46
z>$v?rj0olr4#q6-NC1}+{*zoJAB1A$Bj`|(`BEs9YK)DEiEpR(*^{?e3mgyBkthhY
zN1icM7|aLaKar3N5XQY~Mfto3e8;OAQkwk87Rk)Sv<P;cw&prcvdO{3j|@tf+B44_
zS__;4zJ!pR|0`T?+joh|=+_iMI-<DO`m>{WM`!u96cu}uCBq+>553U91_PVw5G;X9
zdebm(VZc>Ci+JKU*WUI26f*}71tkSZjq`B%fpf3NPpjNP0349h{Nn*kR4YDD0%1ml
zx`$@xPrTlQ8i+hidzuh{2fDD5XlnlH_nepU$*pEXRR|~^WPSPa;%EFLGHoZl&RBtR
z$F<lyxRLV|`5%@Z|LU1yked*JKL|p?fXHwDhGxjUMD1U(EP#Q#M6UA8hCFL0`oZts
zLKpu2#{G=;1<T$v`MS4PD0r+U4(8+5t`n5oFZEZsyJDFe+q}oZb;qyWC1R|&gvwiY
zM_$2Yg-=@trrh)bc^^1G7`tT`FdMv=di(c1%kAG?uP<LIn|OuygJ&8)zsx<+bRY*v
zUl}_rtWhI=0{f=oB~T8B!|=wi6yw(+8`O(0sUTp&`DcZ*t*gylR@SP;j7-YlxPh$h
z?(R0W>I$RI*g%6DoDUtAo3DLgT2{}u)SnCzPQ!q_0WgEK413T61_jlgE+iBrLP)SH
zvog6dV%xdn=Rv&|1c+0~)!q#b;1v@Si)MO%l5_3o^4p1f-b@HNU0{Kzhh7~%kK(<O
zwBiKa#m}}KPj&`|O*vjPe~25Sao=bl8z!@g=5$)u)z8k+D(7RJIBKtQKMLHR%moX9
z5xAB;DFO0#o5Bp${SROW1TD}UY*#b%a+v@mu6{T<IXOu@fbZ;hYh{)U{CDkIjK|>u
z$Nd-Afk%SBorW)ZA@zWNd9!1*lmKU(fET`KA?AFPt6kY1YTT75#uQ47w>(nXbDYz*
z{gGDUeIeKzn)&)U$kKeIq-$_$cV(FKnk(3eyWV#DQ;SsFJsJ$C4GAL?(^qIwtbnUa
zW2J^=WuNVJeH372^~s9me6($#!QI9-V3ae;=KZZfHDY91+wGj4#|NFBKRV|U3$L*^
zbNoF|!8q~L0LL3FSw)v}=r_LSm6y~4P2&-;C0L3xXADVDdu<GLyZW*#@r^+0$t0oY
zy|2+Ho;n@}v$8@bU|T1nsAzX7xB7H{I*8kRh|9D;i(S8QknBO{9TpZUKD(FsJv8A#
zhl2^Hq&tlimhTc?>@4=NXcTQ1AE$-WTYW|%)j-4KtCnn?EbOY)y>HN3T8Y5VcfUDr
z&5;!1J0hZaZRZ$-j)4_+jzGZ**T2@u1aS*Y`dVYVo<4oruJ+?IHBg))^Wk<K=D7ts
zzLOS)IA)zHx2byHUHgV;4U5ub&$H788PC*c8^55H#I!~{U=Xb%4P`lmz``~f8q=Zv
zglt4|av(w1P3?J+iNwHuAc^R;uLOE2?#?{mzgUDE%v8xmFQWmk5yfH93bq~%E#902
zcO@n!dI+8P(z$N`ww{l1{1GSQxjECqTK!;oxHvc8BvmG+zAQ53L{YmMxDC6Np*|<)
zA>-7DaIodIx4$tx4+yiFX_i9eDP=0FXgklgM{veDP5DX*u`PEcHGy5t`5!T^OWDPI
z%7wCVf?%yHQpnT8+4%rS^+gV54;b-}2lg3e*I((n*OesU4`ZNL&e1eJxoqcRe!H&J
zcHZyW3JEY*8#jxagiP&qr~T>AQm+?P6HxIDatoODGfNVVyRKF4HYmpGZ%ooba6!1c
zwrm$^c8zfY`I69kYrd0v@tLx+VS3s@a95$}fchACpLx^+^Y`{FsP*M<ojG7dD=arF
z%M7fdf?tn$9GOevtW|6U(gJ&YlRwj+t<eAh`*GJYgD81z4mM{S+wPn8leZNZ_ZR|Q
zcz#`LK#X^898N$ZSn>61cZI5f!R*BM*9~v^9F|KgM)|fNWjWRBwRq1lq<h9Z&)nM}
z6x<dCqR+NA!E_KOsz>HwcFCm9+F0cZ(dy~ZTCtk~zfON&Uq{Ttz1?BU;&5eUWrvl*
z0Y1UVsorO@0TEV%kv*9zY_8)m){WP9T>_~d_J5suM?|~qpU&SjRrmMUuv`sF4q991
zLrBrb=eD0GA1594z?Zkt8|H00|D&*I(q}-Hf5SUJhZkOC(zgh#;6a#dYER>@Z#zo^
z55w|I`k2ZaIXn4jmB}KWc~@p+FiH1-=v{0PlYF{+#TPl4DjR3VXRd<?ed>F(M9E`S
zQ{|PjMB}lauz_Cy!pD#`O3a`1*KjQ*^!c5iAo9F*&MN`BybCL~niOs#^q+5!jEwZ5
z@fh47;7XDRa%lGsG|GJLtMfs;1-lj~YLut@I`H*dU*MAA&Z}q4V2uE{qWrbzH-59|
zUZ>bRP=MAF3hpDL8JB?W0M-7~@+D_!!OjIZCUFYj1gbr&*^PQqq($E16B*&{t&W;1
zj#0uoQU_McmRO~GE)!6`%!-vsjdNK{M`2=D9dEY1N|y+tCPe}F^KQ^#+<nhDl0GAK
z>2Rx4$db_eIZ31McRI;1xBW?<=Onk_t7DbJpAQ<ybly|)ZUSKmflX9)BcK$KDS*?`
zSv%k(X7z&RXu`I(w)@gYQ~qr44f=DmhO0f#2DDu}oFX3IX1)TnHvg)X+wGJ1N3X#g
zB3Pj&qDiBq-vvv(Y~R2pZb89z#nz8+x4WmNo^$9~9YwhzxT^Af2&l>&R))aJ<@K{b
zQq6`$RU3jhtaJMk@m`ppdEu-YMeS)P70z20rL&~^E+7u$kuu*)5kqh>xq;xyzdNW8
zHdrf4bT@&uHfr30-$?IPb#Do#Yb-|_&m84L2eT<z&#B1njp?T0NA+5a@7s;5PK@Q{
zKMKJ8q<@#Ou1ZwfFDdJn--7!s0<|4TYX%l+#H?Rm3h>kow(65z4f(qtqlFG;NThpq
zmw)LqKT}fLIb6yG9^u6AZVnue69ptoN%KVLc|A<h3D`&U0sDi-RQlnw?DTZ&V?8}%
zW8?8Ecb15}w6rwy;iCLKw#S&?aJ{k8^}HWMuB3X+61_u1Sr7N8{$EKMCrS(myr;5;
zRCL_wIL0su`E?1s(H5NA(4Jk0Exci2Zl3)zg;BG@G*%UCZmTLtX?mXQJdXhQ&vLwK
z)aedK#%!>>^^?W#ud&5$PB#W{_H#h=(5vbZyS|D{$;RrpKHeK6Htwes3<%l(DMI*5
zMaX6Q_b;P3mIobR9F+9sXjvhh?=>>A;Wb81qaTz<40Jxc>53*`5x;oUA&Sl_EG%qy
zX`oYqBvy>*XP^XPo9Ka$EJh{*&IuxZJ737$%&m#(l3;Ur(C{>l<k>UOOpK689WJ&6
zS~$gxYP_F3#UF+C@$LYqitp6Zr39)5qU3tGtR+8#?REdVk0k<b7=%RJf&0aYnna>C
z`ML%dL=X6L%DcMlV7q?scXzVno0amlDpO|K*zS$(9@$}8cem4n`RCU_i#8t#a4ZXZ
z`^LxZ-J{2)ZkGhchX7U<I^7_2rC;tz4NB%1EwgW1{O$<CQfrCzG%2el_$fZE_aM$~
z(|7iIcGwvBvre$;9tMmF$ZWLS(bG{j9b7a}T2aC8v-PQ=p&^KWvpMp<DFHRV)A9ZW
za8#BfB^x0Ap<m#f1Y!%@-MboK5glxC2Z8-=fZPOJcPlnq?}8xPz8l%TI$ADtyk3`E
zxjPibqTv9XsBupUIt*1Et=d+rW$GhFiY><(;@rLhhzGPmN%Q!u29ppbt}lE%G%|8)
zYa3W7zw7SuY+E>xygLvv#RD_d601qDL_S<-YNRb?SOz`-GSn<G`vuSpz*LnUCx>Up
z^FqaAE~a2@_=R$3Cr7O7Lp%iB6$o#+{PSfOJh0PaJ>o|P_sbLyc)IrMn?w(nB%%UD
z0kKaxRfi_hJ=9JU82W(%@UZZ38a}&bC+^Uv*L`49^z6X>upBN}g6;k!&;4)w8p4Xh
z9&4o24jhe;FeKw*){+Z2L4ZMhVa!yp{KqM@BTKsFdDs*8-sE_e&9jTk9DFW5dlxXS
z;AMibItXcC&p#jn(Dkw}qwgopDL@^U38~$VR*L<jHoo*Mm<6?q>PH|E!jP5W;^G&1
z06nTFs;j9*l&lZ$?{l4cI<5@a18kuFz;3I9dvCF?O(9MuhOfnq9H_`qGi?y1kjIF2
zap+wYzTIXJ`OfyL&wMS3+x!X(3(>ppYDO`$B{>F$fLDeckxEKR26yV6BBbcaST%vi
zk&x~Ye@~uo)JeUu_Iz%(XJH$l*tdi<7I#KAW*r$6&yL2=`UeK0B;h^i=LsH}0XI%H
zV2|(pTuW=f)WoFBeksLx2zX87*xaSVsRn<lwqCB4A=BK^BJ(Ky9y_hvoSay0OYIaU
zwbqK6U`gd%?U;0Xi9pIbY;4?BeJpq9&XOB^&h3tz)ddYE{#$rVg6lPy;EoB9&-h(#
zj}<sdPmRq;PiLR|qL2>IiAf~AhX{{x_ZOfOzFnjHSh~Q!is3wTTy<N{Z>p!#QAiJ&
z4gkJ?X{7WUW0UG=NzD5$Qb`0FT@8wAX^n!Mv4i_H5lT)|O<hCdXfZSQM-;cLcjbVN
zr&dn^@VNdqfiyx@E;}aHMCmz8jY6FM`rm<JgolRuZuvaFD;$M`4z2*yOI|Afo#@ZZ
zyHYM8`dR4plzv~cFUV?!;vQRry?!)U2xBUZTkGaRUlek*yV9me<BBjFE!&w65@HoR
zjZi*b^-uizbu-d5$9nA$;Hzfn+-qz?nMkfX&zgzsAJcyV_ks8vk*<|Y6KvQsC>O}0
zBSZ6to4bkpn3IlT0TkRag78=nO4+LR6K5sIv}v>kNPI<~Jn^l3`dr4Jl;x?UWW!oF
z6&}0c>R`SIhqhns>(5r{H_z?kKf=EkY@iNFNHLxoPYqZSTR}Uua8!RucQ+;MzBFgt
zNEE;G*A(OD<)mcORWT7vmb|(g77Y5#W;ouFDZayXst*#D%HEk&y6o8S-+p?VxDiVX
zuuG@VB(xH$HP9eJBm^!@X~L?<yvK3h=Kz$^Yfj;{Aoveq>e<^DPBSEb<m9#(c{#2O
z&P@j=B_k{6Uzv!u{b3#C5QZGgP`bMzE5xw_U=qNxt-YfwLxm4rw$r3|H`{<lP*{}Q
z>>;J`Jl@Fiy8-S6NM?*c@&Nzef*b^ys&x-rgkN&cg6nE&lLFne0bt2$^_fS6uCZAf
zNJJ(g<WgGBb+aaTGG>kDfJz52S)kLY#1fNU(9&gx6HxNVMPKW+7YKcdPR$!9L%=G<
zSVignFFmGEj@ZFdkT87?s_9-RIA~-E?5N%yGQ~;*s+T_loPI!YJG&VuZp_ua0zvWm
z2CxsAkTc-g<@v?x7$>7Ft4f!@w$(!-Q#zt~do*SEVmv@pXt?$g&`LrmcC}BV+N09x
zMPeyq3W&N$5K@>Ime@QG{OP^`Mbnz<nLMfo7lJTH0UuNjTpml|B2a!>5AcVYrytwW
zvgzP+>#ogqeD2c$+<eLtzy)uQ)-$dHCg)pPkCa%?v2jI6gIhavTo#h&5???jYfzY=
z**i;f^dckVX>v~aDxYoW`voFe@#ZWG?YZ^II^JWxuBwIT`OkOjxWHl|9M-q&s6L8B
z%*%htyBiB`V*pp$90Av4dwN~G++pP{{&kxHEypV~f+PFp@7Gws`L>!r(l&wHN2s~h
zN_@2|T|z@cM_rb(LBO`voG3TC3-07-0Q%oV7p^%qI0k^%32?ThOF8Ahoi9o?#RSrE
zlAzM?>{ds95my2a%Zy{b*qeT(YRAp}qZIubxO=9J6!;wDbR~eBrn5!0ohGq57e8Z8
z0_5I)_Ib86M|A?^Q@g{%+Y3Dm!%p<L+tGs+&fg5yOaOkhNHytjH0$T;;Dm!<+Pj~z
zeSBq7gis(Pss;pQ=2#F)r|6AhdFHTEq#~Z9TIVHgrO(as{ijO-YDggcDHP4k%qeRn
z?u-Sg+U0Q<k7^#L^>_n+Pz)f`v_d66Z=A)Mo71uSjup7DI&4{8d3JgP@aEwF&FSIZ
znq^iW2oX}LBp_O9sH>|hC~#O$)w5L}cjx=g=^9`Cz0Va0LixB=-Ie(`Pmd>0T7XBV
z^apqBR8^h&GQ|Eqs~ZPtF(rk~cPz4ZyB>Rl8d2Pq4$S7Dc&6VJ$a3dS#67)Q0KHb)
z+#JKRKql>*NQpU_CZBYAI8aS+o#P8|U{g16_mG3>&lt9L=;u~gb*hf%qAaN&n0-E&
zdohOvw!NHW;si&(dTrPM_*Oh(BMFWHINQ)s&>;!3t844E@)ub2?j{%o15JG!2mm(U
zW^zn(N*e)(fU*9ILut9W#b)^+O&rYA3-kNcIVKzRphG(xfQ@8eMS)b-byv6t!-Z^S
z9-p0_h(S3lB=`;6$&Z5PTxmV`jurtt@iQ8J_XgA#tq&Jc{j-Zr2UfF-$B5Vb0HQP~
zRq43GJ)+y`?Fr(Ym^}IfaY%3jP1RhaX?yArs1>^IjXDlnYS}IivRl(t=p*bZ=svq5
z(3!|eDz?zkGN)}|A6GWOS%8IwD(T=Fk`dN4P$Aomd#<da5=72kJ91yE%uX%;`5WD<
zB#kUq-b#WDqEzP!7=LLA$bG{`?xW#2zw1JBxQyE@7UzBA?%QqRmK7V~k_}~c3(t*@
zK=R4lcQAf-I`3qVo}cLC5}>;DyDP~k);T3vb$a$QyZ-WZaee`%o&stCmv=Vx-gI-<
z9g&3$mEqbhEmd6rGWglN$JzR%#US-r*SQ$zM@DAO*YF;Yt$-9_fVi)FVcQD>^Zgrl
zF!ViKRH#$!GEsw)sz#Ko6b&yE+}Z%zP~Tm)7OSXNO%<483^3wIrE9UlB*1gSRGXYl
z3M7ADAb~o@=QYCuol2N+4RM*Dzdrzu>XHvg_Hw^^oE+?xRaA_8l|Sl)v7qvRmF(8c
zIb{;pWrG`ab`JqUUH{eYxm)wieE2~Z9`Fm_$bRqhxE_f-q@-a6B_n)E)SN$)(BW^|
zwsL^Eh(q&b2Xm=T02VT5o|8my3B7u(*v?+-Tjsvm!tnfwzOa)_G*8!N*g;4N<M+>b
zan&cAZEP%UGb_o-LlvUPN1&?oDn{m0HpH329<&``h>3D9DTHRh%a1$$odW}b;qO7g
zVMKwujV{S3&b_FT6d>bvQ9E$U)lhh+ULX(VuWC=vqs2gtVj<d%qG5|~YzGss3Dt2m
zD_sinRhRm+Su7XIJb)Mcg-2w&+=+b%5`X|lz-Kg;WL5&ji_e2))Ac8N<Cbb-1NPG{
z1HZl*X^h$ME*Op5xw}_RlWyJXQ7}KpUjFyoX6QS+V2=TNDLdgHCHQ&RFu$|}+<}G+
zAxL_L;`sGW&9zfB4ERprKjP8h!9Bfj|Cfd!rXDc=mC$N{VKjIBKw2{V)$g%%6}0xz
z>&t4b#r(3(+QpJ=mAzm6a+cnv9tB-v5o>AFP{yB1t`uv3gal2+1QP)nS1RZ$a$QCe
zaERt0uXamVocpet&zfMIVc)<2NP<SgkWM|)+`m3$lrZ?+q>VFqKTDOn-kv}-mF~Ei
z|7F7nCM&R`=&BSTfn_)(+uGK~lBuYoVk9{q+{0RZY$JI9qDQOZb^WSoLM&O{*{dzs
z)PA+Ivkgq&rB&fhzFwepDvb3ohF$~f6@?*VsT>3#DC&*6xRVEarAp}_-vyu^O>_XU
z%)=Ef{U4z!+7TKNS#P}9AiL%7wcU66!kl(NDN>_)|JZe9U-!_TwXB&K#v+7hpTtj?
zwj3k&t>^xAOeN^1I<l=G7{(<C2YE^N>Fbv3oQ5UKm>|t5-0lX>=KbvKw*pB-0e5v#
zQU3-*NMbNZ=8F~6tE$G<?S6ew)sh<I&R!~W;_f5~Bi;CWjDN;?S<luP4i74e<h4y<
zi$Y%ql|P;Zx+9EvSy}C$KRv$f_6zz3@gd6Vgx4EX<U!5z&a)KN>U}qtO1-+vZAFwc
zJo4c7FWDQL>5U&i-d<VRZ&|scY+(_Z($W;%h%F2XVb1eZk|%pB!{O<hpqk3>aa>~H
zym86VnJ+xrq4BzrYU_Z#`|3z(M8+56A#iyXOXfQo+%mIf?f8lccdhYxRrUJ63tbaO
zzlBlGB=AmbjUuxM=cHS3K~UD1JK&Wif!U_$H^gJ1{S_~nBY^Sh!b9jRcIp4Lcjf<3
zuJ6Cb&>*L=PN58yWNlCmg(=H9WsqYVWK_z&We;JB79~!$W(u7gOQEr5Uk{40RF;T{
zV;%dR{d>)EzOT>6>-)?156F+tyq@R2pX<Kv>v~`B_x)Tji6S&5*WPv5p<i<{nG`U9
zraYti+4F33ueA4vMU<um0zj83=n)e*p`Yizsvw6I(FtG=0cS2yKYw*|eP!TLa+(Ef
zCD4wJeX%37L8GzM0(xuFV4i@C`9xnuUrr3*I;l5>=h`$0c}`vKfVyHsp~Oj!BRNR_
zog->+|7m0#CLO7C#hz~mgX{L!-lKbDvTT|JlTHJ4ah`u`l4-tJzb}aLn6HVGPT)md
zk0z#I5uiNf{7GYOk#o$7?Yk+@xYFe}kg2-ly(ftDyDsG`!+>VY7_G@V82{n={wD^y
zt-{k%Q3P?|>H49JJFp4xxB|qGUTC{_p*wM?i)}8u22RC8X|`s40$OWThjP{?@j;62
zrCC)h_xiW+9Q3l>f@oDcKMwH%*x^anpj_89=%CjSeH=J*bVAVgzeJccuGJi5g8`pE
zkkyye&u84j_cN7*X%>fzZsgm-e;p8-gqTthxZ$_RNVfk1MT~9pb7csn^or)U;K_aa
zg6iblfLQrRvr?aJnaniLFNWIRw(%+M*6IRac9lb3TtGVl(1MO@JW3~+lm<Kw)_Q>C
z3*p2Ss3#bq<+<`auuV#s-h4;XBonA|MU>qK0fJ})AQL{YFwx}!oVUktBlj}}K}YGJ
zr7l~javkAi_NgvhgmU_%t|OGrKA|mO1mG!NTD~-tZ?BSY1|_F%GZ*G>Afns4x06`|
zuF-{9ml*BifEy^l8bDIVPAQHS$%qV@b?n-8(2e;&0(4uUU}BO*f)X5UikT1<hQla*
zR|R)lA@$TZJfV<V_@pDFoC&)vP&N(MS7uS*Qns<wEn@+iL>VlVbj)!{NgiC?C(?@1
zVW`{22f%iZPts^KE2w*JvvR6!ez$(d3d!@Yed^L-4O>T(`fhzyoo`fFB3^G>f|zav
zwAY4NXk7`AQb0AloSXBUQqBb76X*x8!3*M1Ql@VL`a>}jEr%4DoG|1-eq;B(NMhsD
z$cTYh{yV;7odGstFW|7n#aB})+0BT53b#Yb?loyqLugyTK#)OCa&T7|cNcr5e^=TB
zeZZay?FvVsnWRU$q@?8P(xlYeUVyJq(>In@f_PLHA>!JIb78$g=;lnsS!+WnLtAS%
zwK~w%E*z;D<7v4Ct0)4*$-E>igg^{#_;Y(ZCE^~Ctu}!J9`Y1n+|>|bZSqgUnNL%p
z-6gATD{=xrb@(S0FUCUuJrQag5N4|jWAfed1xl^1DA3)U`4TQ9*QoA=o^cXyM>i{R
z23+Fk_a-j@)5Y#Rb-8kvjuDhGyjy-^OZ7eCjPY?(aBI1Wyu3WCiojSZ9|rSK;nyYa
z`c%RBVnE?1Po7jZ>J8-7T_Fz851dQlA9g*nVnRH0MNWT&(kfdt$g}p<<(j0;>_Sgu
z5%17YlOwC5Oe+ySC+P)I+;>?nGl~ag_vrWICW8Y0S`{n9LP<zyL`g4Qn<@d$OR<H!
zb(oNE-ztvlQ-kBy!#>QM1_7q}%rAt8sYVYJYz8t3yaTc*ia=H)h#HD|TE(BMa$<c~
zKIzC&So)zU-d^psGV~+kM<vaLt~%mqD(xDtt!NE@CQMy^jRn4lx`PLHKn6FlKHpNX
z=hWK1%9>Pdcd>XqQ&Us@gfm?vUwiv8VC7w3X+tS9i@!|>f1-afmS?Jb&4sRRdh^sD
zIXOAtv9a!h)(;}cGaz*4uWKaO4mFwD2<k?rM%CLhdF8Fs@wT^soJmW|>N#a%qfu=|
zB0)Qs5Q4uM8`+on`PbKqJiQCp!Yg&T*F3Q6pwvK6T>y7@aA9jcajQ+SNf<TGU_koI
z0TTQH?=cf&;{!S=zr6yXp&WM0RZV#IiBp!4zNHnQKmoZ?oQM7I>mbj7iVS3UEFrW4
z82`E_;PYjm=x#33f*v>@IQopGAJn+=?*p9HyMbYHbTrK%iaz#b64x1G0#K=&=h}p?
z__~bj3%W|C%a#yY6_-^?p+o|TEi2CsG9r`XRYgMqV#&8TS<nY1o6+b&Rlhb53-~0V
z<Y6FFh;SN$ij$fGYpI=gqqp>C!f6&x$f<Y$7}Dn_GIE=9Y~MocdZ>Tah4*pXgIFj?
z0X_vO5iebm4h)HXWeLfZR6ST9RjZU>>*3(w0Df&Pr>PeEeLwIdLW*{g>*tF!#env)
zB}&TaAEUHRv_!cXKu|dY8mqMNlb*=z*wWJLQfEs&?T`Oo4kirm3#Ep249GAKB^F`x
z04Y=Wxa|z}v5-<T@pC}YFqFbC?Ww3EP%2PUCP4!ZeyU*mI6F)k9agP11Y*A$?6naH
zgwSD=_Vbdw>@HsN3>52qtgk`z(nSl6yn~0^HgfCoax#0qQd1znH#{}8uyAz3Q3!$_
zQ>F8I6cszFxL%c+i{#<U;R?WgYiT<Jo7P=0JuI@xl+$Lrf2Azh6kl5?5z1kfoG-&M
zRsq?CAGdsz!e`FN3rt=|keyh8CAVe|feotk)idfSFVrn20iyR08oLjq)5_h?;D{zB
zrySbSJerl?k#?_5lP4#^r<c6IkUzV<Sb4HYa5e+dV}sr#@nKy{(!{4Hwu1cOd;C18
zT#~1#_5)?hy}U|voGBqePc79F8fji#s5b)si>Xb5s_;z8nm~7zNVjtRqBOWB99K67
zedgc<1^<{Gbxa)4i<s4m_c9MLasrMI?r}2-VPO=6omzPyndGU_da1Sgp3v48`^Emx
z8mMvhGuOLO{n7!ofI@?4RLF2Om;~fj2!xs_zFJ1H3wy};XPQzN&o4Y6eSK7fYRN^R
zgC}B)H+26V-hqHxC>d;`XBIzO3DNhfug_=P3Bd9l;|aIg&!LU$FOuO7wq#OpkU|xx
zGrj)P5Bfsi5VOxYK`Yt-j9phwvT8(^h!z;_Edah*Tr}m^)K(w}fXeT1F8FHmAg+!M
zb}a_|c_~w8lk<MS>-vxm2=(9z(*rCnE=H0uUw{xhfBt-m#g}XRIsyAa>a>tby}o|~
zNq>DSMK}33pirfcABQ6bq1N3)V>u1>pd}qra=E0d8*Uu~`}36N=m&yvgM-GLK~MI|
zX0zh-sU7`L*AJTKNa`Z1;h`EC5`o_fg0=!Zuzd9wTJz@S+>^M*Jk5J#=1a0KDT2gC
zXw8G>tyYcPyaPVL6pqh|O><dtH^yFpz!iLrJp_&w;ND8SmXW-?ey2bc9SAgdoxC=7
zfoZl32r?H=?Qz|mVBxP7E5OyJ$UHmrl5+K2&x3cRS}EK!Ixj#4UHa7<#J0}Emu}uH
z8jK06zvkZl+8E@I`(<d{2w*8d86~$`2ZZW#$*{ns@0N~L6iZY9_p!0I%;e@oIizG{
z^4F_%rJWK+flec85uReutq{$w?zXO;6>NJbSKgV^2`ehcQLlKTS$X6U_;>~pTF7n{
zC0)0?S<Hl?(N2Wu=xE}F+)`i*R}wcDQcq8p8fN&?w_K-PNP`juKkwR-i|?e=%pQ(k
zL5V)zulWdB<;3$cO=!=(BR|*Qf63|d*;(^qRjA8AbvPP{VMB)281q?cYMr=!m-}es
z0}gRa%5>RrX(?6J_FHvhAN?7W(1O%)PvgXq!Qo7+)uhJ<$JfqXUnKv*!X;;=T65ne
zF37ax?2BwTBxwB$ZC0ICJFjkUZnr#o6Xeud^%7=Z>wu9#fKn!Or*^rsH-U=?vyS0T
z@1E4t*Eg*gm;<65Fb|{lA%=OTWA5OS^vTk8hnTUcCZ)a}IIy#5mE=zn6}H3*e;0mD
zo&mD^&F#3XS_^GD&AiNg=tBRQ)2G*_h18kS5B&LN%<;pw>COCmBw4O3*0y_>SaP*|
zbtw?6NN@Szj2M?Vp{n|ow{dCetA%(0Bb-V9j6{qk2Jw5B0Gil<A{yZOWIKsR+S?0&
zE2M4O$o*Q{VxL&<W0`J#@*+>#rd6dgqKOZUdZz4S*>@z1^sqs2$#6K<rNDovAqL>8
zi8_yAX@e<S1VhRo(<FUW_7x;dt3tOwCSHI4Eox4|DNfF6^RtLLQ(S?-W1-gG?oA5p
z7=^PJ$Ml{{^lfxbdIQUSM=#Zo?az;_)#R2xR?pT94GrnU$==%9Sgvh9c%f4ao$&LG
zmo-H|WrlKM8rp8b$p*$fnLSG-rKRft8$p>neAKd_+Y<5(?Xp22M(;+=yF>mNZQge(
z?;}k}&o-w@rr!)#GA4oK+k{h4m*CIb9JsAS5Z_9moLYs>zGgRLRv1ZP-R~kqf)+mh
z;<L0yp;8$|6C?Mj`4)(Asf%xZazWc=1min9KRT}KlNT+`&-qJv&=XsRBP=j-Bx!GB
zwul5gbW*OG^U72|v{12SNg~OX8w^8UF<ViNd^rb!MpA7s_e(RNrDqH^e-{X!b}T>|
zk9=^*C;3n2CyaC~=kQK?m#i*kw%_BMP7s7e)_7KH4ctH%pj6{!!@%^Wni|b|Qs6z^
zQT@=X2m^AAX~O7QnY<KB@658av~**0^At!m*Y`h9NH{dSLHU@{#@o4{%;+0*;k8SF
z={FXQuZoky&D>WHCGQPU2mDPDEB-nK8JtcFQ-)e>*3%!+A4Chy@?V}xppS0=x?)xr
zH!_NfiWq9HjG}ItHx&h6nwbRQ*r#NMMlT>cAY=y;0E#jR*xJb$8X9P*Eb#6`Q82Y+
zf__oKK&}Ls+V6(`fm`$zv_Ri|g&E~M^GxrO4QS>a^Eq<VyF?2sZPqZ3_Ew1pq@5A)
zQ>|AXP)C=}6p-Hu3JI;jMx$e7B1F`C9rBrw#b&NGRUcC#C?X<958J09u)HQm{>aW0
z46f^qui>iuc`xVB0d|1|Pr7-3AORwXhi7J&0@;Am=rd~pyP_c;>onZsbt|ee4fBk;
z5l5Z@hQAc3a^So};cZ8KB;k<d-yoz>6BDOb92Vzte3_);tS}rgV#0KRrXb+RPU>fL
zD&lZBb#-+mC8a?hwx4?X1vxsFl?i%!z$PwGY!hCUyS@!D2^u0Y>3zU=hXQ7*=4$Ku
zg0`SXT+^I{3!3OdF1J!tQBW}C@^UOJ%gUMoa%Fkr&_^*2zh|Sx5ubiiB7IBYMO2{e
z{)AP0pY6D6Lk_nJY&dMfa^>cT-&YBmiZ76TI!b{c1<^4ysF?FfdvX#@(icIi>s^2M
z+JA2Fygc$0;l;0Zs#~|dk@s{&vLwP8I)Lj;m2cR%3Nlx(cenFqz4c$i<M<@r(UtUW
z8d6XKi$3KxYCgrk|Es)$Tgzdtkoo!f^s?&<tBd2c$&5W}8mt-|3&?7SYsmqv(~<rc
zwZG9&8S5m9nDA6zMbMQiSKbqUK7G0bj?|Q6s5I>+*zg`VDk?sOzT@fiCHHzDDtPS@
z;I&FA<G!Jlb#m%M-1_k<i?!j2WXxSj5uV~~(6*tXpI%>?uQdR~`w${u*<yzU3r&Jw
zC2YVX-Rt{IKVk(=zo_4w8*FUcD}WF>o&~J3gH3vGIXER~{q^M(x4=LqvF{6H^q%rV
z#0j^nSFeILEYuZe&AmUrGAs(=gSLAP6Dj=N(Ki{~LTK$6KDaWC6&Boxi!9{|3i$|#
zJQ020mEFEog4%CuW3x&!3mmfcnd(u-;kY+^=hH0S1|i19vgR@>RgqM2FrM?k>g3@p
zS*fsiG#3ADnx8mnnRJV#H_}HSF1izB;l6Ha67T+ad}1rYhuiKcjGskzEJ&;zjXmHm
z4&!Tc5Oj69C<_dFitD<kN<0uDu7mSF(g-2B57VWUw~*AMFp6Vu@uXDNEIr~d4;Fu!
z4>9TO@8?&nHo?`Sg2VMo{jQBWxVCP~v7m}`J4V)KM}>q77TBzDt}Dbzw1mNbNOMGY
z=3J)>%J(lUEWiPRcXf58M+(_I_N`P1j0V|!BO!EoAQdP&m%S022`YPXa}J3@k8&3d
z#ag^|?NghHsT4!@f)X|9-ObPHzjxz{Fxgm-t4H_6S;(1p?q;^PBdNI)V^D|>qw=~Z
zXJ=>iQWcbym6?^Oa@UyWRN>!`;bL|+4#VTroQRC?;LPxL`4)(1;)Y=r9OK_eUaT<c
zybugiMWU|p6Bh?7*%0CIqmY6__#4?suisnYQze)b_M%+YY8P1sNfiULT>rG1!JTF$
z>2wtT&#gHeZO;#NYQ7MKX=$)!HmQ&^9zHLJyWnTCAGvs*r5mNF0imJpxtE#Ljv>=j
z&u0COKa50HYd<oJT11#(ly7k}@7s-7L@e5(-tySphQ$jxK7Jz@zwz4@=<CXyPNebW
z%Hy}2kb+p4&gJ*!!bf(@^~9)fl{aFA6<NKm(9w@4uO?TU{Bs{3n_gKNg=9s3;zWgv
z{gP2twR6NfEj&i(_p(^DO=8yOUG$E3+<SxiA_VWgn|xv=IA7@zzC%mTia8hfII^tr
z#0a|sYES@;@@O&j(O_?r<9`b-!+Z@h*1IYO7d5f@!T6_srBoq3?6_E}-1cl<kS-T0
zuo@%fVK$W1^a4><MjVF)`?ldS5E==R5{Dy+cr4+}rA#iVov%1ibXx^+6JD`2x4r8F
zVo@hF^WWK+kpIvZ;N$sK3X4uTm|1rh^S=1~*u#I;7@daMzv*=LC^-X`s-vDEh260l
zsOPd0M<b13Koz317BWZHeN}9Gi5aY7Bb*NGg8*GTnvG3GcbF7E8gdp#j)q4zWIITT
z@0ch0wDm<@Y!bZujn{r096JniJ9TdZ`w@5~?@z`$20NW*RQZk{Vg>vnK%w1$_#*IC
zR?PMryq83)+0X1kjKOc7hube++F?&@V8=IZWDab_F7eliCs8}b>u~$>O%(YAeynGE
z9iNZ7W;wW^we9?EoEbx<Ro19M*pMDghDwe+JJvp9tfuM?>QDI5Fx*hVk#~nhiQSYt
zzi_`l0$xK+A#J<;wwt2SXcP<a!uEpx>$&Ig*hWR&vVYci`w|`tUd)ySO~kit&3pnj
z3M^(%^%MQirC>ZB4*6d`{_m#S_B?TL$5zF~bK7sR?Kr`3q`&gFe|PovB?7EXiajHA
z+l1TW?1o!|lcWFp5(R8ZXl>*CcV7QI6alyH(mDR$m*l~Ml$2MPZ71Fy8VR@Viv8`s
zFI@w3Dd-x?ZM)_6I4Be<WVbYYI{u&c|6#ZvhWp`h|GRPE>zf~2^+$~RPuTyF9DZcg
o{|yezB>W?*{{P^}CW~W(rX%Z&yA%Ag_Fd<-^fcd`yL|h90J@;>h5!Hn

literal 0
HcmV?d00001

diff --git a/packages/docs/static/generated/articles-docs-miscellaneous-linux-dependencies.png b/packages/docs/static/generated/articles-docs-miscellaneous-linux-dependencies.png
new file mode 100644
index 0000000000000000000000000000000000000000..3fc841acfe32d8413f4d8bf85b4c01a4eea76d66
GIT binary patch
literal 37868
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

literal 0
HcmV?d00001


From 7fc59d875742c976a141fa443a06190cc6c2bb6b Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 19 Jun 2024 15:29:40 +0200
Subject: [PATCH 15/36] awesome

---
 packages/cli/src/on-artifact.ts                        | 10 ++++++++--
 packages/core/src/Artifact.tsx                         |  2 +-
 packages/core/src/CompositionManager.tsx               |  2 +-
 packages/core/src/validation/validate-artifact.ts      |  6 +++---
 .../example/src/SubtitleArtifact/SubtitleArtifact.tsx  |  9 ++++++++-
 5 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts
index 7fa19e4c811..b8e7659426d 100644
--- a/packages/cli/src/on-artifact.ts
+++ b/packages/cli/src/on-artifact.ts
@@ -14,12 +14,18 @@ export const handleOnArtifact = (
 			process.cwd(),
 			artifact.filename.replace('/', path.sep),
 		);
-		writeFileSync(artifact.filename, artifact.content);
+
+		const stringOrUintArray =
+			typeof artifact.content === 'string'
+				? artifact.content
+				: new Uint8Array(Object.values(artifact.content));
+
+		writeFileSync(artifact.filename, stringOrUintArray);
 
 		initialProgress.received.push({
 			absoluteOutputDestination,
 			filename: artifact.filename,
-			sizeInBytes: artifact.content.length,
+			sizeInBytes: stringOrUintArray.length,
 		});
 	};
 
diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx
index cca65033b33..28f9fe04461 100644
--- a/packages/core/src/Artifact.tsx
+++ b/packages/core/src/Artifact.tsx
@@ -6,7 +6,7 @@ import {useCurrentFrame} from './use-current-frame';
 
 export const Artifact: React.FC<{
 	readonly filename: string;
-	readonly content: string;
+	readonly content: string | Uint8Array;
 }> = ({filename, content}) => {
 	const {registerRenderAsset, unregisterRenderAsset} =
 		useContext(RenderAssetManager);
diff --git a/packages/core/src/CompositionManager.tsx b/packages/core/src/CompositionManager.tsx
index 7bed4510d9d..728851aa192 100644
--- a/packages/core/src/CompositionManager.tsx
+++ b/packages/core/src/CompositionManager.tsx
@@ -143,7 +143,7 @@ export type ArtifactAsset = {
 	type: 'artifact';
 	id: string;
 	filename: string;
-	content: string;
+	content: string | Uint8Array;
 	frame: number;
 };
 
diff --git a/packages/core/src/validation/validate-artifact.ts b/packages/core/src/validation/validate-artifact.ts
index 162e177b093..f3f4232b291 100644
--- a/packages/core/src/validation/validate-artifact.ts
+++ b/packages/core/src/validation/validate-artifact.ts
@@ -19,13 +19,13 @@ export const validateArtifactFilename = (filename: unknown) => {
 };
 
 const validateContent = (content: unknown) => {
-	if (typeof content !== 'string') {
+	if (typeof content !== 'string' && !(content instanceof Uint8Array)) {
 		throw new TypeError(
-			`The "content" must be a string, but you passed a value of type ${typeof content}`,
+			`The "content" must be a string or Uint8Array, but you passed a value of type ${typeof content}`,
 		);
 	}
 
-	if (content.trim() === '') {
+	if (typeof content === 'string' && content.trim() === '') {
 		throw new Error('The `content` must not be empty');
 	}
 };
diff --git a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx
index 14ca160c160..fde60d1154b 100644
--- a/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx
+++ b/packages/example/src/SubtitleArtifact/SubtitleArtifact.tsx
@@ -5,6 +5,13 @@ export const SubtitleArtifact: React.FC = () => {
 	const frame = useCurrentFrame();
 
 	return frame === 0 ? (
-		<Artifact content="Hello World!" filename="hello.txt" />
+		<Artifact
+			content={
+				new Uint8Array([72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33])
+			}
+			filename="hello-uint8.txt"
+		/>
+	) : frame === 1 ? (
+		<Artifact content="Hello World" filename="hello.txt" />
 	) : null;
 };

From cff3cc991c69074259c4a40d1d4dc70dd1758ed2 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 19 Jun 2024 18:58:24 +0200
Subject: [PATCH 16/36] support uint8array

---
 packages/cli/src/on-artifact.ts               |  9 +---
 .../functions/helpers/serialize-artifact.ts   | 51 +++++++++++++++++++
 .../src/functions/helpers/stream-renderer.ts  | 14 ++---
 packages/lambda/src/functions/launch.ts       |  4 +-
 packages/lambda/src/functions/renderer.ts     |  3 +-
 .../src/functions/streaming/streaming.ts      |  4 +-
 packages/renderer/src/index.ts                |  8 +--
 packages/renderer/src/render-frames.ts        | 17 ++++---
 packages/renderer/src/serialize-artifact.ts   |  5 ++
 9 files changed, 84 insertions(+), 31 deletions(-)
 create mode 100644 packages/lambda/src/functions/helpers/serialize-artifact.ts
 create mode 100644 packages/renderer/src/serialize-artifact.ts

diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts
index b8e7659426d..595d3d946fb 100644
--- a/packages/cli/src/on-artifact.ts
+++ b/packages/cli/src/on-artifact.ts
@@ -15,17 +15,12 @@ export const handleOnArtifact = (
 			artifact.filename.replace('/', path.sep),
 		);
 
-		const stringOrUintArray =
-			typeof artifact.content === 'string'
-				? artifact.content
-				: new Uint8Array(Object.values(artifact.content));
-
-		writeFileSync(artifact.filename, stringOrUintArray);
+		writeFileSync(artifact.filename, artifact.content);
 
 		initialProgress.received.push({
 			absoluteOutputDestination,
 			filename: artifact.filename,
-			sizeInBytes: stringOrUintArray.length,
+			sizeInBytes: artifact.content.length,
 		});
 	};
 
diff --git a/packages/lambda/src/functions/helpers/serialize-artifact.ts b/packages/lambda/src/functions/helpers/serialize-artifact.ts
new file mode 100644
index 00000000000..f65a856928c
--- /dev/null
+++ b/packages/lambda/src/functions/helpers/serialize-artifact.ts
@@ -0,0 +1,51 @@
+import type {EmittedArtifact} from '@remotion/renderer';
+
+export type SerializedArtifact = {
+	filename: string;
+	stringContent: string;
+	frame: number;
+	binary: boolean;
+};
+
+export const deserializeArtifact = (
+	serializedArtifact: SerializedArtifact,
+): EmittedArtifact => {
+	if (serializedArtifact.binary) {
+		const encoder = new TextEncoder();
+		const content = encoder.encode(atob(serializedArtifact.stringContent));
+
+		return {
+			filename: serializedArtifact.filename,
+			content,
+			frame: serializedArtifact.frame,
+		};
+	}
+
+	return {
+		filename: serializedArtifact.filename,
+		content: serializedArtifact.stringContent,
+		frame: serializedArtifact.frame,
+	};
+};
+
+export const serializeArtifact = (
+	artifact: EmittedArtifact,
+): SerializedArtifact => {
+	if (artifact.content instanceof Uint8Array) {
+		const decoder = new TextDecoder('utf8');
+		const b64encoded = btoa(decoder.decode(artifact.content));
+		return {
+			filename: artifact.filename,
+			stringContent: b64encoded,
+			frame: artifact.frame,
+			binary: true,
+		};
+	}
+
+	return {
+		filename: artifact.filename,
+		stringContent: artifact.content,
+		frame: artifact.frame,
+		binary: false,
+	};
+};
diff --git a/packages/lambda/src/functions/helpers/stream-renderer.ts b/packages/lambda/src/functions/helpers/stream-renderer.ts
index e7a041aaaa3..ea99ff99f60 100644
--- a/packages/lambda/src/functions/helpers/stream-renderer.ts
+++ b/packages/lambda/src/functions/helpers/stream-renderer.ts
@@ -1,4 +1,4 @@
-import type {EmittedAsset, LogLevel} from '@remotion/renderer';
+import type {EmittedArtifact, LogLevel} from '@remotion/renderer';
 import {RenderInternals} from '@remotion/renderer';
 import {writeFileSync} from 'fs';
 import {join} from 'path';
@@ -8,6 +8,7 @@ import {callLambdaWithStreaming} from '../../shared/call-lambda';
 import type {OnMessage} from '../streaming/streaming';
 import {getCurrentRegionInFunction} from './get-current-region';
 import type {OverallProgressHelper} from './overall-render-progress';
+import {deserializeArtifact} from './serialize-artifact';
 
 type StreamRendererResponse =
 	| {
@@ -34,7 +35,7 @@ const streamRenderer = ({
 	overallProgress: OverallProgressHelper;
 	files: string[];
 	logLevel: LogLevel;
-	onArtifact: (asset: EmittedAsset) => {alreadyExisted: boolean};
+	onArtifact: (asset: EmittedArtifact) => {alreadyExisted: boolean};
 }) => {
 	if (payload.type !== LambdaRoutines.renderer) {
 		throw new Error('Expected renderer type');
@@ -100,13 +101,14 @@ const streamRenderer = ({
 			}
 
 			if (message.type === 'artifact-emitted') {
+				const artifact = deserializeArtifact(message.payload.artifact);
 				RenderInternals.Log.info(
 					{indent: false, logLevel},
 					`Received artifact on frame ${message.payload.artifact.frame}:`,
-					message.payload.artifact.filename,
-					message.payload.artifact.content.length + 'bytes.',
+					artifact.filename,
+					artifact.content.length + 'bytes.',
 				);
-				const {alreadyExisted} = onArtifact(message.payload.artifact);
+				const {alreadyExisted} = onArtifact(artifact);
 				if (alreadyExisted) {
 					return resolve({
 						type: 'error',
@@ -191,7 +193,7 @@ export const streamRendererFunctionWithRetry = async ({
 	overallProgress: OverallProgressHelper;
 	files: string[];
 	logLevel: LogLevel;
-	onArtifact: (asset: EmittedAsset) => {alreadyExisted: boolean};
+	onArtifact: (asset: EmittedArtifact) => {alreadyExisted: boolean};
 }): Promise<unknown> => {
 	if (payload.type !== LambdaRoutines.renderer) {
 		throw new Error('Expected renderer type');
diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts
index f42a3873c29..2a4932f1d1a 100644
--- a/packages/lambda/src/functions/launch.ts
+++ b/packages/lambda/src/functions/launch.ts
@@ -1,5 +1,5 @@
 /* eslint-disable @typescript-eslint/no-use-before-define */
-import type {EmittedAsset, LogOptions} from '@remotion/renderer';
+import type {EmittedArtifact, LogOptions} from '@remotion/renderer';
 import {RenderInternals} from '@remotion/renderer';
 import {existsSync, mkdirSync, rmSync} from 'fs';
 import {join} from 'path';
@@ -356,7 +356,7 @@ const innerLaunchHandler = async ({
 
 	const files: string[] = [];
 
-	const onArtifact = (artifact: EmittedAsset): {alreadyExisted: boolean} => {
+	const onArtifact = (artifact: EmittedArtifact): {alreadyExisted: boolean} => {
 		if (
 			overallProgress
 				.getReceivedAssets()
diff --git a/packages/lambda/src/functions/renderer.ts b/packages/lambda/src/functions/renderer.ts
index 439c63136d5..f6ac91c2b0a 100644
--- a/packages/lambda/src/functions/renderer.ts
+++ b/packages/lambda/src/functions/renderer.ts
@@ -28,6 +28,7 @@ import {getCurrentRegionInFunction} from './helpers/get-current-region';
 import {startLeakDetection} from './helpers/leak-detection';
 import {onDownloadsHelper} from './helpers/on-downloads-logger';
 import type {RequestContext} from './helpers/request-context';
+import {serializeArtifact} from './helpers/serialize-artifact';
 import {timer} from './helpers/timer';
 import {getTmpDirStateIfENoSp} from './helpers/write-lambda-error';
 import type {OnStream} from './streaming/streaming';
@@ -183,7 +184,7 @@ const renderHandler = async ({
 		onStream({
 			type: 'artifact-emitted',
 			payload: {
-				artifact,
+				artifact: serializeArtifact(artifact),
 			},
 		})
 			.then(() => {
diff --git a/packages/lambda/src/functions/streaming/streaming.ts b/packages/lambda/src/functions/streaming/streaming.ts
index 71966376ef3..d9215838dec 100644
--- a/packages/lambda/src/functions/streaming/streaming.ts
+++ b/packages/lambda/src/functions/streaming/streaming.ts
@@ -1,5 +1,5 @@
-import type {EmittedAsset} from '@remotion/renderer';
 import {makeStreamPayloadMessage} from '@remotion/streaming';
+import type {SerializedArtifact} from '../helpers/serialize-artifact';
 import type {LambdaErrorInfo} from '../helpers/write-lambda-error';
 import type {RenderStillLambdaResponsePayload} from '../still';
 
@@ -90,7 +90,7 @@ export type StreamingPayload =
 	| {
 			type: typeof artifactEmitted;
 			payload: {
-				artifact: EmittedAsset;
+				artifact: SerializedArtifact;
 			};
 	  };
 
diff --git a/packages/renderer/src/index.ts b/packages/renderer/src/index.ts
index 9b3973414ee..912d26d792c 100644
--- a/packages/renderer/src/index.ts
+++ b/packages/renderer/src/index.ts
@@ -108,12 +108,7 @@ export {X264Preset} from './options/x264-preset';
 export {PixelFormat} from './pixel-format';
 export {RemotionServer} from './prepare-server';
 export {ProResProfile} from './prores-profile';
-export {
-	EmittedArtifact as EmittedAsset,
-	OnArtifact,
-	RenderFramesOptions,
-	renderFrames,
-} from './render-frames';
+export {OnArtifact, RenderFramesOptions, renderFrames} from './render-frames';
 export {
 	InternalRenderMediaOptions,
 	RenderMediaOnProgress,
@@ -127,6 +122,7 @@ export {
 	SelectCompositionOptions,
 	selectComposition,
 } from './select-composition';
+export {EmittedArtifact} from './serialize-artifact';
 export {
 	StitchFramesToVideoOptions,
 	stitchFramesToVideo,
diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts
index 5b8dc820233..5bdc261ed56 100644
--- a/packages/renderer/src/render-frames.ts
+++ b/packages/renderer/src/render-frames.ts
@@ -53,6 +53,7 @@ import {puppeteerEvaluateWithCatch} from './puppeteer-evaluate';
 import type {BrowserReplacer} from './replace-browser';
 import {handleBrowserCrash} from './replace-browser';
 import {seekToFrame} from './seek-to-frame';
+import type {EmittedArtifact} from './serialize-artifact';
 import {setPropsAndEnv} from './set-props-and-env';
 import {takeFrameAndCompose} from './take-frame-and-compose';
 import {truthy} from './truthy';
@@ -106,12 +107,6 @@ type InternalRenderFramesOptions = {
 	onArtifact: OnArtifact | null;
 } & ToOptions<typeof optionsMap.renderFrames>;
 
-export type EmittedArtifact = {
-	filename: string;
-	content: string | Uint8Array;
-	frame: number;
-};
-
 type InnerRenderFramesOptions = {
 	onStart: null | ((data: OnStartData) => void);
 	onFrameUpdate:
@@ -531,7 +526,15 @@ const innerRenderFrames = async ({
 				}
 			}
 
-			onArtifact?.(artifact);
+			const stringOrUintArray =
+				typeof artifact.content === 'string'
+					? artifact.content
+					: new Uint8Array(Object.values(artifact.content));
+
+			onArtifact?.({
+				...artifact,
+				content: stringOrUintArray,
+			});
 		}
 
 		const compressedAssets = audioAndVideoAssets.map((asset) => {
diff --git a/packages/renderer/src/serialize-artifact.ts b/packages/renderer/src/serialize-artifact.ts
new file mode 100644
index 00000000000..45b067da23b
--- /dev/null
+++ b/packages/renderer/src/serialize-artifact.ts
@@ -0,0 +1,5 @@
+export type EmittedArtifact = {
+	filename: string;
+	content: string | Uint8Array;
+	frame: number;
+};

From 3821e955647f2428486465d52fa034f75d676741 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 19 Jun 2024 19:21:18 +0200
Subject: [PATCH 17/36] more abstractions

---
 packages/cli/src/render-flows/still.ts        | 20 ++++--
 .../functions/render-media-single-thread.ts   |  5 +-
 .../functions/render-still-single-thread.ts   |  3 +
 packages/lambda/src/functions/still.ts        | 65 +++++++++++++++++--
 packages/renderer/src/collect-assets.ts       | 41 ++++++++++++
 packages/renderer/src/render-frames.ts        | 10 +--
 packages/renderer/src/render-still.ts         | 26 +++++++-
 .../renderer/src/take-frame-and-compose.ts    | 13 +---
 8 files changed, 147 insertions(+), 36 deletions(-)
 create mode 100644 packages/renderer/src/collect-assets.ts

diff --git a/packages/cli/src/render-flows/still.ts b/packages/cli/src/render-flows/still.ts
index 3406481e2cd..83f86725327 100644
--- a/packages/cli/src/render-flows/still.ts
+++ b/packages/cli/src/render-flows/still.ts
@@ -14,6 +14,7 @@ import type {
 	AggregateRenderProgress,
 	JobProgressCallback,
 } from '@remotion/studio-server';
+import type {ArtifactProgress} from '@remotion/studio-shared';
 import {existsSync, mkdirSync} from 'node:fs';
 import path from 'node:path';
 import {NoReactInternals} from 'remotion/no-react';
@@ -27,6 +28,7 @@ import {getCompositionWithDimensionOverride} from '../get-composition-with-dimen
 import {makeHyperlink} from '../hyperlinks/make-link';
 import {Log} from '../log';
 import {makeOnDownload} from '../make-on-download';
+import {handleOnArtifact} from '../on-artifact';
 import {parsedCli, quietFlagProvided} from '../parsed-cli';
 import type {OverwriteableCliOutput} from '../progress-bar';
 import {
@@ -124,15 +126,13 @@ export const renderStillFlow = async ({
 	const updateRenderProgress = ({
 		newline,
 		printToConsole,
-		isUsingParallelEncoding,
 	}: {
 		newline: boolean;
 		printToConsole: boolean;
-		isUsingParallelEncoding: boolean;
 	}) => {
 		const {output, progress, message} = makeRenderingAndStitchingProgress({
 			prog: aggregate,
-			isUsingParallelEncoding,
+			isUsingParallelEncoding: false,
 		});
 		if (printToConsole) {
 			renderProgress.update(updatesDontOverwrite ? message : output, newline);
@@ -176,7 +176,6 @@ export const renderStillFlow = async ({
 				updateRenderProgress({
 					newline: false,
 					printToConsole: true,
-					isUsingParallelEncoding: false,
 				});
 			},
 			indentOutput: indent,
@@ -299,6 +298,8 @@ export const renderStillFlow = async ({
 
 	const renderStart = Date.now();
 
+	let artifactState: ArtifactProgress = {received: []};
+
 	aggregate.rendering = {
 		frames: 0,
 		doneIn: null,
@@ -309,7 +310,6 @@ export const renderStillFlow = async ({
 	updateRenderProgress({
 		newline: false,
 		printToConsole: true,
-		isUsingParallelEncoding: false,
 	});
 
 	const onDownload: RenderMediaOnDownload = makeOnDownload({
@@ -321,6 +321,14 @@ export const renderStillFlow = async ({
 		isUsingParallelEncoding: false,
 	});
 
+	const {onArtifact} = handleOnArtifact(artifactState, (progress) => {
+		artifactState = progress;
+		updateRenderProgress({
+			newline: false,
+			printToConsole: true,
+		});
+	});
+
 	await RenderInternals.internalRenderStill({
 		composition: config,
 		frame: stillFrame,
@@ -352,6 +360,7 @@ export const renderStillFlow = async ({
 		offthreadVideoCacheSizeInBytes,
 		binariesDirectory,
 		onBrowserDownload,
+		onArtifact,
 	});
 
 	aggregate.rendering = {
@@ -363,7 +372,6 @@ export const renderStillFlow = async ({
 	updateRenderProgress({
 		newline: true,
 		printToConsole: true,
-		isUsingParallelEncoding: false,
 	});
 	Log.info(
 		{indent, logLevel},
diff --git a/packages/cloudrun/src/functions/render-media-single-thread.ts b/packages/cloudrun/src/functions/render-media-single-thread.ts
index 625d3421063..2b977ee6f71 100644
--- a/packages/cloudrun/src/functions/render-media-single-thread.ts
+++ b/packages/cloudrun/src/functions/render-media-single-thread.ts
@@ -64,9 +64,8 @@ export const renderMediaSingleThread = async (
 			enableMultiProcessOnLinux: true,
 		};
 
-		const onArtifact: OnArtifact = (artifact) => {
-			// TODO: Do something with the artifact
-			console.log(artifact);
+		const onArtifact: OnArtifact = () => {
+			throw new Error('Emitting artifacts is not supported in Cloud Run');
 		};
 
 		await RenderInternals.internalRenderMedia({
diff --git a/packages/cloudrun/src/functions/render-still-single-thread.ts b/packages/cloudrun/src/functions/render-still-single-thread.ts
index d822f768cb4..a650953d8e3 100644
--- a/packages/cloudrun/src/functions/render-still-single-thread.ts
+++ b/packages/cloudrun/src/functions/render-still-single-thread.ts
@@ -98,6 +98,9 @@ export const renderStillSingleThread = async (
 			onBrowserDownload: () => {
 				throw new Error('Should not download a browser in Cloud Run');
 			},
+			onArtifact: () => {
+				throw new Error('Emitting artifacts is not supported in Cloud Run');
+			},
 		});
 		Log.info({indent: false, logLevel: body.logLevel}, 'Still rendered');
 
diff --git a/packages/lambda/src/functions/still.ts b/packages/lambda/src/functions/still.ts
index 6df26d9a4a1..c741ed64e49 100644
--- a/packages/lambda/src/functions/still.ts
+++ b/packages/lambda/src/functions/still.ts
@@ -1,4 +1,4 @@
-import type {StillImageFormat} from '@remotion/renderer';
+import type {EmittedArtifact, StillImageFormat} from '@remotion/renderer';
 import {RenderInternals} from '@remotion/renderer';
 import fs from 'node:fs';
 import path from 'node:path';
@@ -18,6 +18,7 @@ import type {
 import {
 	LambdaRoutines,
 	MAX_EPHEMERAL_STORAGE_IN_MB,
+	artifactName,
 	overallProgressKey,
 } from '../shared/constants';
 import {convertToServeUrl} from '../shared/convert-to-serve-url';
@@ -39,6 +40,7 @@ import {getCurrentRegionInFunction} from './helpers/get-current-region';
 import {getOutputUrlFromMetadata} from './helpers/get-output-url-from-metadata';
 import {lambdaWriteFile} from './helpers/io';
 import {onDownloadsHelper} from './helpers/on-downloads-logger';
+import type {ReceivedAsset} from './helpers/overall-render-progress';
 import {makeInitialOverallRenderProgress} from './helpers/overall-render-progress';
 import {validateComposition} from './helpers/validate-composition';
 import type {OnStream} from './streaming/streaming';
@@ -194,6 +196,58 @@ const innerStillHandler = async ({
 		throw new Error('Should not download a browser in Lambda');
 	};
 
+	const receivedAssets: ReceivedAsset[] = [];
+
+	const {key, renderBucketName, customCredentials} = getExpectedOutName(
+		renderMetadata,
+		bucketName,
+		getCredentialsFromOutName(lambdaParams.outName),
+	);
+
+	const onArtifact = (artifact: EmittedArtifact): {alreadyExisted: boolean} => {
+		if (receivedAssets.find((a) => a.filename === artifact.filename)) {
+			return {alreadyExisted: true};
+		}
+
+		const s3Key = artifactName(renderMetadata.renderId, artifact.filename);
+		receivedAssets.push({
+			filename: artifact.filename,
+			sizeInBytes: artifact.content.length,
+			s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`,
+		});
+
+		const startTime = Date.now();
+		RenderInternals.Log.info(
+			{indent: false, logLevel: lambdaParams.logLevel},
+			'Writing artifact ' + artifact.filename + ' to S3',
+		);
+		lambdaWriteFile({
+			bucketName: renderBucketName,
+			key: s3Key,
+			body: artifact.content,
+			region,
+			privacy: lambdaParams.privacy,
+			expectedBucketOwner,
+			downloadBehavior: lambdaParams.downloadBehavior,
+			customCredentials,
+		})
+			.then(() => {
+				RenderInternals.Log.info(
+					{indent: false, logLevel: lambdaParams.logLevel},
+					`Wrote artifact to S3 in ${Date.now() - startTime}ms`,
+				);
+			})
+			.catch((err) => {
+				// TODO: Handle error
+				RenderInternals.Log.error(
+					{indent: false, logLevel: lambdaParams.logLevel},
+					'Failed to write artifact to S3',
+					err,
+				);
+			});
+		return {alreadyExisted: false};
+	};
+
 	await RenderInternals.internalRenderStill({
 		composition,
 		output: outputPath,
@@ -229,14 +283,9 @@ const innerStillHandler = async ({
 		offthreadVideoCacheSizeInBytes: lambdaParams.offthreadVideoCacheSizeInBytes,
 		binariesDirectory: null,
 		onBrowserDownload,
+		onArtifact,
 	});
 
-	const {key, renderBucketName, customCredentials} = getExpectedOutName(
-		renderMetadata,
-		bucketName,
-		getCredentialsFromOutName(lambdaParams.outName),
-	);
-
 	const {size} = await fs.promises.stat(outputPath);
 
 	await lambdaWriteFile({
@@ -284,6 +333,7 @@ const innerStillHandler = async ({
 		estimatedPrice: formatCostsInfo(estimatedPrice),
 		renderId,
 		outKey,
+		receivedAssets,
 	};
 
 	onStream({
@@ -301,6 +351,7 @@ export type RenderStillLambdaResponsePayload = {
 	sizeInBytes: number;
 	estimatedPrice: CostsInfo;
 	renderId: string;
+	receivedAssets: ReceivedAsset[];
 };
 
 export const stillHandler = async (
diff --git a/packages/renderer/src/collect-assets.ts b/packages/renderer/src/collect-assets.ts
new file mode 100644
index 00000000000..334d33df73e
--- /dev/null
+++ b/packages/renderer/src/collect-assets.ts
@@ -0,0 +1,41 @@
+import type {TRenderAsset} from 'remotion/no-react';
+import type {Page} from './browser/BrowserPage';
+import {puppeteerEvaluateWithCatch} from './puppeteer-evaluate';
+
+export const collectAssets = async ({
+	frame,
+	freePage,
+	timeoutInMilliseconds,
+}: {
+	frame: number;
+	freePage: Page;
+	timeoutInMilliseconds: number;
+}) => {
+	const {value} = await puppeteerEvaluateWithCatch<TRenderAsset[]>({
+		pageFunction: () => {
+			return window.remotion_collectAssets();
+		},
+		args: [],
+		frame,
+		page: freePage,
+		timeoutInMilliseconds,
+	});
+
+	const fixedArtifacts = value.map((asset) => {
+		if (asset.type !== 'artifact') {
+			return asset;
+		}
+
+		const stringOrUintArray =
+			typeof asset.content === 'string'
+				? asset.content
+				: new Uint8Array(Object.values(asset.content));
+
+		return {
+			...asset,
+			content: stringOrUintArray,
+		};
+	});
+
+	return fixedArtifacts;
+};
diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts
index 5bdc261ed56..b338772df0d 100644
--- a/packages/renderer/src/render-frames.ts
+++ b/packages/renderer/src/render-frames.ts
@@ -526,15 +526,7 @@ const innerRenderFrames = async ({
 				}
 			}
 
-			const stringOrUintArray =
-				typeof artifact.content === 'string'
-					? artifact.content
-					: new Uint8Array(Object.values(artifact.content));
-
-			onArtifact?.({
-				...artifact,
-				content: stringOrUintArray,
-			});
+			onArtifact?.(artifact);
 		}
 
 		const compressedAssets = audioAndVideoAssets.map((asset) => {
diff --git a/packages/renderer/src/render-still.ts b/packages/renderer/src/render-still.ts
index 54b8453188d..14796a1ce34 100644
--- a/packages/renderer/src/render-still.ts
+++ b/packages/renderer/src/render-still.ts
@@ -17,6 +17,7 @@ import type {Compositor} from './compositor/compositor';
 import {convertToPositiveFrameIndex} from './convert-to-positive-frame-index';
 import {ensureOutputDirectory} from './ensure-output-directory';
 import {handleJavascriptException} from './error-handling/handle-javascript-exception';
+import {onlyArtifact} from './filter-asset-types';
 import {findRemotionRoot} from './find-closest-package-json';
 import type {StillImageFormat} from './image-format';
 import {
@@ -34,6 +35,7 @@ import {DEFAULT_OVERWRITE} from './overwrite';
 import type {RemotionServer} from './prepare-server';
 import {makeOrReuseServer} from './prepare-server';
 import {puppeteerEvaluateWithCatch} from './puppeteer-evaluate';
+import type {OnArtifact} from './render-frames';
 import {seekToFrame} from './seek-to-frame';
 import {setPropsAndEnv} from './set-props-and-env';
 import {takeFrameAndCompose} from './take-frame-and-compose';
@@ -68,6 +70,7 @@ type InternalRenderStillOptions = {
 	serveUrl: string;
 	port: number | null;
 	offthreadVideoCacheSizeInBytes: number | null;
+	onArtifact: OnArtifact | null;
 } & ToOptions<typeof optionsMap.renderStill>;
 
 export type RenderStillOptions = {
@@ -99,6 +102,7 @@ export type RenderStillOptions = {
 	 * @deprecated Renamed to `jpegQuality`
 	 */
 	quality?: never;
+	onArtifact?: OnArtifact;
 } & Partial<ToOptions<typeof optionsMap.renderStill>>;
 
 type CleanupFn = () => Promise<unknown>;
@@ -130,6 +134,7 @@ const innerRenderStill = async ({
 	indent,
 	serializedResolvedPropsWithCustomSchema,
 	onBrowserDownload,
+	onArtifact,
 }: InternalRenderStillOptions & {
 	downloadMap: DownloadMap;
 	serveUrl: string;
@@ -316,7 +321,7 @@ const innerRenderStill = async ({
 		attempt: 0,
 	});
 
-	const {buffer} = await takeFrameAndCompose({
+	const {buffer, collectedAssets} = await takeFrameAndCompose({
 		frame: stillFrame,
 		freePage: page,
 		height: composition.height,
@@ -331,6 +336,23 @@ const innerRenderStill = async ({
 		timeoutInMilliseconds,
 	});
 
+	const artifactAssets = onlyArtifact(collectedAssets);
+	const previousArtifactAssets = [];
+
+	for (const artifact of artifactAssets) {
+		for (const previousArtifact of previousArtifactAssets) {
+			if (artifact.filename === previousArtifact.filename) {
+				throw new Error(
+					`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. An artifact`,
+				);
+			}
+		}
+
+		previousArtifactAssets.push(artifact);
+
+		onArtifact?.(artifact);
+	}
+
 	await cleanup();
 
 	return {buffer: output ? null : buffer};
@@ -443,6 +465,7 @@ export const renderStill = (
 		logLevel: passedLogLevel,
 		binariesDirectory,
 		onBrowserDownload,
+		onArtifact,
 	} = options;
 
 	if (typeof jpegQuality !== 'undefined' && imageFormat !== 'jpeg') {
@@ -499,5 +522,6 @@ export const renderStill = (
 		onBrowserDownload:
 			onBrowserDownload ??
 			defaultBrowserDownloadProgress({indent, logLevel, api: 'renderStill()'}),
+		onArtifact: onArtifact ?? null,
 	});
 };
diff --git a/packages/renderer/src/take-frame-and-compose.ts b/packages/renderer/src/take-frame-and-compose.ts
index 6324d63dfdb..8f47880a9e2 100644
--- a/packages/renderer/src/take-frame-and-compose.ts
+++ b/packages/renderer/src/take-frame-and-compose.ts
@@ -3,6 +3,7 @@ import path from 'node:path';
 import type {ClipRegion, TRenderAsset} from 'remotion/no-react';
 import type {DownloadMap} from './assets/download-map';
 import type {Page} from './browser/BrowserPage';
+import {collectAssets} from './collect-assets';
 import {compose} from './compositor/compose';
 import type {Compositor} from './compositor/compositor';
 import type {StillImageFormat, VideoImageFormat} from './image-format';
@@ -37,7 +38,7 @@ export const takeFrameAndCompose = async ({
 	compositor: Compositor;
 	timeoutInMilliseconds: number;
 }): Promise<{buffer: Buffer | null; collectedAssets: TRenderAsset[]}> => {
-	const [{value: clipRegion}, {value: collectedAssets}] = await Promise.all([
+	const [{value: clipRegion}, collectedAssets] = await Promise.all([
 		puppeteerEvaluateWithCatch<ClipRegion | null>({
 			pageFunction: () => {
 				if (typeof window.remotion_getClipRegion === 'undefined') {
@@ -51,15 +52,7 @@ export const takeFrameAndCompose = async ({
 			page: freePage,
 			timeoutInMilliseconds,
 		}),
-		puppeteerEvaluateWithCatch<TRenderAsset[]>({
-			pageFunction: () => {
-				return window.remotion_collectAssets();
-			},
-			args: [],
-			frame,
-			page: freePage,
-			timeoutInMilliseconds,
-		}),
+		collectAssets({frame, freePage, timeoutInMilliseconds}),
 	]);
 
 	if (imageFormat === 'none') {

From 4b7bc6505f066ef171e4c3d98af1dccbfbe37e62 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 19 Jun 2024 19:24:35 +0200
Subject: [PATCH 18/36] Update still.ts

---
 packages/cli/src/render-flows/still.ts | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/packages/cli/src/render-flows/still.ts b/packages/cli/src/render-flows/still.ts
index 83f86725327..8ffa426fa50 100644
--- a/packages/cli/src/render-flows/still.ts
+++ b/packages/cli/src/render-flows/still.ts
@@ -14,7 +14,6 @@ import type {
 	AggregateRenderProgress,
 	JobProgressCallback,
 } from '@remotion/studio-server';
-import type {ArtifactProgress} from '@remotion/studio-shared';
 import {existsSync, mkdirSync} from 'node:fs';
 import path from 'node:path';
 import {NoReactInternals} from 'remotion/no-react';
@@ -298,8 +297,6 @@ export const renderStillFlow = async ({
 
 	const renderStart = Date.now();
 
-	let artifactState: ArtifactProgress = {received: []};
-
 	aggregate.rendering = {
 		frames: 0,
 		doneIn: null,
@@ -321,8 +318,9 @@ export const renderStillFlow = async ({
 		isUsingParallelEncoding: false,
 	});
 
-	const {onArtifact} = handleOnArtifact(artifactState, (progress) => {
-		artifactState = progress;
+	const {onArtifact} = handleOnArtifact(aggregate.artifactState, (progress) => {
+		aggregate.artifactState = progress;
+
 		updateRenderProgress({
 			newline: false,
 			printToConsole: true,

From 8474cfd20a221e84d13a32dde744b6b16afe52ac Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 19 Jun 2024 20:05:19 +0200
Subject: [PATCH 19/36] lambda improvements

---
 packages/example/runlambda.sh                 |  2 +-
 .../lambda/src/api/render-still-on-lambda.ts  |  3 +
 .../src/cli/commands/render/progress.ts       | 10 ++--
 .../lambda/src/cli/commands/render/render.ts  | 60 +++++++++++++------
 packages/lambda/src/cli/commands/still.ts     | 53 ++++++++++++----
 .../helpers/overall-render-progress.ts        |  1 +
 packages/lambda/src/functions/launch.ts       |  1 +
 packages/lambda/src/functions/still.ts        |  1 +
 8 files changed, 96 insertions(+), 35 deletions(-)

diff --git a/packages/example/runlambda.sh b/packages/example/runlambda.sh
index 935c37f18bd..0c9bd57010b 100644
--- a/packages/example/runlambda.sh
+++ b/packages/example/runlambda.sh
@@ -7,4 +7,4 @@ cd example
 bunx remotion lambda functions rmall -f
 bunx remotion lambda functions deploy --memory=3000 --disk=10000
 bunx remotion lambda sites create --site-name=testbed-v6 --log=verbose --enable-folder-expiry
-bunx remotion lambda render testbed-v6 subtitle --log=verbose --delete-after="1-day"
+bunx remotion lambda still testbed-v6 subtitle --log=verbose --delete-after="1-day" out-there.png
diff --git a/packages/lambda/src/api/render-still-on-lambda.ts b/packages/lambda/src/api/render-still-on-lambda.ts
index 6b9c6c297ce..dd4c2d8ce21 100644
--- a/packages/lambda/src/api/render-still-on-lambda.ts
+++ b/packages/lambda/src/api/render-still-on-lambda.ts
@@ -5,6 +5,7 @@ import type {
 } from '@remotion/renderer';
 import type {BrowserSafeApis} from '@remotion/renderer/client';
 import {NoReactAPIs} from '@remotion/renderer/pure';
+import type {ReceivedAsset} from '../functions/helpers/overall-render-progress';
 import type {RenderStillLambdaResponsePayload} from '../functions/still';
 import type {AwsRegion} from '../pricing/aws-regions';
 import {callLambdaWithStreaming} from '../shared/call-lambda';
@@ -67,6 +68,7 @@ export type RenderStillOnLambdaOutput = {
 	bucketName: string;
 	renderId: string;
 	cloudWatchLogs: string;
+	receivedAssets: ReceivedAsset[];
 };
 
 const internalRenderStillOnLambda = async (
@@ -130,6 +132,7 @@ const internalRenderStillOnLambda = async (
 				renderId: res.renderId,
 				rendererFunctionName: null,
 			}),
+			receivedAssets: res.receivedAssets,
 		};
 	} catch (err) {
 		if ((err as Error).stack?.includes('UnrecognizedClientException')) {
diff --git a/packages/lambda/src/cli/commands/render/progress.ts b/packages/lambda/src/cli/commands/render/progress.ts
index 316a6a14fe4..cfd637d60fb 100644
--- a/packages/lambda/src/cli/commands/render/progress.ts
+++ b/packages/lambda/src/cli/commands/render/progress.ts
@@ -2,6 +2,7 @@ import {CliInternals} from '@remotion/cli';
 import {RenderInternals} from '@remotion/renderer';
 import {NoReactInternals} from 'remotion/no-react';
 import type {RenderProgress} from '../../../defaults';
+import type {ReceivedAsset} from '../../../functions/helpers/overall-render-progress';
 import {truthy} from '../../../shared/truthy';
 
 type LambdaInvokeProgress = {
@@ -200,8 +201,7 @@ const makeTopRow = (overall: RenderProgress) => {
 	return CliInternals.chalk.gray(str);
 };
 
-const makeArtifactProgress = (progress: RenderProgress) => {
-	const {artifactProgress} = progress;
+export const makeArtifactProgress = (artifactProgress: ReceivedAsset[]) => {
 	if (artifactProgress.length === 0) {
 		return null;
 	}
@@ -209,12 +209,12 @@ const makeArtifactProgress = (progress: RenderProgress) => {
 	return artifactProgress
 		.map((artifact) => {
 			return [
-				CliInternals.chalk.blue('+'.padEnd(CliInternals.LABEL_WIDTH)),
+				CliInternals.chalk.blue('+ S3'.padEnd(CliInternals.LABEL_WIDTH)),
 				CliInternals.chalk.blue(
 					CliInternals.makeHyperlink({
 						url: artifact.s3Url,
 						fallback: artifact.filename,
-						text: artifact.filename,
+						text: artifact.s3Key,
 					}),
 				),
 				CliInternals.chalk.gray(
@@ -239,8 +239,8 @@ export const makeProgressString = ({
 		...makeInvokeProgress(overall),
 		...makeRenderProgress(overall),
 		makeCombinationProgress(overall),
-		makeArtifactProgress(overall),
 		downloadInfo ? makeDownloadProgress(downloadInfo) : null,
+		makeArtifactProgress(overall.artifactProgress),
 	]
 		.filter(NoReactInternals.truthy)
 		.join('\n');
diff --git a/packages/lambda/src/cli/commands/render/render.ts b/packages/lambda/src/cli/commands/render/render.ts
index 332cd2c4768..a193a4dfe2a 100644
--- a/packages/lambda/src/cli/commands/render/render.ts
+++ b/packages/lambda/src/cli/commands/render/render.ts
@@ -3,6 +3,7 @@ import {ConfigInternals} from '@remotion/cli/config';
 import type {ChromiumOptions, LogLevel} from '@remotion/renderer';
 import {RenderInternals} from '@remotion/renderer';
 import {BrowserSafeApis} from '@remotion/renderer/client';
+import path from 'path';
 import {NoReactInternals} from 'remotion/no-react';
 import {downloadMedia} from '../../../api/download-media';
 import {getRenderProgress} from '../../../api/get-render-progress';
@@ -428,16 +429,11 @@ export const renderCommand = async (
 		);
 
 		if (newStatus.done) {
-			progressBar.update(
-				makeProgressString({
-					downloadInfo: null,
-					overall: newStatus,
-				}),
-				false,
-			);
+			let downloadOrNothing;
+
 			if (downloadName) {
 				const downloadStart = Date.now();
-				const {outputPath, sizeInBytes} = await downloadMedia({
+				const download = await downloadMedia({
 					bucketName: res.bucketName,
 					outPath: downloadName,
 					region: getAwsRegion(),
@@ -457,29 +453,55 @@ export const renderCommand = async (
 						);
 					},
 				});
+				downloadOrNothing = download;
 				progressBar.update(
 					makeProgressString({
 						downloadInfo: {
 							doneIn: Date.now() - downloadStart,
-							downloaded: sizeInBytes,
-							totalSize: sizeInBytes,
+							downloaded: download.sizeInBytes,
+							totalSize: download.sizeInBytes,
 						},
 						overall: newStatus,
 					}),
 					false,
 				);
-				Log.info({indent: false, logLevel});
-				Log.info({indent: false, logLevel});
+			}
+
+			Log.info({indent: false, logLevel});
+			Log.info(
+				{indent: false, logLevel},
+				CliInternals.chalk.blue('+ S3 '.padEnd(CliInternals.LABEL_WIDTH)),
+				CliInternals.chalk.blue(
+					CliInternals.makeHyperlink({
+						fallback: newStatus.outputFile as string,
+						text: newStatus.outKey as string,
+						url: newStatus.outputFile as string,
+					}),
+				),
+				CliInternals.chalk.gray(
+					CliInternals.formatBytes(newStatus.outputSizeInBytes as number),
+				),
+			);
+
+			if (downloadOrNothing) {
+				const relativeOutputPath = path.relative(
+					process.cwd(),
+					downloadOrNothing.outputPath,
+				);
 				Log.info(
 					{indent: false, logLevel},
-					'Done!',
-					outputPath,
-					CliInternals.formatBytes(sizeInBytes),
+					CliInternals.chalk.blue('↓'.padEnd(CliInternals.LABEL_WIDTH)),
+					CliInternals.chalk.blue(
+						CliInternals.makeHyperlink({
+							url: `file://${downloadOrNothing.outputPath}`,
+							text: relativeOutputPath,
+							fallback: downloadOrNothing.outputPath,
+						}),
+					),
+					CliInternals.chalk.gray(
+						CliInternals.formatBytes(downloadOrNothing.sizeInBytes),
+					),
 				);
-			} else {
-				Log.info({indent: false, logLevel});
-				Log.info({indent: false, logLevel});
-				Log.info({indent: false, logLevel}, 'Done! ' + newStatus.outputFile);
 			}
 
 			Log.info(
diff --git a/packages/lambda/src/cli/commands/still.ts b/packages/lambda/src/cli/commands/still.ts
index 328e99cad1e..4560da05c77 100644
--- a/packages/lambda/src/cli/commands/still.ts
+++ b/packages/lambda/src/cli/commands/still.ts
@@ -3,6 +3,7 @@ import {ConfigInternals} from '@remotion/cli/config';
 import type {ChromiumOptions, LogLevel} from '@remotion/renderer';
 import {RenderInternals} from '@remotion/renderer';
 import {BrowserSafeApis} from '@remotion/renderer/client';
+import path from 'path';
 import {NoReactInternals} from 'remotion/no-react';
 import {downloadMedia} from '../../api/download-media';
 import {renderStillOnLambda} from '../../api/render-still-on-lambda';
@@ -19,6 +20,7 @@ import {getAwsRegion} from '../get-aws-region';
 import {findFunctionName} from '../helpers/find-function-name';
 import {quit} from '../helpers/quit';
 import {Log} from '../log';
+import {makeArtifactProgress} from './render/progress';
 
 const {
 	offthreadVideoCacheSizeInBytesOption,
@@ -235,18 +237,46 @@ export const stillCommand = async (
 			);
 			Log.verbose(
 				{indent: false, logLevel},
-				`CloudWatch logs (if enabled): ${cloudWatchLogs}`,
+				`${CliInternals.makeHyperlink({
+					text: 'CloudWatch Logs',
+					url: cloudWatchLogs,
+					fallback: `CloudWatch Logs: ${cloudWatchLogs}`,
+				})} (if enabled)`,
 			);
 			Log.verbose(
 				{indent: false, logLevel},
-				`Lambda Insights (if enabled): ${lambdaInsightsUrl}`,
+				`${CliInternals.makeHyperlink({
+					text: 'Lambda Insights',
+					url: lambdaInsightsUrl,
+					fallback: `Lambda Insights: ${lambdaInsightsUrl}`,
+				})} (if enabled)`,
 			);
 		},
 		deleteAfter,
 	});
 
+	Log.info(
+		{
+			indent: false,
+			logLevel,
+		},
+		makeArtifactProgress(res.receivedAssets),
+	);
+
+	Log.info(
+		{indent: false, logLevel},
+		chalk.blue('+ S3'.padEnd(CliInternals.LABEL_WIDTH)),
+		chalk.blue(
+			CliInternals.makeHyperlink({
+				fallback: res.url,
+				url: res.url,
+				text: res.outKey,
+			}),
+		),
+		chalk.gray(formatBytes(res.sizeInBytes)),
+	);
+
 	if (downloadName) {
-		Log.info({indent: false, logLevel}, 'Finished rendering. Downloading...');
 		const {outputPath, sizeInBytes} = await downloadMedia({
 			bucketName: res.bucketName,
 			outPath: downloadName,
@@ -254,15 +284,18 @@ export const stillCommand = async (
 			renderId: res.renderId,
 			logLevel,
 		});
+		const relativePath = path.relative(process.cwd(), outputPath);
 		Log.info(
 			{indent: false, logLevel},
-			'Done!',
-			outputPath,
-			formatBytes(sizeInBytes),
+			chalk.blue('↓'.padEnd(CliInternals.LABEL_WIDTH)),
+			chalk.blue(
+				CliInternals.makeHyperlink({
+					url: 'file://' + outputPath,
+					text: relativePath,
+					fallback: outputPath,
+				}),
+			),
+			chalk.gray(formatBytes(sizeInBytes)),
 		);
-	} else {
-		Log.info({indent: false, logLevel}, `Finished still!`);
-		Log.info({indent: false, logLevel});
-		Log.info({indent: false, logLevel}, res.url);
 	}
 };
diff --git a/packages/lambda/src/functions/helpers/overall-render-progress.ts b/packages/lambda/src/functions/helpers/overall-render-progress.ts
index 68a793d98c2..710ad3c7b44 100644
--- a/packages/lambda/src/functions/helpers/overall-render-progress.ts
+++ b/packages/lambda/src/functions/helpers/overall-render-progress.ts
@@ -33,6 +33,7 @@ export type ReceivedAsset = {
 	filename: string;
 	sizeInBytes: number;
 	s3Url: string;
+	s3Key: string;
 };
 
 export type OverallProgressHelper = {
diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts
index 2a4932f1d1a..9c15005c11c 100644
--- a/packages/lambda/src/functions/launch.ts
+++ b/packages/lambda/src/functions/launch.ts
@@ -371,6 +371,7 @@ const innerLaunchHandler = async ({
 			filename: artifact.filename,
 			sizeInBytes: artifact.content.length,
 			s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`,
+			s3Key,
 		});
 
 		const start = Date.now();
diff --git a/packages/lambda/src/functions/still.ts b/packages/lambda/src/functions/still.ts
index c741ed64e49..49219e0a702 100644
--- a/packages/lambda/src/functions/still.ts
+++ b/packages/lambda/src/functions/still.ts
@@ -214,6 +214,7 @@ const innerStillHandler = async ({
 			filename: artifact.filename,
 			sizeInBytes: artifact.content.length,
 			s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`,
+			s3Key,
 		});
 
 		const startTime = Date.now();

From 1b95674ec6b6572be708d9ae4621c3cd745182ca Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 19 Jun 2024 20:11:17 +0200
Subject: [PATCH 20/36] Update still.ts

---
 packages/lambda/src/cli/commands/still.ts | 28 ++++++++++++++++++-----
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/packages/lambda/src/cli/commands/still.ts b/packages/lambda/src/cli/commands/still.ts
index 4560da05c77..9c17a9ba290 100644
--- a/packages/lambda/src/cli/commands/still.ts
+++ b/packages/lambda/src/cli/commands/still.ts
@@ -12,6 +12,7 @@ import {
 	DEFAULT_MAX_RETRIES,
 	DEFAULT_OUTPUT_PRIVACY,
 } from '../../shared/constants';
+import {getS3RenderUrl} from '../../shared/get-aws-urls';
 import {validatePrivacy} from '../../shared/validate-privacy';
 import {validateMaxRetries} from '../../shared/validate-retries';
 import {validateServeUrl} from '../../shared/validate-serveurl';
@@ -199,7 +200,14 @@ export const stillCommand = async (
 	Log.info(
 		{indent: false, logLevel},
 		CliInternals.chalk.gray(
-			`functionName = ${functionName}, imageFormat = ${imageFormat} (${imageFormatReason})`,
+			`Function: ${CliInternals.makeHyperlink({text: functionName, fallback: functionName, url: `https://${getAwsRegion()}.console.aws.amazon.com/lambda/home#/functions/${functionName}?tab=code`})}`,
+		),
+	);
+
+	Log.info(
+		{indent: false, logLevel},
+		CliInternals.chalk.gray(
+			`Image Format = ${imageFormat} (${imageFormatReason})`,
 		),
 	);
 
@@ -230,11 +238,7 @@ export const stillCommand = async (
 		scale,
 		forceHeight: height,
 		forceWidth: width,
-		onInit: ({cloudWatchLogs, renderId, lambdaInsightsUrl}) => {
-			Log.info(
-				{indent: false, logLevel},
-				chalk.gray(`Render invoked with ID = ${renderId}`),
-			);
+		onInit: ({cloudWatchLogs, lambdaInsightsUrl}) => {
 			Log.verbose(
 				{indent: false, logLevel},
 				`${CliInternals.makeHyperlink({
@@ -254,6 +258,18 @@ export const stillCommand = async (
 		},
 		deleteAfter,
 	});
+	Log.info(
+		{indent: false, logLevel},
+		CliInternals.chalk.gray(
+			`Render ID: ${CliInternals.makeHyperlink({text: res.renderId, fallback: res.renderId, url: getS3RenderUrl({bucketName: res.bucketName, renderId: res.renderId, region: getAwsRegion()})})}`,
+		),
+	);
+	Log.info(
+		{indent: false, logLevel},
+		CliInternals.chalk.gray(
+			`Bucket: ${CliInternals.makeHyperlink({text: res.bucketName, fallback: res.bucketName, url: `https://${getAwsRegion()}.console.aws.amazon.com/s3/buckets/${res.bucketName}/?region=${getAwsRegion()}`})}`,
+		),
+	);
 
 	Log.info(
 		{

From 85d1531e6e3a0b71524c90db4a2db3de31fd6624 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Wed, 19 Jun 2024 20:14:39 +0200
Subject: [PATCH 21/36] Update render.ts

---
 packages/lambda/src/cli/commands/render/render.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/packages/lambda/src/cli/commands/render/render.ts b/packages/lambda/src/cli/commands/render/render.ts
index a193a4dfe2a..77a4be8be9a 100644
--- a/packages/lambda/src/cli/commands/render/render.ts
+++ b/packages/lambda/src/cli/commands/render/render.ts
@@ -504,6 +504,7 @@ export const renderCommand = async (
 				);
 			}
 
+			Log.info({indent: false, logLevel});
 			Log.info(
 				{indent: false, logLevel},
 				[

From 8d2cc13f99bc96946b1998a5cf9b4d5a4d6003ba Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 11:40:11 +0200
Subject: [PATCH 22/36] update documentation

---
 packages/docs/docs/artifact.mdx               |    5 +-
 packages/docs/docs/artifacts.mdx              |  193 +
 packages/docs/sidebars.js                     |    1 +
 packages/docs/src/data/articles.ts            | 5087 +++++++++--------
 .../generated/articles-docs-artifacts.png     |  Bin 0 -> 37466 bytes
 .../lambda/src/api/render-still-on-lambda.ts  |    6 +-
 .../src/cli/commands/render/progress.ts       |    6 +-
 packages/lambda/src/cli/commands/still.ts     |    2 +-
 .../helpers/create-post-render-data.ts        |    2 +-
 .../src/functions/helpers/get-progress.ts     |    4 +-
 .../helpers/overall-render-progress.ts        |   18 +-
 packages/lambda/src/functions/launch.ts       |    4 +-
 packages/lambda/src/functions/still.ts        |   12 +-
 packages/lambda/src/shared/constants.ts       |    6 +-
 packages/studio-shared/src/index.ts           |    2 +-
 packages/studio-shared/src/render-job.ts      |    4 +-
 16 files changed, 2777 insertions(+), 2575 deletions(-)
 create mode 100644 packages/docs/docs/artifacts.mdx
 create mode 100644 packages/docs/static/generated/articles-docs-artifacts.png

diff --git a/packages/docs/docs/artifact.mdx b/packages/docs/docs/artifact.mdx
index 146c33e5068..4193d3e0e4a 100644
--- a/packages/docs/docs/artifact.mdx
+++ b/packages/docs/docs/artifact.mdx
@@ -6,7 +6,7 @@ crumb: API
 
 # `<Artifact>`<AvailableFrom v="4.0.176"/>
 
-By rendering an `<Artifact>` tag in your Remotion markup, an extra file will get emitted during rendering.
+By rendering an `<Artifact>` tag in your Remotion markup, [an extra file will get emitted during rendering](/docs/emitting-artifacts).
 
 ```tsx twoslash title="MyComp.tsx"
 import { Artifact, useCurrentFrame } from "remotion";
@@ -55,8 +55,9 @@ Must match the regex `/^([0-9a-zA-Z-!_.*'()/:&$@=;+,?]+)/g`.
 
 ### `content`
 
-A string or `UInt8Array` that is the content of the file that will be emitted.
+A `string` or `Uint8Array` that is the content of the file that will be emitted. Don't consider an `Uint8Array` to be faster, because it needs to be serialized.
 
 ## See also
 
+- [Emitting Artifacts](/docs/artifacts)
 - [Source code for this component](https://github.com/remotion-dev/remotion/blob/main/packages/core/src/components/Artifact.tsx)
diff --git a/packages/docs/docs/artifacts.mdx b/packages/docs/docs/artifacts.mdx
new file mode 100644
index 00000000000..619223cf28e
--- /dev/null
+++ b/packages/docs/docs/artifacts.mdx
@@ -0,0 +1,193 @@
+---
+image: /generated/articles-docs-artifacts.png
+title: Emitting Artifacts
+id: artifacts
+crumb: "Techniques"
+---
+
+# Emitting Artifacts<AvailableFrom v="4.0.176" />
+
+Sometimes you wish to generate additional files when rendering your video. For example:
+
+- A `.srt` subtitle file
+- A `.txt` containing chapters of the video
+- A `CREDITS` file for the assets used in the video
+- Debug information from the render.
+
+You can use the [`<Artifact>`](/docs/artifact) component to emit arbitrary files from your video.
+
+:::note
+Emitting artifacts is not currently supported by `@remotion/cloudrun`.
+:::
+
+## Example
+
+```tsx twoslash title="MyComp.tsx"
+// @filename: subtitles.tsx
+
+export const generateSubtitles = () => {
+  return ``;
+};
+// @filename: MyComp.tsx
+// ---cut---
+import React from "react";
+import { Artifact, useCurrentFrame } from "remotion";
+import { generateSubtitles } from "./subtitles";
+
+export const MyComp: React.FC = () => {
+  const frame = useCurrentFrame();
+  return (
+    <>
+      {frame === 0 ? (
+        <Artifact filename="captions.srt" content={generateSubtitles()} />
+      ) : null}
+    </>
+  );
+};
+```
+
+## Rules of artifacts
+
+<Step>1</Step> The asset should only be rendered for one single frame of the video.
+Otherwise, the asset will get emitted multiple times. <br />
+
+<Step>2</Step> It is possible to emit multiple assets, but they may not have the
+same filename.
+<br />
+
+<Step>3</Step> For the <code>content</code> prop it is possible to pass a <code>
+  string
+</code>
+, or a <code>Uint8Array</code> for binary data. Passing an <code>
+  Uint8Array
+</code> should not be considered faster due to it having to be serialized.
+<br />
+
+## Receiving artifacts
+
+### In the CLI or Studio
+
+Artifacts get saved to `out/[composition-id]/[filename]` when rendering a video.
+
+### Using `renderMedia()`, `renderStill()` or `renderFrames()`
+
+Use the [`onArtifact`](/docs/renderer/render-media#onartifact) callback to receive the artifacts.
+
+```tsx twoslash title="render.mjs"
+// @module: es2022
+// @target: es2017
+import type { VideoConfig } from "remotion";
+import fs from "fs";
+
+const composition: VideoConfig = {
+  width: 100,
+  height: 100,
+  fps: 30,
+  defaultProps: {},
+  props: {},
+  defaultCodec: null,
+  id: "hi",
+  durationInFrames: 100,
+};
+const serveUrl = "http://localhost:8080";
+const inputProps = {};
+// ---cut---
+import { renderMedia, OnArtifact } from "@remotion/renderer";
+
+const onArtifact: OnArtifact = (artifact) => {
+  console.log(artifact.filename); // string
+  console.log(artifact.content); // string | Uint8Array
+  console.log(artifact.frame); // number, frame in the composition which emitted this
+
+  // Example action: Write the artifact to disk
+  fs.writeFileSync(artifact.filename, artifact.content);
+};
+
+await renderMedia({
+  composition,
+  serveUrl,
+  onArtifact,
+  codec: "h264",
+  inputProps,
+});
+```
+
+### Using the Remotion Lambda CLI
+
+When using [`npx remotion lambda render`](/docs/lambda/cli/render) or [`npx remotion lambda still`](/docs/lambda/cli/still), artifacts get saved to the S3 bucket under the key `renders/[render-id]/artifacts/[filename]`.
+
+They will get logged to the console and you can click them to download them.  
+The `--privacy` option also applies to artifacts.
+
+### Using `renderMediaOnLambda()`
+
+When using [`renderMediaOnLambda()`](/docs/lambda/rendermediaonlambda), artifacts get saved to the S3 bucket under the key `renders/[render-id]/artifacts/[filename]`.
+
+You can obtain a list of currently received assets from [`getRenderProgress()`](/docs/lambda/getrenderprogress#artifacts).
+
+```tsx twoslash title="progress.ts"
+// @module: es2022
+// @target: es2017
+import { getRenderProgress } from "@remotion/lambda/client";
+
+const renderProgress = await getRenderProgress({
+  renderId: "hi",
+  functionName: "hi",
+  bucketName: "hi",
+  region: "eu-central-1",
+});
+
+for (const artifact of renderProgress.artifacts) {
+  console.log(artifact.filename); // "hello-world.txt"
+  console.log(artifact.sizeInBytes); // 12
+  console.log(artifact.s3Url); // "https://s3.eu-central-1.amazonaws.com/remotion-lambda-abcdef/renders/abcdef/artifacts/hello-world.txt"
+  console.log(artifact.s3Key); // "renders/abcdef/artifacts/hello-world.txt"
+}
+```
+
+### Using `renderStillOnLambda()`
+
+When using [`renderStillOnLambda()`](/docs/lambda/renderstillonlambda), artifacts get saved to the S3 bucket under the key `renders/[render-id]/artifacts/[filename]`.
+
+You can obtain a list of received assets from [`artifacts`](/docs/lambda/renderstillonlambda#artifacts) field of `renderStillOnLambda()`.
+
+```tsx twoslash title="still.ts"
+const serveUrl = "http://localhost:8080";
+const inputProps = {};
+const functionName = "hi";
+const composition = "hi";
+const privacy = "public" as const;
+const imageFormat = "png" as const;
+const region = "eu-central-1" as const;
+
+// ---cut---
+// @module: es2022
+// @target: es2017
+import { renderStillOnLambda } from "@remotion/lambda/client";
+
+const stillResponse = await renderStillOnLambda({
+  functionName,
+  region,
+  serveUrl,
+  composition,
+  inputProps,
+  imageFormat,
+  privacy,
+});
+
+for (const artifact of stillResponse.artifacts) {
+  console.log(artifact.filename); // "hello-world.txt"
+  console.log(artifact.sizeInBytes); // 12
+  console.log(artifact.s3Url); // "https://s3.eu-central-1.amazonaws.com/remotion-lambda-abcdef/renders/abcdef/artifacts/hello-world.txt"
+  console.log(artifact.s3Key); // "renders/abcdef/artifacts/hello-world.txt"
+}
+```
+
+### Using Cloud Run
+
+In the Cloud Run Alpha, emitting artifacts is not supported and will throw an error.  
+We plan on revising Cloud Run to use the same runtime as Lambda in the future and will bring this feature along.
+
+## See also
+
+- [`<Artifact>`](/docs/artifact)
diff --git a/packages/docs/sidebars.js b/packages/docs/sidebars.js
index 3f56d1f2350..a7e5b2514be 100644
--- a/packages/docs/sidebars.js
+++ b/packages/docs/sidebars.js
@@ -630,6 +630,7 @@ module.exports = {
         "render-all",
         "miscellaneous/video-formats",
         "video-vs-offthreadvideo",
+        "artifacts",
       ],
     },
     {
diff --git a/packages/docs/src/data/articles.ts b/packages/docs/src/data/articles.ts
index 42e185e56a3..bbfc3875903 100644
--- a/packages/docs/src/data/articles.ts
+++ b/packages/docs/src/data/articles.ts
@@ -1,80 +1,73 @@
 export const articles = [
   {
-    id: "figma",
-    title: "Import from Figma",
-    relativePath: "docs/figma.mdx",
-    compId: "articles-docs-figma",
-    crumb: "The best of both",
+    id: "2-0-migration",
+    title: "v2.0 Migration",
+    relativePath: "docs/2-0-migration.mdx",
+    compId: "articles-docs-2-0-migration",
+    crumb: "Version Upgrade",
   },
   {
-    id: "env-variables",
-    title: "Environment variables",
-    relativePath: "docs/env-variables.mdx",
-    compId: "articles-docs-env-variables",
-    crumb: "How To",
+    id: "3-0-migration",
+    title: "v3.0 Migration",
+    relativePath: "docs/3-0-migration.mdx",
+    compId: "articles-docs-3-0-migration",
+    crumb: "Version Upgrade",
   },
   {
-    id: "skia",
-    title: "@remotion/skia",
-    relativePath: "docs/skia/skia.mdx",
-    compId: "articles-docs-skia-skia",
-    crumb: null,
+    id: "4-0-alpha",
+    title: "v4.0 Alpha",
+    relativePath: "docs/4-0-alpha.mdx",
+    compId: "articles-docs-4-0-alpha",
+    crumb: "Version Upgrade",
   },
   {
-    id: "skia/enable-skia",
-    title: "enableSkia()",
-    relativePath: "docs/skia/enable-skia.mdx",
-    compId: "articles-docs-skia-enable-skia",
-    crumb: "@remotion/skia",
+    id: "4-0-migration",
+    title: "v4.0 Migration",
+    relativePath: "docs/4-0-migration.mdx",
+    compId: "articles-docs-4-0-migration",
+    crumb: "Version Upgrade",
   },
   {
-    id: "skia/skia-canvas",
-    title: "<SkiaCanvas />",
-    relativePath: "docs/skia/skia-canvas.mdx",
-    compId: "articles-docs-skia-skia-canvas",
-    crumb: "@remotion/skia",
+    id: "5-0-migration",
+    title: "v5.0 Migration",
+    relativePath: "docs/5-0-migration.mdx",
+    compId: "articles-docs-5-0-migration",
+    crumb: "Version Upgrade",
   },
   {
-    id: "spring",
-    title: "spring()",
-    relativePath: "docs/spring.mdx",
-    compId: "articles-docs-spring",
+    id: "absolute-fill",
+    title: "<AbsoluteFill>",
+    relativePath: "docs/absolute-fill.mdx",
+    compId: "articles-docs-absolute-fill",
     crumb: "API",
   },
   {
-    id: "audio-visualization",
-    title: "Audio visualization",
-    relativePath: "docs/audio-visualization.mdx",
-    compId: "articles-docs-audio-visualization",
-    crumb: "Techniques",
-  },
-  {
-    id: "react-native",
-    title: "React Native",
-    relativePath: "docs/react-native.mdx",
-    compId: "articles-docs-react-native",
-    crumb: null,
+    id: "acknowledgements",
+    title: "Acknowledgements",
+    relativePath: "docs/acknowledgements.mdx",
+    compId: "articles-docs-acknowledgements",
+    crumb: "Credits",
   },
   {
-    id: "use-current-scale",
-    title: "useCurrentScale()",
-    relativePath: "docs/use-current-scale.mdx",
-    compId: "articles-docs-use-current-scale",
-    crumb: "API",
+    id: "after-effects",
+    title: "Import from After Effects",
+    relativePath: "docs/after-effects.mdx",
+    compId: "articles-docs-after-effects",
+    crumb: "Integrations",
   },
   {
-    id: "config",
-    title: "Configuration file",
-    relativePath: "docs/config.mdx",
-    compId: "articles-docs-config",
-    crumb: "remotion.config.ts",
+    id: "animating-properties",
+    title: "Animating properties",
+    relativePath: "docs/animating-properties.mdx",
+    compId: "articles-docs-animating-properties",
+    crumb: "The basics",
   },
   {
-    id: "make-transform",
-    title: "makeTransform()",
-    relativePath: "docs/animation-utils/make-transform.mdx",
-    compId: "articles-docs-animation-utils-make-transform",
-    crumb: "@remotion/animation-utils",
+    id: "animation-math",
+    title: "Animation math",
+    relativePath: "docs/animation-math.mdx",
+    compId: "articles-docs-animation-math",
+    crumb: "Techniques",
   },
   {
     id: "animation-utils/index",
@@ -91,25 +84,60 @@ export const articles = [
     crumb: "@remotion/animation-utils",
   },
   {
-    id: "4-0-alpha",
-    title: "v4.0 Alpha",
-    relativePath: "docs/4-0-alpha.mdx",
-    compId: "articles-docs-4-0-alpha",
-    crumb: "Version Upgrade",
+    id: "make-transform",
+    title: "makeTransform()",
+    relativePath: "docs/animation-utils/make-transform.mdx",
+    compId: "articles-docs-animation-utils-make-transform",
+    crumb: "@remotion/animation-utils",
   },
   {
-    id: "use-video-config",
-    title: "useVideoConfig()",
-    relativePath: "docs/use-video-config.mdx",
-    compId: "articles-docs-use-video-config",
+    id: "api",
+    title: "API overview",
+    relativePath: "docs/api.mdx",
+    compId: "articles-docs-api",
+    crumb: null,
+  },
+  {
+    id: "artifact",
+    title: "<Artifact>",
+    relativePath: "docs/artifact.mdx",
+    compId: "articles-docs-artifact",
     crumb: "API",
   },
   {
-    id: "legacy-babel",
-    title: "Using legacy Babel transpilation",
-    relativePath: "docs/legacy-babel-loader.mdx",
-    compId: "articles-docs-legacy-babel-loader",
-    crumb: "How To",
+    id: "artifacts",
+    title: "Emitting Artifacts",
+    relativePath: "docs/artifacts.mdx",
+    compId: "articles-docs-artifacts",
+    crumb: "Techniques",
+  },
+  {
+    id: "get-help",
+    title: "Get help",
+    relativePath: "docs/ask-for-help.mdx",
+    compId: "articles-docs-ask-for-help",
+    crumb: null,
+  },
+  {
+    id: "audio-visualization",
+    title: "Audio visualization",
+    relativePath: "docs/audio-visualization.mdx",
+    compId: "articles-docs-audio-visualization",
+    crumb: "Techniques",
+  },
+  {
+    id: "audio",
+    title: "<Audio>",
+    relativePath: "docs/audio.mdx",
+    compId: "articles-docs-audio",
+    crumb: "How to",
+  },
+  {
+    id: "audio-buffer-to-data-url",
+    title: "audioBufferToDataUrl()",
+    relativePath: "docs/audiobuffertodataurl.mdx",
+    compId: "articles-docs-audiobuffertodataurl",
+    crumb: "@remotion/media-utils",
   },
   {
     id: "brownfield",
@@ -119,487 +147,508 @@ export const articles = [
     crumb: "Brownfield integration",
   },
   {
-    id: "studio/studio",
-    title: "Starting the Studio",
-    relativePath: "docs/studio/studio.mdx",
-    compId: "articles-docs-studio-studio",
-    crumb: "Timeline basics",
+    id: "buffer-state",
+    title: "Display a buffer state",
+    relativePath: "docs/buffer-state.mdx",
+    compId: "articles-docs-buffer-state",
+    crumb: "Building video apps",
   },
   {
-    id: "studio/quick-switcher",
-    title: "Quick Switcher",
-    relativePath: "docs/studio/quick-switcher.mdx",
-    compId: "articles-docs-studio-quick-switcher",
-    crumb: "Remotion Studio",
+    id: "bun",
+    title: "Bun support",
+    relativePath: "docs/bun.mdx",
+    compId: "articles-docs-bun",
+    crumb: "bun bun bun bun bun",
   },
   {
-    id: "studio/restart-studio",
-    title: "restartStudio()",
-    relativePath: "docs/studio/restart-studio.mdx",
-    compId: "articles-docs-studio-restart-studio",
-    crumb: "@remotion/studio",
+    id: "bundle",
+    title: "bundle()",
+    relativePath: "docs/bundle.mdx",
+    compId: "articles-docs-bundle",
+    crumb: "@remotion/bundler",
   },
   {
-    id: "studio/focus-default-props-path",
-    title: "focusDefaultPropsPath()",
-    relativePath: "docs/studio/focus-default-props-path.mdx",
-    compId: "articles-docs-studio-focus-default-props-path",
-    crumb: "@remotion/studio",
+    id: "bundler",
+    title: "@remotion/bundler",
+    relativePath: "docs/bundler.mdx",
+    compId: "articles-docs-bundler",
+    crumb: null,
   },
   {
-    id: "studio/save-default-props",
-    title: "saveDefaultProps()",
-    relativePath: "docs/studio/save-default-props.mdx",
-    compId: "articles-docs-studio-save-default-props",
-    crumb: "@remotion/studio",
+    id: "calculate-metadata",
+    title: "calculateMetadata()",
+    relativePath: "docs/calculate-metadata.mdx",
+    compId: "articles-docs-calculate-metadata",
+    crumb: "API",
   },
   {
-    id: "studio/deploy-static",
-    title: "Deploy Remotion Studio as static site",
-    relativePath: "docs/studio/deploy-static.mdx",
-    compId: "articles-docs-studio-deploy-static",
-    crumb: "Remotion Studio",
+    id: "cancel-render",
+    title: "cancelRender()",
+    relativePath: "docs/cancel-render.mdx",
+    compId: "articles-docs-cancel-render",
+    crumb: "How to",
   },
   {
-    id: "studio/watch-public-folder",
-    title: "watchPublicFolder()",
-    relativePath: "docs/studio/watch-public-folder.mdx",
-    compId: "articles-docs-studio-watch-public-folder",
-    crumb: "@remotion/studio",
+    id: "chromium-flags",
+    title: "Chromium flags",
+    relativePath: "docs/chromium-flags.mdx",
+    compId: "articles-docs-chromium-flags",
+    crumb: "Tweaks",
   },
   {
-    id: "studio/deploy-server",
-    title: "Deploy the Remotion Studio on a VPS",
-    relativePath: "docs/studio/deploy-server.mdx",
-    compId: "articles-docs-studio-deploy-server",
-    crumb: "Remotion Studio",
+    id: "cli/benchmark",
+    title: "npx remotion benchmark",
+    relativePath: "docs/cli/benchmark.mdx",
+    compId: "articles-docs-cli-benchmark",
+    crumb: "CLI Reference",
   },
   {
-    id: "studio/get-static-files",
-    title: "getStaticFiles()",
-    relativePath: "docs/studio/get-static-files.mdx",
-    compId: "articles-docs-studio-get-static-files",
-    crumb: "@remotion/studio",
+    id: "cli/browser/ensure",
+    title: "npx remotion browser ensure",
+    relativePath: "docs/cli/browser/ensure.mdx",
+    compId: "articles-docs-cli-browser-ensure",
+    crumb: "@remotion/cli",
   },
   {
-    id: "studio/watch-static-file",
-    title: "watchStaticFile()",
-    relativePath: "docs/studio/watch-static-file.mdx",
-    compId: "articles-docs-studio-watch-static-file",
-    crumb: "@remotion/studio",
+    id: "cli/browser/index",
+    title: "npx remotion browser",
+    relativePath: "docs/cli/browser/index.mdx",
+    compId: "articles-docs-cli-browser-index",
+    crumb: "@remotion/cli",
   },
   {
-    id: "studio/shortcuts",
-    title: "Keyboard navigation",
-    relativePath: "docs/studio/shortcuts.mdx",
-    compId: "articles-docs-studio-shortcuts",
-    crumb: "Remotion Studio",
+    id: "cli/bundle",
+    title: "npx remotion bundle",
+    relativePath: "docs/cli/bundle.mdx",
+    compId: "articles-docs-cli-bundle",
+    crumb: "CLI Reference",
   },
   {
-    id: "studio/api",
-    title: "@remotion/studio",
-    relativePath: "docs/studio/api.mdx",
-    compId: "articles-docs-studio-api",
+    id: "cli",
+    title: "Command line reference",
+    relativePath: "docs/cli/cli.mdx",
+    compId: "articles-docs-cli-cli",
     crumb: null,
   },
   {
-    id: "studio/reevaluate-composition",
-    title: "reevaluateComposition()",
-    relativePath: "docs/studio/reevaluate-composition.mdx",
-    compId: "articles-docs-studio-reevaluate-composition",
-    crumb: "@remotion/studio",
+    id: "cli/compositions",
+    title: "npx remotion compositions",
+    relativePath: "docs/cli/compositions.mdx",
+    compId: "articles-docs-cli-compositions",
+    crumb: "CLI Reference",
   },
   {
-    id: "studio/write-static-file",
-    title: "writeStaticFile()",
-    relativePath: "docs/studio/write-static-file.mdx",
-    compId: "articles-docs-studio-write-static-file",
-    crumb: "@remotion/studio",
+    id: "ffmpeg",
+    title: "npx remotion ffmpeg",
+    relativePath: "docs/cli/ffmpeg.mdx",
+    compId: "articles-docs-cli-ffmpeg",
+    crumb: "@remotion/cli",
   },
   {
-    id: "studio/update-default-props",
-    title: "updateDefaultProps()",
-    relativePath: "docs/studio/update-default-props.mdx",
-    compId: "articles-docs-studio-update-default-props",
-    crumb: "@remotion/studio",
+    id: "ffprobe",
+    title: "npx remotion ffprobe",
+    relativePath: "docs/cli/ffprobe.mdx",
+    compId: "articles-docs-cli-ffprobe",
+    crumb: "@remotion/cli",
   },
   {
-    id: "studio/delete-static-file",
-    title: "deleteStaticFile()",
-    relativePath: "docs/studio/delete-static-file.mdx",
-    compId: "articles-docs-studio-delete-static-file",
-    crumb: "@remotion/studio",
+    id: "cli/gpu",
+    title: "npx remotion gpu",
+    relativePath: "docs/cli/gpu.mdx",
+    compId: "articles-docs-cli-gpu",
+    crumb: "CLI Reference",
   },
   {
-    id: "video-uploads",
-    title: "Handling user video uploads",
-    relativePath: "docs/video-uploads.mdx",
-    compId: "articles-docs-video-uploads",
-    crumb: "Building video apps",
+    id: "cli/help",
+    title: "npx remotion help",
+    relativePath: "docs/cli/help.mdx",
+    compId: "articles-docs-cli-help",
+    crumb: "CLI Reference",
   },
   {
-    id: "bundler",
-    title: "@remotion/bundler",
-    relativePath: "docs/bundler.mdx",
-    compId: "articles-docs-bundler",
-    crumb: null,
+    id: "cli/install",
+    title: "npx remotion install",
+    relativePath: "docs/cli/install.mdx",
+    compId: "articles-docs-cli-install",
+    crumb: "CLI Reference",
   },
   {
-    id: "using-audio",
-    title: "Using audio",
-    relativePath: "docs/using-audio.mdx",
-    compId: "articles-docs-using-audio",
-    crumb: "Techniques",
+    id: "cli/render",
+    title: "npx remotion render",
+    relativePath: "docs/cli/render.mdx",
+    compId: "articles-docs-cli-render",
+    crumb: "CLI Reference",
   },
   {
-    id: "get-compositions",
-    title: "getCompositions()",
-    relativePath: "docs/renderer/get-compositions.mdx",
-    compId: "articles-docs-renderer-get-compositions",
-    crumb: "@remotion/renderer",
+    id: "cli/still",
+    title: "npx remotion still",
+    relativePath: "docs/cli/still.mdx",
+    compId: "articles-docs-cli-still",
+    crumb: "CLI Reference",
   },
   {
-    id: "make-cancel-signal",
-    title: "makeCancelSignal()",
-    relativePath: "docs/renderer/make-cancel-signal.mdx",
-    compId: "articles-docs-renderer-make-cancel-signal",
-    crumb: "@remotion/renderer",
+    id: "cli/studio",
+    title: "npx remotion studio",
+    relativePath: "docs/cli/studio.mdx",
+    compId: "articles-docs-cli-studio",
+    crumb: "CLI Reference",
   },
   {
-    id: "extract-audio",
-    title: "extractAudio()",
-    relativePath: "docs/renderer/extract-audio.mdx",
-    compId: "articles-docs-renderer-extract-audio",
-    crumb: "@remotion/renderer",
+    id: "cli/upgrade",
+    title: "npx remotion upgrade",
+    relativePath: "docs/cli/upgrade.mdx",
+    compId: "articles-docs-cli-upgrade",
+    crumb: "CLI Reference",
   },
   {
-    id: "get-video-metadata",
-    title: "getVideoMetadata()",
-    relativePath: "docs/renderer/get-video-metadata.mdx",
-    compId: "articles-docs-renderer-get-video-metadata",
-    crumb: "@remotion/renderer",
+    id: "cli/versions",
+    title: "npx remotion versions",
+    relativePath: "docs/cli/versions.mdx",
+    compId: "articles-docs-cli-versions",
+    crumb: "CLI Reference",
   },
   {
-    id: "select-composition",
-    title: "selectComposition()",
-    relativePath: "docs/renderer/select-composition.mdx",
-    compId: "articles-docs-renderer-select-composition",
-    crumb: "@remotion/renderer",
+    id: "clipper",
+    title: "<Experimental.Clipper>",
+    relativePath: "docs/clipper.mdx",
+    compId: "articles-docs-clipper",
+    crumb: "Experimental API",
   },
   {
-    id: "ensure-ffmpeg",
-    title: "ensureFfmpeg()",
-    relativePath: "docs/renderer/ensure-ffmpeg.mdx",
-    compId: "articles-docs-renderer-ensure-ffmpeg",
-    crumb: "@remotion/renderer",
+    id: "cloudrun/api",
+    title: "@remotion/cloudrun",
+    relativePath: "docs/cloudrun/api.mdx",
+    compId: "articles-docs-cloudrun-api",
+    crumb: "Render videos without servers on GCP",
   },
   {
-    id: "get-can-extract-frames-fast",
-    title: "getCanExtractFramesFast()",
-    relativePath: "docs/renderer/get-can-extract-frames-fast.mdx",
-    compId: "articles-docs-renderer-get-can-extract-frames-fast",
-    crumb: "@remotion/renderer",
+    id: "checklist",
+    title: "Production Checklist",
+    relativePath: "docs/cloudrun/checklist.mdx",
+    compId: "articles-docs-cloudrun-checklist",
+    crumb: "Cloud Run",
   },
   {
-    id: "get-silent-parts",
-    title: "getSilentParts()",
-    relativePath: "docs/renderer/get-silent-parts.mdx",
-    compId: "articles-docs-renderer-get-silent-parts",
-    crumb: "@remotion/renderer",
+    id: "permissions",
+    title: "npx remotion cloudrun permissions",
+    relativePath: "docs/cloudrun/cli/permissions.mdx",
+    compId: "articles-docs-cloudrun-cli-permissions",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "open-browser",
-    title: "openBrowser()",
-    relativePath: "docs/renderer/open-browser.mdx",
-    compId: "articles-docs-renderer-open-browser",
-    crumb: "@remotion/renderer",
+    id: "regions",
+    title: "npx remotion cloudrun regions",
+    relativePath: "docs/cloudrun/cli/regions.mdx",
+    compId: "articles-docs-cloudrun-cli-regions",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "stitch-frames-to-video",
-    title: "stitchFramesToVideo()",
-    relativePath: "docs/renderer/stitch-frames-to-video.mdx",
-    compId: "articles-docs-renderer-stitch-frames-to-video",
-    crumb: "@remotion/renderer",
+    id: "render",
+    title: "npx remotion cloudrun render",
+    relativePath: "docs/cloudrun/cli/render.mdx",
+    compId: "articles-docs-cloudrun-cli-render",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "ensure-ffprobe",
-    title: "ensureFfprobe()",
-    relativePath: "docs/renderer/ensure-ffprobe.mdx",
-    compId: "articles-docs-renderer-ensure-ffprobe",
-    crumb: "@remotion/renderer",
+    id: "services",
+    title: "npx remotion cloudrun services",
+    relativePath: "docs/cloudrun/cli/services.mdx",
+    compId: "articles-docs-cloudrun-cli-services",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "render-media",
-    title: "renderMedia()",
-    relativePath: "docs/renderer/render-media.mdx",
-    compId: "articles-docs-renderer-render-media",
-    crumb: "@remotion/renderer",
+    id: "sites",
+    title: "npx remotion cloudrun sites",
+    relativePath: "docs/cloudrun/cli/sites.mdx",
+    compId: "articles-docs-cloudrun-cli-sites",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "ensure-browser",
-    title: "ensureBrowser()",
-    relativePath: "docs/renderer/ensure-browser.mdx",
-    compId: "articles-docs-renderer-ensure-browser",
-    crumb: "@remotion/renderer",
+    id: "still",
+    title: "npx remotion cloudrun still",
+    relativePath: "docs/cloudrun/cli/still.mdx",
+    compId: "articles-docs-cloudrun-cli-still",
+    crumb: "Cloud Run CLI Reference",
   },
   {
-    id: "render-still",
-    title: "renderStill()",
-    relativePath: "docs/renderer/render-still.mdx",
-    compId: "articles-docs-renderer-render-still",
-    crumb: "@remotion/renderer",
+    id: "cli",
+    title: "@remotion/cloudrun - CLI",
+    relativePath: "docs/cloudrun/cli.mdx",
+    compId: "articles-docs-cloudrun-cli",
+    crumb: null,
   },
   {
-    id: "render-frames",
-    title: "renderFrames()",
-    relativePath: "docs/renderer/render-frames.mdx",
-    compId: "articles-docs-renderer-render-frames",
-    crumb: "@remotion/renderer",
+    id: "deleteservice",
+    title: "deleteService()",
+    relativePath: "docs/cloudrun/deleteservice.mdx",
+    compId: "articles-docs-cloudrun-deleteservice",
+    crumb: "Cloud Run API",
   },
   {
-    id: "bundle",
-    title: "bundle()",
-    relativePath: "docs/bundle.mdx",
-    compId: "articles-docs-bundle",
-    crumb: "@remotion/bundler",
+    id: "deletesite",
+    title: "deleteSite()",
+    relativePath: "docs/cloudrun/deletesite.mdx",
+    compId: "articles-docs-cloudrun-deletesite",
+    crumb: "Cloud Run API",
   },
   {
-    id: "img",
-    title: "<Img>",
-    relativePath: "docs/img.mdx",
-    compId: "articles-docs-img",
-    crumb: "API",
+    id: "deployservice",
+    title: "deployService()",
+    relativePath: "docs/cloudrun/deployservice.mdx",
+    compId: "articles-docs-cloudrun-deployservice",
+    crumb: "Cloud Run API",
   },
   {
-    id: "encoding",
-    title: "Encoding Guide",
-    relativePath: "docs/encoding.mdx",
-    compId: "articles-docs-encoding",
-    crumb: "Codecs and more",
+    id: "deploysite",
+    title: "deploySite()",
+    relativePath: "docs/cloudrun/deploysite.mdx",
+    compId: "articles-docs-cloudrun-deploysite",
+    crumb: "Cloud Run API",
   },
   {
-    id: "get-remotion-environment",
-    title: "getRemotionEnvironment()",
-    relativePath: "docs/get-remotion-environment.mdx",
-    compId: "articles-docs-get-remotion-environment",
-    crumb: "API",
+    id: "generate-env",
+    title: "Generate .env File",
+    relativePath: "docs/cloudrun/generate-env.mdx",
+    compId: "articles-docs-cloudrun-generate-env",
+    crumb: "Cloud Run",
   },
   {
-    id: "sequence",
-    title: "<Sequence>",
-    relativePath: "docs/sequence.mdx",
-    compId: "articles-docs-sequence",
-    crumb: "API",
+    id: "getserviceinfo",
+    title: "getServiceInfo()",
+    relativePath: "docs/cloudrun/getServiceinfo.mdx",
+    compId: "articles-docs-cloudrun-getServiceinfo",
+    crumb: "Cloud Run API",
   },
   {
-    id: "the-fundamentals",
-    title: "The fundamentals",
-    relativePath: "docs/the-fundamentals.mdx",
-    compId: "articles-docs-the-fundamentals",
-    crumb: "Getting started",
+    id: "getorcreatebucket",
+    title: "getOrCreateBucket()",
+    relativePath: "docs/cloudrun/getorcreatebucket.mdx",
+    compId: "articles-docs-cloudrun-getorcreatebucket",
+    crumb: "Cloud Run API",
   },
   {
-    id: "ssr",
-    title: "Server-Side Rendering",
-    relativePath: "docs/ssr.mdx",
-    compId: "articles-docs-ssr",
-    crumb: "The power of",
+    id: "getregions",
+    title: "getRegions()",
+    relativePath: "docs/cloudrun/getregions.mdx",
+    compId: "articles-docs-cloudrun-getregions",
+    crumb: "Cloud Run API",
   },
   {
-    id: "after-effects",
-    title: "Import from After Effects",
-    relativePath: "docs/after-effects.mdx",
-    compId: "articles-docs-after-effects",
-    crumb: "Integrations",
+    id: "getservices",
+    title: "getServices()",
+    relativePath: "docs/cloudrun/getservices.mdx",
+    compId: "articles-docs-cloudrun-getservices",
+    crumb: "Cloud Run API",
   },
   {
-    id: "assets",
-    title: "Importing assets",
-    relativePath: "docs/importing-assets.mdx",
-    compId: "articles-docs-importing-assets",
-    crumb: "How To",
+    id: "getsites",
+    title: "getSites()",
+    relativePath: "docs/cloudrun/getsites.mdx",
+    compId: "articles-docs-cloudrun-getsites",
+    crumb: "Cloud Run API",
   },
   {
-    id: "get-video-metadata",
-    title: "getVideoMetadata()",
-    relativePath: "docs/get-video-metadata.mdx",
-    compId: "articles-docs-get-video-metadata",
-    crumb: "@remotion/media-utils",
+    id: "instancecount",
+    title: "Instance Count",
+    relativePath: "docs/cloudrun/instancecount.mdx",
+    compId: "articles-docs-cloudrun-instancecount",
+    crumb: "Cloud Run",
   },
   {
-    id: "media-playback-error",
-    title: "Could not play video/audio with src",
-    relativePath: "docs/media-playback-error.mdx",
-    compId: "articles-docs-media-playback-error",
-    crumb: "Troubleshooting",
+    id: "cloudrun/light-client",
+    title: "Light client",
+    relativePath: "docs/cloudrun/light-client.mdx",
+    compId: "articles-docs-cloudrun-light-client",
+    crumb: "Cloud Run",
   },
   {
-    id: "staticfile-relative-paths",
-    title: "staticFile() does not support relative paths",
-    relativePath: "docs/static-file-relative-paths.mdx",
-    compId: "articles-docs-static-file-relative-paths",
-    crumb: "Troubleshooting",
+    id: "cloudrun/limits",
+    title: "Cloud Run Limits",
+    relativePath: "docs/cloudrun/limits.mdx",
+    compId: "articles-docs-cloudrun-limits",
+    crumb: "Cloud Run",
   },
   {
-    id: "interpolate",
-    title: "interpolate()",
-    relativePath: "docs/interpolate.mdx",
-    compId: "articles-docs-interpolate",
-    crumb: "API",
+    id: "cloudrun/multiple-buckets",
+    title: "Multiple buckets in Remotion Cloud Run",
+    relativePath: "docs/cloudrun/multiple-buckets.mdx",
+    compId: "articles-docs-cloudrun-multiple-buckets",
+    crumb: "@remotion/cloudrun",
   },
   {
-    id: "render-as-gif",
-    title: "Rendering GIFs",
-    relativePath: "docs/render-as-gif.mdx",
-    compId: "articles-docs-render-as-gif",
-    crumb: "Techniques",
+    id: "cloudrun/permissions",
+    title: "Permissions",
+    relativePath: "docs/cloudrun/permissions.mdx",
+    compId: "articles-docs-cloudrun-permissions",
+    crumb: "Cloud Run",
   },
   {
-    id: "webpack-dynamic-imports",
-    title: "Webpack dynamic imports",
-    relativePath: "docs/dynamic-import.mdx",
-    compId: "articles-docs-dynamic-import",
-    crumb: "Knowledge Base",
+    id: "region-selection",
+    title: "Region selection",
+    relativePath: "docs/cloudrun/region-selection.mdx",
+    compId: "articles-docs-cloudrun-region-selection",
+    crumb: "Cloud Run",
   },
   {
-    id: "terminology/studio",
-    title: "Remotion Studio",
-    relativePath: "docs/terminology/studio.mdx",
-    compId: "articles-docs-terminology-studio",
-    crumb: "Terminology",
+    id: "rendermediaoncloudrun",
+    title: "renderMediaOnCloudrun()",
+    relativePath: "docs/cloudrun/rendermediaoncloudrun.mdx",
+    compId: "articles-docs-cloudrun-rendermediaoncloudrun",
+    crumb: "Cloud Run API",
   },
   {
-    id: "terminology/bundle",
-    title: "Remotion Bundle",
-    relativePath: "docs/terminology/bundle.mdx",
-    compId: "articles-docs-terminology-bundle",
-    crumb: "Terminology",
+    id: "renderstilloncloudrun",
+    title: "renderStillOnCloudrun()",
+    relativePath: "docs/cloudrun/renderstilloncloudrun.mdx",
+    compId: "articles-docs-cloudrun-renderstilloncloudrun",
+    crumb: "Cloud Run API",
   },
   {
-    id: "terminology/sequence",
-    title: "Sequence",
-    relativePath: "docs/terminology/sequence.mdx",
-    compId: "articles-docs-terminology-sequence",
-    crumb: "Terminology",
+    id: "setup",
+    title: "Setup",
+    relativePath: "docs/cloudrun/setup.mdx",
+    compId: "articles-docs-cloudrun-setup",
+    crumb: "Cloud Run",
   },
   {
-    id: "terminology/concurrency",
-    title: "Concurrency",
-    relativePath: "docs/terminology/concurrency.mdx",
-    compId: "articles-docs-terminology-concurrency",
-    crumb: "Terminology",
+    id: "speculateservicename",
+    title: "speculateServiceName()",
+    relativePath: "docs/cloudrun/speculateservicename.mdx",
+    compId: "articles-docs-cloudrun-speculateservicename",
+    crumb: "Cloud Run API",
   },
   {
-    id: "terminology/entry-point",
-    title: "Entry point",
-    relativePath: "docs/terminology/entry-point.mdx",
-    compId: "articles-docs-terminology-entry-point",
-    crumb: "Terminology",
+    id: "testpermissions",
+    title: "testPermissions()",
+    relativePath: "docs/cloudrun/testpermissions.mdx",
+    compId: "articles-docs-cloudrun-testpermissions",
+    crumb: "Cloud Run API",
   },
   {
-    id: "terminology/player",
-    title: "Remotion Player",
-    relativePath: "docs/terminology/player.mdx",
-    compId: "articles-docs-terminology-player",
-    crumb: "Terminology",
+    id: "uninstall",
+    title: "Uninstall Cloud Run",
+    relativePath: "docs/cloudrun/uninstall.mdx",
+    compId: "articles-docs-cloudrun-uninstall",
+    crumb: null,
   },
   {
-    id: "terminology/cloud-run-url",
-    title: "Cloud Run URL",
-    relativePath: "docs/terminology/cloud-run-url.mdx",
-    compId: "articles-docs-terminology-cloud-run-url",
-    crumb: "Terminology",
+    id: "upgrading",
+    title: "Upgrading Cloud Run",
+    relativePath: "docs/cloudrun/upgrading.mdx",
+    compId: "articles-docs-cloudrun-upgrading",
+    crumb: null,
   },
   {
-    id: "terminology/input-props",
-    title: "Input Props",
-    relativePath: "docs/terminology/input-props.mdx",
-    compId: "articles-docs-terminology-input-props",
-    crumb: "Terminology",
+    id: "cloudrun-alpha",
+    title: "Cloud Run Alpha",
+    relativePath: "docs/cloudrun-alpha.mdx",
+    compId: "articles-docs-cloudrun-alpha",
+    crumb: "Version Upgrade",
   },
   {
-    id: "terminology/public-dir",
-    title: "Public directory",
-    relativePath: "docs/terminology/public-dir.mdx",
-    compId: "articles-docs-terminology-public-dir",
-    crumb: "Terminology",
+    id: "cloudrun",
+    title: "@remotion/cloudrun",
+    relativePath: "docs/cloudrun.mdx",
+    compId: "articles-docs-cloudrun",
+    crumb: null,
   },
   {
-    id: "terminology/root-file",
-    title: "Root file",
-    relativePath: "docs/terminology/root-file.mdx",
-    compId: "articles-docs-terminology-root-file",
-    crumb: "Terminology",
+    id: "compare/motion-canvas",
+    title: "How does Remotion compare to Motion Canvas?",
+    relativePath: "docs/compare/motion-canvas.mdx",
+    compId: "articles-docs-compare-motion-canvas",
+    crumb: "FAQ",
   },
   {
-    id: "terminology/service-name",
-    title: "Service Name",
-    relativePath: "docs/terminology/service-name.mdx",
-    compId: "articles-docs-terminology-service-name",
-    crumb: "Terminology",
+    id: "composition",
+    title: "<Composition>",
+    relativePath: "docs/composition.mdx",
+    compId: "articles-docs-composition",
+    crumb: "API",
   },
   {
-    id: "terminology/serve-url",
-    title: "Serve URL",
-    relativePath: "docs/terminology/serve-url.mdx",
-    compId: "articles-docs-terminology-serve-url",
-    crumb: "Terminology",
+    id: "config",
+    title: "Configuration file",
+    relativePath: "docs/config.mdx",
+    compId: "articles-docs-config",
+    crumb: "remotion.config.ts",
   },
   {
-    id: "terminology/composition",
-    title: "Composition",
-    relativePath: "docs/terminology/composition.mdx",
-    compId: "articles-docs-terminology-composition",
-    crumb: "Terminology",
+    id: "continue-render",
+    title: "continueRender()",
+    relativePath: "docs/continue-render.mdx",
+    compId: "articles-docs-continue-render",
+    crumb: "API",
   },
   {
-    id: "terminology/remotion-root",
-    title: "Remotion Root",
-    relativePath: "docs/terminology/remotion-root.mdx",
-    compId: "articles-docs-terminology-remotion-root",
-    crumb: "Terminology",
+    id: "contributing/bounty",
+    title: "Take and solve bounty issues",
+    relativePath: "docs/contributing/bounty.mdx",
+    compId: "articles-docs-contributing-bounty",
+    crumb: "Contributing",
   },
   {
-    id: "motion-blur/camera-motion-blur",
-    title: "<CameraMotionBlur>",
-    relativePath: "docs/motion-blur/camera-motion-blur.mdx",
-    compId: "articles-docs-motion-blur-camera-motion-blur",
-    crumb: "Realistic camera effect",
+    id: "contributing/docs",
+    title: "Contributing to the documentation",
+    relativePath: "docs/contributing/docs.mdx",
+    compId: "articles-docs-contributing-docs",
+    crumb: "Contributing",
   },
   {
-    id: "motion-blur/index",
-    title: "@remotion/motion-blur",
-    relativePath: "docs/motion-blur/index.mdx",
-    compId: "articles-docs-motion-blur-index",
-    crumb: null,
+    id: "contributing/feature",
+    title: "Implementing a new feature",
+    relativePath: "docs/contributing/feature.mdx",
+    compId: "articles-docs-contributing-feature",
+    crumb: "Contributing",
   },
   {
-    id: "motion-blur/motion-blur",
-    title: "<MotionBlur>",
-    relativePath: "docs/motion-blur/motion-blur.mdx",
-    compId: "articles-docs-motion-blur-motion-blur",
-    crumb: null,
+    id: "contributing/formatting",
+    title: "Formatting in the Remotion repo",
+    relativePath: "docs/contributing/formatting.mdx",
+    compId: "articles-docs-contributing-formatting",
+    crumb: "Contributing",
   },
   {
-    id: "motion-blur/trail",
-    title: "<Trail>",
-    relativePath: "docs/motion-blur/trail.mdx",
-    compId: "articles-docs-motion-blur-trail",
-    crumb: "Motion blur",
+    id: "contributing/index",
+    title: "Contributing to Remotion",
+    relativePath: "docs/contributing/index.mdx",
+    compId: "articles-docs-contributing-index",
+    crumb: "How to be awesome",
   },
   {
-    id: "freeze",
-    title: "<Freeze>",
-    relativePath: "docs/freeze.mdx",
-    compId: "articles-docs-freeze",
-    crumb: "API",
+    id: "contributing/ineligible",
+    title: "Ineligible for bounties",
+    relativePath: "docs/contributing/ineligible.mdx",
+    compId: "articles-docs-contributing-ineligible",
+    crumb: "Contributing",
   },
   {
-    id: "terminology",
-    title: "Terminology",
-    relativePath: "docs/terminology.mdx",
-    compId: "articles-docs-terminology",
-    crumb: "The Remotion dictionary",
+    id: "contributing/option",
+    title: "Implementing a new option",
+    relativePath: "docs/contributing/option.mdx",
+    compId: "articles-docs-contributing-option",
+    crumb: "Contributing",
+  },
+  {
+    id: "contributing/presentation",
+    title: "Contribute your own presentation",
+    relativePath: "docs/contributing/presentation.mdx",
+    compId: "articles-docs-contributing-presentation",
+    crumb: "Contributing",
+  },
+  {
+    id: "contributing/rust",
+    title: "Contributing Rust code",
+    relativePath: "docs/contributing/rust.mdx",
+    compId: "articles-docs-contributing-rust",
+    crumb: "Contributing",
+  },
+  {
+    id: "authoring-packages",
+    title: "Authoring a Remotion library",
+    relativePath: "docs/creating-a-library.mdx",
+    compId: "articles-docs-creating-a-library",
+    crumb: "How to",
   },
   {
     id: "data-fetching",
@@ -609,102 +658,158 @@ export const articles = [
     crumb: "How to",
   },
   {
-    id: "target-closed",
-    title: "'Target closed' error message",
-    relativePath: "docs/target-closed.mdx",
-    compId: "articles-docs-target-closed",
-    crumb: "Troubleshooting",
+    id: "dataset-render",
+    title: "Render videos programmatically from a dataset",
+    relativePath: "docs/dataset-render.mdx",
+    compId: "articles-docs-dataset-render",
+    crumb: "Tutorials",
   },
   {
-    id: "wrong-composition-mount",
-    title: "Wrongly mounted <Composition>",
-    relativePath: "docs/wrong-composition-mount.mdx",
-    compId: "articles-docs-wrong-composition-mount",
+    id: "delay-render",
+    title: "delayRender() and continueRender()",
+    relativePath: "docs/delay-render.mdx",
+    compId: "articles-docs-delay-render",
+    crumb: "How to",
+  },
+  {
+    id: "docker",
+    title: "Dockerizing a Remotion app",
+    relativePath: "docs/docker.mdx",
+    compId: "articles-docs-docker",
+    crumb: "Building video apps",
+  },
+  {
+    id: "webpack-dynamic-imports",
+    title: "Webpack dynamic imports",
+    relativePath: "docs/dynamic-import.mdx",
+    compId: "articles-docs-dynamic-import",
+    crumb: "Knowledge Base",
+  },
+  {
+    id: "dynamic-metadata",
+    title: "Variable duration and dimensions",
+    relativePath: "docs/dynamic-metadata.mdx",
+    compId: "articles-docs-dynamic-metadata",
+    crumb: "How To",
+  },
+  {
+    id: "easing",
+    title: "Easing",
+    relativePath: "docs/easing.mdx",
+    compId: "articles-docs-easing",
+    crumb: "API",
+  },
+  {
+    id: "enable-scss/enable-scss",
+    title: "enableScss()",
+    relativePath: "docs/enable-scss/enable-scss.mdx",
+    compId: "articles-docs-enable-scss-enable-scss",
+    crumb: "@remotion/enable-scss",
+  },
+  {
+    id: "enable-scss/overview",
+    title: "@remotion/enable-scss",
+    relativePath: "docs/enable-scss/overview.mdx",
+    compId: "articles-docs-enable-scss-overview",
+    crumb: null,
+  },
+  {
+    id: "enametoolong",
+    title: "ENAMETOOLONG",
+    relativePath: "docs/enametoolong.mdx",
+    compId: "articles-docs-enametoolong",
     crumb: "Troubleshooting",
   },
   {
-    id: "flickering",
-    title: "Flickering",
-    relativePath: "docs/flickering.mdx",
-    compId: "articles-docs-flickering",
-    crumb: "How to avoid",
+    id: "encoding",
+    title: "Encoding Guide",
+    relativePath: "docs/encoding.mdx",
+    compId: "articles-docs-encoding",
+    crumb: "Codecs and more",
   },
   {
-    id: "contributing/ineligible",
-    title: "Ineligible for bounties",
-    relativePath: "docs/contributing/ineligible.mdx",
-    compId: "articles-docs-contributing-ineligible",
-    crumb: "Contributing",
+    id: "env-variables",
+    title: "Environment variables",
+    relativePath: "docs/env-variables.mdx",
+    compId: "articles-docs-env-variables",
+    crumb: "How To",
   },
   {
-    id: "contributing/bounty",
-    title: "Take and solve bounty issues",
-    relativePath: "docs/contributing/bounty.mdx",
-    compId: "articles-docs-contributing-bounty",
-    crumb: "Contributing",
+    id: "ffmpeg",
+    title: "Installing FFmpeg",
+    relativePath: "docs/ffmpeg.mdx",
+    compId: "articles-docs-ffmpeg",
+    crumb: "(you don't have to)",
   },
   {
-    id: "contributing/rust",
-    title: "Contributing Rust code",
-    relativePath: "docs/contributing/rust.mdx",
-    compId: "articles-docs-contributing-rust",
-    crumb: "Contributing",
+    id: "figma",
+    title: "Import from Figma",
+    relativePath: "docs/figma.mdx",
+    compId: "articles-docs-figma",
+    crumb: "The best of both",
+  },
+  {
+    id: "flickering",
+    title: "Flickering",
+    relativePath: "docs/flickering.mdx",
+    compId: "articles-docs-flickering",
+    crumb: "How to avoid",
   },
   {
-    id: "contributing/option",
-    title: "Implementing a new option",
-    relativePath: "docs/contributing/option.mdx",
-    compId: "articles-docs-contributing-option",
-    crumb: "Contributing",
+    id: "folder",
+    title: "<Folder>",
+    relativePath: "docs/folder.mdx",
+    compId: "articles-docs-folder",
+    crumb: "API",
   },
   {
-    id: "contributing/presentation",
-    title: "Contribute your own presentation",
-    relativePath: "docs/contributing/presentation.mdx",
-    compId: "articles-docs-contributing-presentation",
-    crumb: "Contributing",
+    id: "font-picker",
+    title: "Build a Google Font picker",
+    relativePath: "docs/font-picker.mdx",
+    compId: "articles-docs-font-picker",
+    crumb: "Building video apps",
   },
   {
-    id: "contributing/feature",
-    title: "Implementing a new feature",
-    relativePath: "docs/contributing/feature.mdx",
-    compId: "articles-docs-contributing-feature",
-    crumb: "Contributing",
+    id: "fonts-api",
+    title: "@remotion/fonts",
+    relativePath: "docs/fonts-api/index.mdx",
+    compId: "articles-docs-fonts-api-index",
+    crumb: null,
   },
   {
-    id: "contributing/index",
-    title: "Contributing to Remotion",
-    relativePath: "docs/contributing/index.mdx",
-    compId: "articles-docs-contributing-index",
-    crumb: "How to be awesome",
+    id: "fonts-api/load-font",
+    title: "loadFont()",
+    relativePath: "docs/fonts-api/load-font.mdx",
+    compId: "articles-docs-fonts-api-load-font",
+    crumb: "@remotion/fonts",
   },
   {
-    id: "contributing/formatting",
-    title: "Formatting in the Remotion repo",
-    relativePath: "docs/contributing/formatting.mdx",
-    compId: "articles-docs-contributing-formatting",
-    crumb: "Contributing",
+    id: "fonts",
+    title: "Using fonts",
+    relativePath: "docs/fonts.mdx",
+    compId: "articles-docs-fonts",
+    crumb: "Techniques",
   },
   {
-    id: "contributing/docs",
-    title: "Contributing to the documentation",
-    relativePath: "docs/contributing/docs.mdx",
-    compId: "articles-docs-contributing-docs",
-    crumb: "Contributing",
+    id: "freeze",
+    title: "<Freeze>",
+    relativePath: "docs/freeze.mdx",
+    compId: "articles-docs-freeze",
+    crumb: "API",
   },
   {
-    id: "third-party",
-    title: "Third party integrations",
-    relativePath: "docs/third-party.mdx",
-    compId: "articles-docs-third-party",
-    crumb: "Integrations",
+    id: "get-audio-data",
+    title: "getAudioData()",
+    relativePath: "docs/get-audio-data.mdx",
+    compId: "articles-docs-get-audio-data",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "tailwind",
-    title: "TailwindCSS",
-    relativePath: "docs/tailwind.mdx",
-    compId: "articles-docs-tailwind",
-    crumb: "text-lg font-bold",
+    id: "get-audio-duration-in-seconds",
+    title: "getAudioDurationInSeconds()",
+    relativePath: "docs/get-audio-duration-in-seconds.mdx",
+    compId: "articles-docs-get-audio-duration-in-seconds",
+    crumb: "@remotion/media-utils",
   },
   {
     id: "get-image-dimensions",
@@ -714,115 +819,150 @@ export const articles = [
     crumb: "@remotion/media-utils",
   },
   {
-    id: "cancel-render",
-    title: "cancelRender()",
-    relativePath: "docs/cancel-render.mdx",
-    compId: "articles-docs-cancel-render",
-    crumb: "How to",
+    id: "get-input-props",
+    title: "getInputProps()",
+    relativePath: "docs/get-input-props.mdx",
+    compId: "articles-docs-get-input-props",
+    crumb: "API",
   },
   {
-    id: "spline",
-    title: "Import from Spline",
-    relativePath: "docs/spline.mdx",
-    compId: "articles-docs-spline",
-    crumb: "Integrations",
+    id: "get-remotion-environment",
+    title: "getRemotionEnvironment()",
+    relativePath: "docs/get-remotion-environment.mdx",
+    compId: "articles-docs-get-remotion-environment",
+    crumb: "API",
   },
   {
-    id: "use-video-texture",
-    title: "useVideoTexture()",
-    relativePath: "docs/use-video-texture.mdx",
-    compId: "articles-docs-use-video-texture",
-    crumb: "@remotion/three",
+    id: "getstaticfiles",
+    title: "getStaticFiles()",
+    relativePath: "docs/get-static-files.mdx",
+    compId: "articles-docs-get-static-files",
+    crumb: "API",
   },
   {
-    id: "gpu",
-    title: "Using the GPU",
-    relativePath: "docs/gpu.mdx",
-    compId: "articles-docs-gpu",
-    crumb: "Need for Speed",
+    id: "get-video-metadata",
+    title: "getVideoMetadata()",
+    relativePath: "docs/get-video-metadata.mdx",
+    compId: "articles-docs-get-video-metadata",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "timeout",
-    title: "Debugging timeouts",
-    relativePath: "docs/timeout.mdx",
-    compId: "articles-docs-timeout",
-    crumb: "Troubleshooting",
+    id: "get-waveform-portion",
+    title: "getWaveformPortion()",
+    relativePath: "docs/get-waveform-portion.mdx",
+    compId: "articles-docs-get-waveform-portion",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "renderer",
-    title: "@remotion/renderer",
-    relativePath: "docs/renderer.mdx",
-    compId: "articles-docs-renderer",
-    crumb: null,
+    id: "getting-started",
+    title: "Creating a new project",
+    relativePath: "docs/getting-started.mdx",
+    compId: "articles-docs-getting-started",
+    crumb: "Let's begin!",
   },
   {
-    id: "three",
-    title: "@remotion/three",
-    relativePath: "docs/three.mdx",
-    compId: "articles-docs-three",
+    id: "get-gif-duration-in-seconds",
+    title: "getGifDurationInSeconds()",
+    relativePath: "docs/gif/get-gif-duration-in-seconds.mdx",
+    compId: "articles-docs-gif-get-gif-duration-in-seconds",
+    crumb: "@remotion/gif",
+  },
+  {
+    id: "gif/gif",
+    title: "<Gif>",
+    relativePath: "docs/gif/gif.mdx",
+    compId: "articles-docs-gif-gif",
+    crumb: "@remotion/gif",
+  },
+  {
+    id: "gif/index",
+    title: "@remotion/gif",
+    relativePath: "docs/gif/index.mdx",
+    compId: "articles-docs-gif-index",
     crumb: null,
   },
   {
-    id: "absolute-fill",
-    title: "<AbsoluteFill>",
-    relativePath: "docs/absolute-fill.mdx",
-    compId: "articles-docs-absolute-fill",
-    crumb: "API",
+    id: "gif/preload-gif",
+    title: "preloadGif()",
+    relativePath: "docs/gif/preload-gif.mdx",
+    compId: "articles-docs-gif-preload-gif",
+    crumb: "@remotion/gif",
   },
   {
-    id: "offthreadvideo",
-    title: "<OffthreadVideo>",
-    relativePath: "docs/offthreadvideo.mdx",
-    compId: "articles-docs-offthreadvideo",
-    crumb: "API",
+    id: "google-fonts/get-available-fonts",
+    title: "getAvailableFonts()",
+    relativePath: "docs/google-fonts/get-available-fonts.mdx",
+    compId: "articles-docs-google-fonts-get-available-fonts",
+    crumb: "@remotion/google-fonts",
   },
   {
-    id: "dynamic-metadata",
-    title: "Variable duration and dimensions",
-    relativePath: "docs/dynamic-metadata.mdx",
-    compId: "articles-docs-dynamic-metadata",
-    crumb: "How To",
+    id: "google-fonts/get-info",
+    title: "getInfo()",
+    relativePath: "docs/google-fonts/get-info.mdx",
+    compId: "articles-docs-google-fonts-get-info",
+    crumb: "@remotion/google-fonts",
   },
   {
-    id: "zod-types/z-textarea",
-    title: "zTextarea()",
-    relativePath: "docs/zod-types/z-textarea.mdx",
-    compId: "articles-docs-zod-types-z-textarea",
+    id: "google-fonts",
+    title: "@remotion/google-fonts",
+    relativePath: "docs/google-fonts/index.mdx",
+    compId: "articles-docs-google-fonts-index",
     crumb: null,
   },
   {
-    id: "zod-types/z-color",
-    title: "zColor()",
-    relativePath: "docs/zod-types/z-color.mdx",
-    compId: "articles-docs-zod-types-z-color",
-    crumb: null,
+    id: "google-fonts/load-font",
+    title: "loadFont()",
+    relativePath: "docs/google-fonts/load-font.mdx",
+    compId: "articles-docs-google-fonts-load-font",
+    crumb: "@remotion/google-fonts",
   },
   {
-    id: "zod-types/index",
-    title: "@remotion/zod-types",
-    relativePath: "docs/zod-types/index.mdx",
-    compId: "articles-docs-zod-types-index",
-    crumb: "Schema",
+    id: "gpu",
+    title: "Using the GPU",
+    relativePath: "docs/gpu.mdx",
+    compId: "articles-docs-gpu",
+    crumb: "Need for Speed",
   },
   {
-    id: "non-seekable-media",
-    title: "Non-seekable media",
-    relativePath: "docs/non-seekable-media.mdx",
-    compId: "articles-docs-non-seekable-media",
-    crumb: "Troubleshooting",
+    id: "props-resolution",
+    title: "How props get resolved",
+    relativePath: "docs/how-props-flow.mdx",
+    compId: "articles-docs-how-props-flow",
+    crumb: "Parameterized videos",
   },
   {
-    id: "docker",
-    title: "Dockerizing a Remotion app",
-    relativePath: "docs/docker.mdx",
-    compId: "articles-docs-docker",
-    crumb: "Building video apps",
+    id: "iframe",
+    title: "<IFrame>",
+    relativePath: "docs/iframe.mdx",
+    compId: "articles-docs-iframe",
+    crumb: "API",
+  },
+  {
+    id: "img",
+    title: "<Img>",
+    relativePath: "docs/img.mdx",
+    compId: "articles-docs-img",
+    crumb: "API",
+  },
+  {
+    id: "assets",
+    title: "Importing assets",
+    relativePath: "docs/importing-assets.mdx",
+    compId: "articles-docs-importing-assets",
+    crumb: "How To",
+  },
+  {
+    id: "install-whisper-cpp/convert-to-captions",
+    title: "convertToCaptions()",
+    relativePath: "docs/install-whisper-cpp/convert-to-captions.mdx",
+    compId: "articles-docs-install-whisper-cpp-convert-to-captions",
+    crumb: "@remotion/install-whisper-cpp",
   },
   {
-    id: "install-whisper-cpp/install-whisper-cpp",
-    title: "installWhisperCpp()",
-    relativePath: "docs/install-whisper-cpp/install-whisper-cpp.mdx",
-    compId: "articles-docs-install-whisper-cpp-install-whisper-cpp",
+    id: "install-whisper-cpp/download-whisper-model",
+    title: "downloadWhisperModel()",
+    relativePath: "docs/install-whisper-cpp/download-whisper-model.mdx",
+    compId: "articles-docs-install-whisper-cpp-download-whisper-model",
     crumb: "@remotion/install-whisper-cpp",
   },
   {
@@ -832,6 +972,13 @@ export const articles = [
     compId: "articles-docs-install-whisper-cpp-index",
     crumb: "Transcribe audio locally",
   },
+  {
+    id: "install-whisper-cpp/install-whisper-cpp",
+    title: "installWhisperCpp()",
+    relativePath: "docs/install-whisper-cpp/install-whisper-cpp.mdx",
+    compId: "articles-docs-install-whisper-cpp-install-whisper-cpp",
+    crumb: "@remotion/install-whisper-cpp",
+  },
   {
     id: "install-whisper-cpp/transcribe",
     title: "transcribe()",
@@ -840,706 +987,710 @@ export const articles = [
     crumb: "@remotion/install-whisper-cpp",
   },
   {
-    id: "install-whisper-cpp/convert-to-captions",
-    title: "convertToCaptions()",
-    relativePath: "docs/install-whisper-cpp/convert-to-captions.mdx",
-    compId: "articles-docs-install-whisper-cpp-convert-to-captions",
-    crumb: "@remotion/install-whisper-cpp",
-  },
-  {
-    id: "install-whisper-cpp/download-whisper-model",
-    title: "downloadWhisperModel()",
-    relativePath: "docs/install-whisper-cpp/download-whisper-model.mdx",
-    compId: "articles-docs-install-whisper-cpp-download-whisper-model",
-    crumb: "@remotion/install-whisper-cpp",
+    id: "interpolate-colors",
+    title: "interpolateColors()",
+    relativePath: "docs/interpolate-colors.mdx",
+    compId: "articles-docs-interpolate-colors",
+    crumb: "API",
   },
   {
-    id: "get-audio-duration-in-seconds",
-    title: "getAudioDurationInSeconds()",
-    relativePath: "docs/get-audio-duration-in-seconds.mdx",
-    compId: "articles-docs-get-audio-duration-in-seconds",
-    crumb: "@remotion/media-utils",
+    id: "interpolate",
+    title: "interpolate()",
+    relativePath: "docs/interpolate.mdx",
+    compId: "articles-docs-interpolate",
+    crumb: "API",
   },
   {
-    id: "dataset-render",
-    title: "Render videos programmatically from a dataset",
-    relativePath: "docs/dataset-render.mdx",
-    compId: "articles-docs-dataset-render",
-    crumb: "Tutorials",
+    id: "javascript",
+    title: "Plain JavaScript",
+    relativePath: "docs/jsx-support.mdx",
+    compId: "articles-docs-jsx-support",
+    crumb: "How To",
   },
   {
-    id: "prefetch",
-    title: "prefetch()",
-    relativePath: "docs/prefetch.mdx",
-    compId: "articles-docs-prefetch",
-    crumb: "API",
+    id: "lambda/api",
+    title: "@remotion/lambda",
+    relativePath: "docs/lambda/api.mdx",
+    compId: "articles-docs-lambda-api",
+    crumb: "Render videos without servers on AWS",
   },
   {
-    id: "audio-buffer-to-data-url",
-    title: "audioBufferToDataUrl()",
-    relativePath: "docs/audiobuffertodataurl.mdx",
-    compId: "articles-docs-audiobuffertodataurl",
-    crumb: "@remotion/media-utils",
+    id: "authentication",
+    title: "Authentication",
+    relativePath: "docs/lambda/authentication.mdx",
+    compId: "articles-docs-lambda-authentication",
+    crumb: "Lambda",
   },
   {
-    id: "animating-properties",
-    title: "Animating properties",
-    relativePath: "docs/animating-properties.mdx",
-    compId: "articles-docs-animating-properties",
-    crumb: "The basics",
+    id: "autodelete",
+    title: "Auto-delete renders",
+    relativePath: "docs/lambda/autodelete.mdx",
+    compId: "articles-docs-lambda-autodelete",
+    crumb: "Lambda API",
   },
   {
-    id: "getstaticfiles",
-    title: "getStaticFiles()",
-    relativePath: "docs/get-static-files.mdx",
-    compId: "articles-docs-get-static-files",
-    crumb: "API",
+    id: "lambda/bucket-naming",
+    title: "Bucket names in Remotion Lambda",
+    relativePath: "docs/lambda/bucket-naming.mdx",
+    compId: "articles-docs-lambda-bucket-naming",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "paths/reverse-path",
-    title: "reversePath()",
-    relativePath: "docs/paths/reverse-path.mdx",
-    compId: "articles-docs-paths-reverse-path",
-    crumb: "@remotion/paths",
+    id: "changelog",
+    title: "Prerelease Changelog",
+    relativePath: "docs/lambda/changelog.mdx",
+    compId: "articles-docs-lambda-changelog",
+    crumb: null,
   },
   {
-    id: "paths/evolve-path",
-    title: "evolvePath()",
-    relativePath: "docs/paths/evolve-path.mdx",
-    compId: "articles-docs-paths-evolve-path",
-    crumb: "@remotion/paths",
+    id: "checklist",
+    title: "Production Checklist",
+    relativePath: "docs/lambda/checklist.mdx",
+    compId: "articles-docs-lambda-checklist",
+    crumb: "Lambda",
   },
   {
-    id: "paths/get-subpaths",
-    title: "getSubpaths()",
-    relativePath: "docs/paths/get-subpaths.mdx",
-    compId: "articles-docs-paths-get-subpaths",
-    crumb: "@remotion/paths",
+    id: "lambda/cli/compositions",
+    title: "npx remotion lambda compositions",
+    relativePath: "docs/lambda/cli/compositions.mdx",
+    compId: "articles-docs-lambda-cli-compositions",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "paths/get-point-at-length",
-    title: "getPointAtLength()",
-    relativePath: "docs/paths/get-point-at-length.mdx",
-    compId: "articles-docs-paths-get-point-at-length",
-    crumb: "@remotion/paths",
+    id: "functions",
+    title: "npx remotion lambda functions",
+    relativePath: "docs/lambda/cli/functions.mdx",
+    compId: "articles-docs-lambda-cli-functions",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "paths/get-length",
-    title: "getLength()",
-    relativePath: "docs/paths/get-length.mdx",
-    compId: "articles-docs-paths-get-length",
-    crumb: "@remotion/paths",
+    id: "policies",
+    title: "npx remotion lambda policies",
+    relativePath: "docs/lambda/cli/policies.mdx",
+    compId: "articles-docs-lambda-cli-policies",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "paths/scale-path",
-    title: "scalePath()",
-    relativePath: "docs/paths/scale-path.mdx",
-    compId: "articles-docs-paths-scale-path",
-    crumb: "@remotion/paths",
+    id: "quotas",
+    title: "npx remotion lambda quotas",
+    relativePath: "docs/lambda/cli/quotas.mdx",
+    compId: "articles-docs-lambda-cli-quotas",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "paths/get-parts",
-    title: "getParts()",
-    relativePath: "docs/paths/get-parts.mdx",
-    compId: "articles-docs-paths-get-parts",
-    crumb: "@remotion/paths",
+    id: "regions",
+    title: "npx remotion lambda regions",
+    relativePath: "docs/lambda/cli/regions.mdx",
+    compId: "articles-docs-lambda-cli-regions",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "paths/get-tangent-at-length",
-    title: "getTangentAtLength()",
-    relativePath: "docs/paths/get-tangent-at-length.mdx",
-    compId: "articles-docs-paths-get-tangent-at-length",
-    crumb: "@remotion/paths",
+    id: "render",
+    title: "npx remotion lambda render",
+    relativePath: "docs/lambda/cli/render.mdx",
+    compId: "articles-docs-lambda-cli-render",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "paths/get-instruction-index-at-length",
-    title: "getInstructionIndexAtLength()",
-    relativePath: "docs/paths/get-instruction-index-at-length.mdx",
-    compId: "articles-docs-paths-get-instruction-index-at-length",
-    crumb: "@remotion/paths",
+    id: "sites",
+    title: "npx remotion lambda sites",
+    relativePath: "docs/lambda/cli/sites.mdx",
+    compId: "articles-docs-lambda-cli-sites",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "paths/extend-viewbox",
-    title: "extendViewBox()",
-    relativePath: "docs/paths/extend-viewbox.mdx",
-    compId: "articles-docs-paths-extend-viewbox",
-    crumb: "@remotion/paths",
+    id: "still",
+    title: "npx remotion lambda still",
+    relativePath: "docs/lambda/cli/still.mdx",
+    compId: "articles-docs-lambda-cli-still",
+    crumb: "Lambda CLI Reference",
   },
   {
-    id: "paths/parse-path",
-    title: "parsePath()",
-    relativePath: "docs/paths/parse-path.mdx",
-    compId: "articles-docs-paths-parse-path",
-    crumb: "@remotion/paths",
+    id: "cli",
+    title: "@remotion/lambda - CLI",
+    relativePath: "docs/lambda/cli.mdx",
+    compId: "articles-docs-lambda-cli",
+    crumb: null,
   },
   {
-    id: "paths/translate-path",
-    title: "translatePath()",
-    relativePath: "docs/paths/translate-path.mdx",
-    compId: "articles-docs-paths-translate-path",
-    crumb: "@remotion/paths",
+    id: "concurrency",
+    title: "Concurrency",
+    relativePath: "docs/lambda/concurrency.mdx",
+    compId: "articles-docs-lambda-concurrency",
+    crumb: "Lambda",
   },
   {
-    id: "paths/normalize-path",
-    title: "normalizePath()",
-    relativePath: "docs/paths/normalize-path.mdx",
-    compId: "articles-docs-paths-normalize-path",
-    crumb: "@remotion/paths",
+    id: "optimizing-cost",
+    title: "Optimizing for cost",
+    relativePath: "docs/lambda/cost.mdx",
+    compId: "articles-docs-lambda-cost",
+    crumb: "Lambda",
   },
   {
-    id: "paths/index",
-    title: "@remotion/paths",
-    relativePath: "docs/paths/index.mdx",
-    compId: "articles-docs-paths-index",
-    crumb: "SVG",
+    id: "custom-destination",
+    title: "Customizing Lambda output destination",
+    relativePath: "docs/lambda/custom-destination.mdx",
+    compId: "articles-docs-lambda-custom-destination",
+    crumb: "Lambda",
   },
   {
-    id: "paths/get-bounding-box",
-    title: "getBoundingBox()",
-    relativePath: "docs/paths/get-bounding-box.mdx",
-    compId: "articles-docs-paths-get-bounding-box",
-    crumb: "@remotion/paths",
+    id: "lambda/custom-layers",
+    title: "Custom Layers",
+    relativePath: "docs/lambda/custom-layers.mdx",
+    compId: "articles-docs-lambda-custom-layers",
+    crumb: "Lambda",
   },
   {
-    id: "paths/warp-path",
-    title: "warpPath()",
-    relativePath: "docs/paths/warp-path.mdx",
-    compId: "articles-docs-paths-warp-path",
-    crumb: "@remotion/paths",
+    id: "deletefunction",
+    title: "deleteFunction()",
+    relativePath: "docs/lambda/deletefunction.mdx",
+    compId: "articles-docs-lambda-deletefunction",
+    crumb: "Lambda API",
   },
   {
-    id: "paths/interpolate-path",
-    title: "interpolatePath()",
-    relativePath: "docs/paths/interpolate-path.mdx",
-    compId: "articles-docs-paths-interpolate-path",
-    crumb: "@remotion/paths",
+    id: "deleterender",
+    title: "deleteRender()",
+    relativePath: "docs/lambda/deleterender.mdx",
+    compId: "articles-docs-lambda-deleterender",
+    crumb: "Lambda API",
   },
   {
-    id: "paths/serialize-instructions",
-    title: "serializeInstructions()",
-    relativePath: "docs/paths/serialize-instructions.mdx",
-    compId: "articles-docs-paths-serialize-instructions",
-    crumb: "@remotion/paths",
+    id: "deletesite",
+    title: "deleteSite()",
+    relativePath: "docs/lambda/deletesite.mdx",
+    compId: "articles-docs-lambda-deletesite",
+    crumb: "Lambda API",
   },
   {
-    id: "paths/reset-path",
-    title: "resetPath()",
-    relativePath: "docs/paths/reset-path.mdx",
-    compId: "articles-docs-paths-reset-path",
-    crumb: "@remotion/paths",
+    id: "deployfunction",
+    title: "deployFunction()",
+    relativePath: "docs/lambda/deployfunction.mdx",
+    compId: "articles-docs-lambda-deployfunction",
+    crumb: "Lambda API",
   },
   {
-    id: "paths/reduce-instructions",
-    title: "reduceInstructions()",
-    relativePath: "docs/paths/reduce-instructions.mdx",
-    compId: "articles-docs-paths-reduce-instructions",
-    crumb: "@remotion/paths",
+    id: "deploysite",
+    title: "deploySite()",
+    relativePath: "docs/lambda/deploysite.mdx",
+    compId: "articles-docs-lambda-deploysite",
+    crumb: "Lambda API",
   },
   {
-    id: "rive/index",
-    title: "@remotion/rive",
-    relativePath: "docs/rive/index.mdx",
-    compId: "articles-docs-rive-index",
-    crumb: "Integrations",
+    id: "disk-size",
+    title: "Disk size",
+    relativePath: "docs/lambda/disk-size.mdx",
+    compId: "articles-docs-lambda-disk-size",
+    crumb: "Lambda",
   },
   {
-    id: "rive/remotionrivecanvas",
-    title: "<RemotionRiveCanvas>",
-    relativePath: "docs/rive/remotionrivecanvas.mdx",
-    compId: "articles-docs-rive-remotionrivecanvas",
-    crumb: "@remotion/rive",
+    id: "downloadmedia",
+    title: "downloadMedia()",
+    relativePath: "docs/lambda/downloadmedia.mdx",
+    compId: "articles-docs-lambda-downloadmedia",
+    crumb: "Lambda API",
   },
   {
-    id: "layers",
-    title: "Layers",
-    relativePath: "docs/layers.mdx",
-    compId: "articles-docs-layers",
-    crumb: "Designing videos",
+    id: "downloadvideo",
+    title: "downloadVideo()",
+    relativePath: "docs/lambda/downloadvideo.mdx",
+    compId: "articles-docs-lambda-downloadvideo",
+    crumb: "Lambda API",
   },
   {
-    id: "use-img-and-iframe",
-    title: "<Img>, <Video> and <Audio>",
-    relativePath: "docs/use-img-and-iframe.mdx",
-    compId: "articles-docs-use-img-and-iframe",
-    crumb: "Best practices",
+    id: "estimateprice",
+    title: "estimatePrice()",
+    relativePath: "docs/lambda/estimateprice.mdx",
+    compId: "articles-docs-lambda-estimateprice",
+    crumb: "Lambda API",
   },
   {
-    id: "font-picker",
-    title: "Build a Google Font picker",
-    relativePath: "docs/font-picker.mdx",
-    compId: "articles-docs-font-picker",
-    crumb: "Building video apps",
+    id: "lambda/faq",
+    title: "FAQ",
+    relativePath: "docs/lambda/faq.mdx",
+    compId: "articles-docs-lambda-faq",
+    crumb: "Lambda",
   },
   {
-    id: "support",
-    title: "Support Policy",
-    relativePath: "docs/support.mdx",
-    compId: "articles-docs-support",
-    crumb: "Let us help you",
+    id: "feb-2022-outage",
+    title: "February 2022 Outage",
+    relativePath: "docs/lambda/feb-2022-outage.mdx",
+    compId: "articles-docs-lambda-feb-2022-outage",
+    crumb: null,
   },
   {
-    id: "register-root",
-    title: "registerRoot()",
-    relativePath: "docs/register-root.mdx",
-    compId: "articles-docs-register-root",
-    crumb: "API",
+    id: "lambda/feb-2023-incident",
+    title: "Upgrade your Lambda functions to prevent breakage",
+    relativePath: "docs/lambda/feb-2023-incident.mdx",
+    compId: "articles-docs-lambda-feb-2023-incident",
+    crumb: "DevOps advisory",
   },
   {
-    id: "props-resolution",
-    title: "How props get resolved",
-    relativePath: "docs/how-props-flow.mdx",
-    compId: "articles-docs-how-props-flow",
-    crumb: "Parameterized videos",
+    id: "getawsclient",
+    title: "getAwsClient()",
+    relativePath: "docs/lambda/getawsclient.mdx",
+    compId: "articles-docs-lambda-getawsclient",
+    crumb: "Lambda API",
   },
   {
-    id: "preview",
-    title: "Preview your video",
-    relativePath: "docs/preview.mdx",
-    compId: "articles-docs-preview",
-    crumb: "Timeline basics",
+    id: "getcompositionsonlambda",
+    title: "getCompositionsOnLambda()",
+    relativePath: "docs/lambda/getcompositionsonlambda.mdx",
+    compId: "articles-docs-lambda-getcompositionsonlambda",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/triangle",
-    title: "<Triangle />",
-    relativePath: "docs/shapes/triangle.mdx",
-    compId: "articles-docs-shapes-triangle",
-    crumb: "@remotion/shapes",
+    id: "getfunctioninfo",
+    title: "getFunctionInfo()",
+    relativePath: "docs/lambda/getfunctioninfo.mdx",
+    compId: "articles-docs-lambda-getfunctioninfo",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/rect",
-    title: "<Rect />",
-    relativePath: "docs/shapes/rect.mdx",
-    compId: "articles-docs-shapes-rect",
-    crumb: "@remotion/shapes",
+    id: "getfunctions",
+    title: "getFunctions()",
+    relativePath: "docs/lambda/getfunctions.mdx",
+    compId: "articles-docs-lambda-getfunctions",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/polygon",
-    title: "<Polygon />",
-    relativePath: "docs/shapes/polygon.mdx",
-    compId: "articles-docs-shapes-polygon",
-    crumb: "@remotion/shapes",
+    id: "getorcreatebucket",
+    title: "getOrCreateBucket()",
+    relativePath: "docs/lambda/getorcreatebucket.mdx",
+    compId: "articles-docs-lambda-getorcreatebucket",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/circle",
-    title: "<Circle />",
-    relativePath: "docs/shapes/circle.mdx",
-    compId: "articles-docs-shapes-circle",
-    crumb: "@remotion/shapes",
+    id: "getregions",
+    title: "getRegions()",
+    relativePath: "docs/lambda/getregions.mdx",
+    compId: "articles-docs-lambda-getregions",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/make-rect",
-    title: "makeRect()",
-    relativePath: "docs/shapes/make-rect.mdx",
-    compId: "articles-docs-shapes-make-rect",
-    crumb: "@remotion/shapes",
+    id: "getrenderprogress",
+    title: "getRenderProgress()",
+    relativePath: "docs/lambda/getrenderprogress.mdx",
+    compId: "articles-docs-lambda-getrenderprogress",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/index",
-    title: "@remotion/shapes",
-    relativePath: "docs/shapes/index.mdx",
-    compId: "articles-docs-shapes-index",
-    crumb: "SVG component library",
+    id: "getrolepolicy",
+    title: "getRolePolicy()",
+    relativePath: "docs/lambda/getrolepolicy.mdx",
+    compId: "articles-docs-lambda-getrolepolicy",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/ellipse",
-    title: "<Ellipse />",
-    relativePath: "docs/shapes/ellipse.mdx",
-    compId: "articles-docs-shapes-ellipse",
-    crumb: "@remotion/shapes",
+    id: "getsites",
+    title: "getSites()",
+    relativePath: "docs/lambda/getsites.mdx",
+    compId: "articles-docs-lambda-getsites",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/make-triangle",
-    title: "makeTriangle()",
-    relativePath: "docs/shapes/make-triangle.mdx",
-    compId: "articles-docs-shapes-make-triangle",
-    crumb: "@remotion/shapes",
+    id: "getuserpolicy",
+    title: "getUserPolicy()",
+    relativePath: "docs/lambda/getuserpolicy.mdx",
+    compId: "articles-docs-lambda-getuserpolicy",
+    crumb: "Lambda API",
   },
   {
-    id: "shapes/pie",
-    title: "<Pie />",
-    relativePath: "docs/shapes/pie.mdx",
-    compId: "articles-docs-shapes-pie",
-    crumb: "@remotion/shapes",
+    id: "lambda/go",
+    title: "Triggering renders from Go",
+    relativePath: "docs/lambda/go.mdx",
+    compId: "articles-docs-lambda-go",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "shapes/make-star",
-    title: "makeStar()",
-    relativePath: "docs/shapes/make-star.mdx",
-    compId: "articles-docs-shapes-make-star",
-    crumb: "@remotion/shapes",
+    id: "how-lambda-works",
+    title: "How Remotion Lambda works",
+    relativePath: "docs/lambda/how-lambda-works.mdx",
+    compId: "articles-docs-lambda-how-lambda-works",
+    crumb: "Lambda",
   },
   {
-    id: "shapes/make-pie",
-    title: "makePie()",
-    relativePath: "docs/shapes/make-pie.mdx",
-    compId: "articles-docs-shapes-make-pie",
-    crumb: "@remotion/shapes",
+    id: "lambda/insights",
+    title: "Enable Lambda Insights",
+    relativePath: "docs/lambda/insights.mdx",
+    compId: "articles-docs-lambda-insights",
+    crumb: "Lambda",
   },
   {
-    id: "shapes/make-ellipse",
-    title: "makeEllipse()",
-    relativePath: "docs/shapes/make-ellipse.mdx",
-    compId: "articles-docs-shapes-make-ellipse",
-    crumb: "@remotion/shapes",
+    id: "lambda/light-client",
+    title: "Light client",
+    relativePath: "docs/lambda/light-client.mdx",
+    compId: "articles-docs-lambda-light-client",
+    crumb: "Lambda",
+  },
+  {
+    id: "lambda/limits",
+    title: "Lambda Limits",
+    relativePath: "docs/lambda/limits.mdx",
+    compId: "articles-docs-lambda-limits",
+    crumb: "Lambda",
   },
   {
-    id: "shapes/star",
-    title: "<Star />",
-    relativePath: "docs/shapes/star.mdx",
-    compId: "articles-docs-shapes-star",
-    crumb: "@remotion/shapes",
+    id: "lambda/multiple-buckets",
+    title: "Multiple buckets in Remotion Lambda",
+    relativePath: "docs/lambda/multiple-buckets.mdx",
+    compId: "articles-docs-lambda-multiple-buckets",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "shapes/make-polygon",
-    title: "makePolygon()",
-    relativePath: "docs/shapes/make-polygon.mdx",
-    compId: "articles-docs-shapes-make-polygon",
-    crumb: "@remotion/shapes",
+    id: "naming-convention",
+    title: "Function naming convention",
+    relativePath: "docs/lambda/naming-convention.mdx",
+    compId: "articles-docs-lambda-naming-convention",
+    crumb: "Lambda",
   },
   {
-    id: "shapes/make-circle",
-    title: "makeCircle()",
-    relativePath: "docs/shapes/make-circle.mdx",
-    compId: "articles-docs-shapes-make-circle",
-    crumb: "@remotion/shapes",
+    id: "lambda/permissions",
+    title: "Permissions",
+    relativePath: "docs/lambda/permissions.mdx",
+    compId: "articles-docs-lambda-permissions",
+    crumb: "Lambda",
   },
   {
-    id: "series",
-    title: "<Series>",
-    relativePath: "docs/series.mdx",
-    compId: "articles-docs-series",
-    crumb: "API",
+    id: "lambda/php",
+    title: "Triggering renders from PHP",
+    relativePath: "docs/lambda/php.mdx",
+    compId: "articles-docs-lambda-php",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "bun",
-    title: "Bun support",
-    relativePath: "docs/bun.mdx",
-    compId: "articles-docs-bun",
-    crumb: "bun bun bun bun bun",
+    id: "presignurl",
+    title: "presignUrl()",
+    relativePath: "docs/lambda/presignurl.mdx",
+    compId: "articles-docs-lambda-presignurl",
+    crumb: "Lambda API",
   },
   {
-    id: "loading-root-component",
-    title: "Root component Timeout",
-    relativePath: "docs/troubleshooting/loading-root-component.mdx",
-    compId: "articles-docs-troubleshooting-loading-root-component",
-    crumb: "Troubleshooting",
+    id: "lambda/python",
+    title: "Triggering renders from Python",
+    relativePath: "docs/lambda/python.mdx",
+    compId: "articles-docs-lambda-python",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "troubleshooting/background-image",
-    title: "Flickering when using background-image or mask-image",
-    relativePath: "docs/troubleshooting/background-image.mdx",
-    compId: "articles-docs-troubleshooting-background-image",
-    crumb: "Common mistakes",
+    id: "region-selection",
+    title: "Region selection",
+    relativePath: "docs/lambda/region-selection.mdx",
+    compId: "articles-docs-lambda-region-selection",
+    crumb: "Lambda",
   },
   {
-    id: "rosetta",
-    title: "Apple Silicon under Rosetta",
-    relativePath: "docs/troubleshooting/rosetta.mdx",
-    compId: "articles-docs-troubleshooting-rosetta",
-    crumb: "Troubleshooting",
+    id: "rendermediaonlambda",
+    title: "renderMediaOnLambda()",
+    relativePath: "docs/lambda/rendermediaonlambda.mdx",
+    compId: "articles-docs-lambda-rendermediaonlambda",
+    crumb: "Lambda API",
   },
   {
-    id: "troubleshooting/delay-render-proxy",
-    title: "Loading <Img> with src http://localhost:3000/proxy",
-    relativePath: "docs/troubleshooting/delay-render-proxy.mdx",
-    compId: "articles-docs-troubleshooting-delay-render-proxy",
-    crumb: "Troubleshooting",
+    id: "renderstillonlambda",
+    title: "renderStillOnLambda()",
+    relativePath: "docs/lambda/renderstillonlambda.mdx",
+    compId: "articles-docs-lambda-renderstillonlambda",
+    crumb: "Lambda API",
   },
   {
-    id: "broken-fast-refresh",
-    title: "Fast Refresh not working",
-    relativePath: "docs/troubleshooting/broken-fast-refresh.mdx",
-    compId: "articles-docs-troubleshooting-broken-fast-refresh",
-    crumb: "Troubleshooting",
+    id: "rendervideoonlambda",
+    title: "renderVideoOnLambda()",
+    relativePath: "docs/lambda/rendervideoonlambda.mdx",
+    compId: "articles-docs-lambda-rendervideoonlambda",
+    crumb: "Lambda API",
   },
   {
-    id: "troubleshooting/timed-out-page-function",
-    title: "Timed out evaluating page function",
-    relativePath: "docs/troubleshooting/timed-out-page-function.mdx",
-    compId: "articles-docs-troubleshooting-timed-out-page-function",
-    crumb: "Troubleshooting",
+    id: "runtime",
+    title: "Runtime",
+    relativePath: "docs/lambda/runtime.mdx",
+    compId: "articles-docs-lambda-runtime",
+    crumb: "Lambda",
   },
   {
-    id: "troubleshooting/browser-launch",
-    title: "Failed to launch the browser process",
-    relativePath: "docs/troubleshooting/browser-launch.mdx",
-    compId: "articles-docs-troubleshooting-browser-launch",
-    crumb: "Troubleshooting",
+    id: "lambda/s3-public-access",
+    title: "Cannot create a S3 bucket using Remotion",
+    relativePath: "docs/lambda/s3-public-access.mdx",
+    compId: "articles-docs-lambda-s3-public-access",
+    crumb: "DevOps advisory",
   },
   {
-    id: "troubleshooting/sigkill",
-    title: "Process quit with signal SIGKILL",
-    relativePath: "docs/troubleshooting/sigkill.mdx",
-    compId: "articles-docs-troubleshooting-sigkill",
-    crumb: "Troubleshooting",
+    id: "lambda/serverless-framework-integration",
+    title: "Using the Serverless Framework with Remotion Lambda",
+    relativePath: "docs/lambda/serverless-framework-integration.mdx",
+    compId: "articles-docs-lambda-serverless-framework-integration",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "troubleshooting/nextjs-image",
-    title: "Flickering when using Next.js <Image> tag",
-    relativePath: "docs/troubleshooting/nextjs-image.mdx",
-    compId: "articles-docs-troubleshooting-nextjs-image",
-    crumb: "Common mistakes",
+    id: "setup",
+    title: "Setup",
+    relativePath: "docs/lambda/setup.mdx",
+    compId: "articles-docs-lambda-setup",
+    crumb: "Lambda",
   },
   {
-    id: "player-flicker",
-    title: "Avoiding flickering in <Player>",
-    relativePath: "docs/troubleshooting/video-flicker.mdx",
-    compId: "articles-docs-troubleshooting-video-flicker",
-    crumb: "Frame-perfection",
+    id: "simulatepermissions",
+    title: "simulatePermissions()",
+    relativePath: "docs/lambda/simulatepermissions.mdx",
+    compId: "articles-docs-lambda-simulatepermissions",
+    crumb: "Lambda API",
   },
   {
-    id: "troubleshooting/debug-failed-render",
-    title: "Debugging render failures",
-    relativePath: "docs/troubleshooting/debug-failed-render.mdx",
-    compId: "articles-docs-troubleshooting-debug-failed-render",
-    crumb: "Troubleshooting",
+    id: "speculatefunctionname",
+    title: "speculateFunctionName()",
+    relativePath: "docs/lambda/speculateFunctionName.mdx",
+    compId: "articles-docs-lambda-speculateFunctionName",
+    crumb: "Lambda API",
   },
   {
-    id: "defaultprops-too-big",
-    title: "defaultProps too big - could not serialize",
-    relativePath: "docs/troubleshooting/defaultprops-too-big.mdx",
-    compId: "articles-docs-troubleshooting-defaultprops-too-big",
-    crumb: "Troubleshooting",
+    id: "optimizing-speed",
+    title: "Optimizing for speed",
+    relativePath: "docs/lambda/speed.mdx",
+    compId: "articles-docs-lambda-speed",
+    crumb: "Lambda",
   },
   {
-    id: "troubleshooting/bundling-bundle",
-    title: "Calling bundle() in bundled code",
-    relativePath: "docs/troubleshooting/bundling-bundle.mdx",
-    compId: "articles-docs-troubleshooting-bundling-bundle",
-    crumb: "Troubleshooting",
+    id: "lambda/sqs",
+    title: "Using Lambda with SQS",
+    relativePath: "docs/lambda/sqs.mdx",
+    compId: "articles-docs-lambda-sqs",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "troubleshooting/could-not-be-parsed-as-a-value-list",
-    title: "The source provided could not be parsed as a value list",
-    relativePath:
-      "docs/troubleshooting/could-not-be-parsed-as-a-value-list.mdx",
-    compId: "articles-docs-troubleshooting-could-not-be-parsed-as-a-value-list",
-    crumb: "Browser quirks",
+    id: "bucket-disallows-acl",
+    title: "The bucket does not allow ACLs",
+    relativePath: "docs/lambda/troubleshooting/bucket-disallows-acl.mdx",
+    compId: "articles-docs-lambda-troubleshooting-bucket-disallows-acl",
+    crumb: "Lambda Troubleshooting",
   },
   {
-    id: "cli/studio",
-    title: "npx remotion studio",
-    relativePath: "docs/cli/studio.mdx",
-    compId: "articles-docs-cli-studio",
-    crumb: "CLI Reference",
+    id: "debug",
+    title: "Debugging failed Lambda renders",
+    relativePath: "docs/lambda/troubleshooting/debug.mdx",
+    compId: "articles-docs-lambda-troubleshooting-debug",
+    crumb: "Lambda",
   },
   {
-    id: "cli",
-    title: "Command line reference",
-    relativePath: "docs/cli/cli.mdx",
-    compId: "articles-docs-cli-cli",
-    crumb: null,
+    id: "permissions",
+    title: "AWS Permissions Troubleshooting",
+    relativePath: "docs/lambda/troubleshooting/permissions.mdx",
+    compId: "articles-docs-lambda-troubleshooting-permissions",
+    crumb: "Lambda Troubleshooting",
   },
   {
-    id: "cli/bundle",
-    title: "npx remotion bundle",
-    relativePath: "docs/cli/bundle.mdx",
-    compId: "articles-docs-cli-bundle",
-    crumb: "CLI Reference",
+    id: "rate-limit",
+    title: "AWS Rate Limit Troubleshooting",
+    relativePath: "docs/lambda/troubleshooting/rate-limit.mdx",
+    compId: "articles-docs-lambda-troubleshooting-rate-limit",
+    crumb: "Lambda Troubleshooting",
   },
   {
-    id: "cli/gpu",
-    title: "npx remotion gpu",
-    relativePath: "docs/cli/gpu.mdx",
-    compId: "articles-docs-cli-gpu",
-    crumb: "CLI Reference",
+    id: "lambda/troubleshooting/security-token",
+    title: "'\"The security token included in the request is invalid\"'",
+    relativePath: "docs/lambda/troubleshooting/security-token.mdx",
+    compId: "articles-docs-lambda-troubleshooting-security-token",
+    crumb: "Lambda",
   },
   {
-    id: "cli/install",
-    title: "npx remotion install",
-    relativePath: "docs/cli/install.mdx",
-    compId: "articles-docs-cli-install",
-    crumb: "CLI Reference",
+    id: "unrecognizedclientexception",
+    title: "UnrecognizedClientException",
+    relativePath: "docs/lambda/troubleshooting/unrecognizedclientexception.mdx",
+    compId: "articles-docs-lambda-troubleshooting-unrecognizedclientexception",
+    crumb: "Lambda Troubleshooting",
   },
   {
-    id: "cli/versions",
-    title: "npx remotion versions",
-    relativePath: "docs/cli/versions.mdx",
-    compId: "articles-docs-cli-versions",
-    crumb: "CLI Reference",
+    id: "uninstall",
+    title: "Uninstall Lambda",
+    relativePath: "docs/lambda/uninstall.mdx",
+    compId: "articles-docs-lambda-uninstall",
+    crumb: null,
+  },
+  {
+    id: "upgrading",
+    title: "Upgrading Lambda",
+    relativePath: "docs/lambda/upgrading.mdx",
+    compId: "articles-docs-lambda-upgrading",
+    crumb: null,
   },
   {
-    id: "cli/browser/index",
-    title: "npx remotion browser",
-    relativePath: "docs/cli/browser/index.mdx",
-    compId: "articles-docs-cli-browser-index",
-    crumb: "@remotion/cli",
+    id: "validatewebhooksignature",
+    title: "validateWebhookSignature()",
+    relativePath: "docs/lambda/validatewebhooksignature.mdx",
+    compId: "articles-docs-lambda-validatewebhooksignature",
+    crumb: "Lambda API",
   },
   {
-    id: "cli/browser/ensure",
-    title: "npx remotion browser ensure",
-    relativePath: "docs/cli/browser/ensure.mdx",
-    compId: "articles-docs-cli-browser-ensure",
-    crumb: "@remotion/cli",
+    id: "lambda/webhooks",
+    title: "Webhooks",
+    relativePath: "docs/lambda/webhooks.mdx",
+    compId: "articles-docs-lambda-webhooks",
+    crumb: "Lambda",
   },
   {
-    id: "ffprobe",
-    title: "npx remotion ffprobe",
-    relativePath: "docs/cli/ffprobe.mdx",
-    compId: "articles-docs-cli-ffprobe",
-    crumb: "@remotion/cli",
+    id: "lambda/without-iam/ec2",
+    title: "Authenticating Lambda with EC2",
+    relativePath: "docs/lambda/without-iam/ec2.mdx",
+    compId: "articles-docs-lambda-without-iam-ec2",
+    crumb: "@remotion/lambda",
   },
   {
-    id: "cli/render",
-    title: "npx remotion render",
-    relativePath: "docs/cli/render.mdx",
-    compId: "articles-docs-cli-render",
-    crumb: "CLI Reference",
+    id: "lambda/without-iam/example",
+    title: "Example setup without IAM user",
+    relativePath: "docs/lambda/without-iam/example.mdx",
+    compId: "articles-docs-lambda-without-iam-example",
+    crumb: "IAM Roles Example",
   },
   {
-    id: "cli/compositions",
-    title: "npx remotion compositions",
-    relativePath: "docs/cli/compositions.mdx",
-    compId: "articles-docs-cli-compositions",
-    crumb: "CLI Reference",
+    id: "lambda/without-iam/index",
+    title: "Using Lambda with IAM roles",
+    relativePath: "docs/lambda/without-iam/index.mdx",
+    compId: "articles-docs-lambda-without-iam-index",
+    crumb: "Lambda without IAM",
   },
   {
-    id: "cli/benchmark",
-    title: "npx remotion benchmark",
-    relativePath: "docs/cli/benchmark.mdx",
-    compId: "articles-docs-cli-benchmark",
-    crumb: "CLI Reference",
+    id: "lambda",
+    title: "@remotion/lambda",
+    relativePath: "docs/lambda.mdx",
+    compId: "articles-docs-lambda",
+    crumb: null,
   },
   {
-    id: "ffmpeg",
-    title: "npx remotion ffmpeg",
-    relativePath: "docs/cli/ffmpeg.mdx",
-    compId: "articles-docs-cli-ffmpeg",
-    crumb: "@remotion/cli",
+    id: "layers",
+    title: "Layers",
+    relativePath: "docs/layers.mdx",
+    compId: "articles-docs-layers",
+    crumb: "Designing videos",
   },
   {
-    id: "cli/still",
-    title: "npx remotion still",
-    relativePath: "docs/cli/still.mdx",
-    compId: "articles-docs-cli-still",
-    crumb: "CLI Reference",
+    id: "layout-utils/best-practices",
+    title: "Best practices for @remotion/layout-utils",
+    relativePath: "docs/layout-utils/best-practices.mdx",
+    compId: "articles-docs-layout-utils-best-practices",
+    crumb: "@remotion/layout-utils",
   },
   {
-    id: "cli/help",
-    title: "npx remotion help",
-    relativePath: "docs/cli/help.mdx",
-    compId: "articles-docs-cli-help",
-    crumb: "CLI Reference",
+    id: "layout-utils/fill-text-box",
+    title: "fillTextBox()",
+    relativePath: "docs/layout-utils/fill-text-box.mdx",
+    compId: "articles-docs-layout-utils-fill-text-box",
+    crumb: "@remotion/layout-utils",
   },
   {
-    id: "cli/upgrade",
-    title: "npx remotion upgrade",
-    relativePath: "docs/cli/upgrade.mdx",
-    compId: "articles-docs-cli-upgrade",
-    crumb: "CLI Reference",
+    id: "layout-utils/fit-text",
+    title: "fitText()",
+    relativePath: "docs/layout-utils/fit-text.mdx",
+    compId: "articles-docs-layout-utils-fit-text",
+    crumb: "@remotion/layout-utils",
   },
   {
-    id: "watchstaticfile",
-    title: "watchStaticFile()",
-    relativePath: "docs/watch-static-file.mdx",
-    compId: "articles-docs-watch-static-file",
-    crumb: "API",
+    id: "layout-utils/index",
+    title: "@remotion/layout-utils",
+    relativePath: "docs/layout-utils/index.mdx",
+    compId: "articles-docs-layout-utils-index",
+    crumb: null,
   },
   {
-    id: "chromium-flags",
-    title: "Chromium flags",
-    relativePath: "docs/chromium-flags.mdx",
-    compId: "articles-docs-chromium-flags",
-    crumb: "Tweaks",
+    id: "layout-utils/measure-text",
+    title: "measureText()",
+    relativePath: "docs/layout-utils/measure-text.mdx",
+    compId: "articles-docs-layout-utils-measure-text",
+    crumb: "@remotion/layout-utils",
   },
   {
-    id: "noise-visualization",
-    title: "Noise visualization",
-    relativePath: "docs/noise-visualization.mdx",
-    compId: "articles-docs-noise-visualization",
-    crumb: "Techniques",
+    id: "legacy-babel",
+    title: "Using legacy Babel transpilation",
+    relativePath: "docs/legacy-babel-loader.mdx",
+    compId: "articles-docs-legacy-babel-loader",
+    crumb: "How To",
   },
   {
-    id: "acknowledgements",
-    title: "Acknowledgements",
-    relativePath: "docs/acknowledgements.mdx",
-    compId: "articles-docs-acknowledgements",
-    crumb: "Credits",
+    id: "license",
+    title: "License & Pricing",
+    relativePath: "docs/license.mdx",
+    compId: "articles-docs-license",
+    crumb: null,
   },
   {
-    id: "miscellaneous/snippets/accelerated-video",
-    title: "Change the speed of a video over time",
-    relativePath: "docs/miscellaneous/snippets/accelerated-video.mdx",
-    compId: "articles-docs-miscellaneous-snippets-accelerated-video",
-    crumb: "Snippets",
+    id: "loop",
+    title: "<Loop>",
+    relativePath: "docs/loop.mdx",
+    compId: "articles-docs-loop",
+    crumb: "API",
   },
   {
-    id: "miscellaneous/snippets/fps-converter",
-    title: "FPS converter",
-    relativePath: "docs/miscellaneous/snippets/fps-converter.mdx",
-    compId: "articles-docs-miscellaneous-snippets-fps-converter",
-    crumb: "Snippets",
+    id: "getlottiemetadata",
+    title: "getLottieMetadata()",
+    relativePath: "docs/lottie/getlottiemetadata.mdx",
+    compId: "articles-docs-lottie-getlottiemetadata",
+    crumb: "@remotion/lottie",
   },
   {
-    id: "hls",
-    title: "HLS support (HTTP Live Streaming)",
-    relativePath: "docs/miscellaneous/snippets/hls.mdx",
-    compId: "articles-docs-miscellaneous-snippets-hls",
-    crumb: "Video",
+    id: "lottie-index",
+    title: "@remotion/lottie",
+    relativePath: "docs/lottie/index.mdx",
+    compId: "articles-docs-lottie-index",
+    crumb: null,
   },
   {
-    id: "miscellaneous/snippets/offthread-video-while-rendering",
-    title: "<OffthreadVideo /> while rendering",
-    relativePath:
-      "docs/miscellaneous/snippets/offthread-video-while-rendering.mdx",
-    compId:
-      "articles-docs-miscellaneous-snippets-offthread-video-while-rendering",
-    crumb: "Snippets",
+    id: "lottie-comp",
+    title: "<Lottie>",
+    relativePath: "docs/lottie/lottie-comp.mdx",
+    compId: "articles-docs-lottie-lottie-comp",
+    crumb: "@remotion/lottie",
   },
   {
-    id: "miscellaneous/snippets/player-in-iframe",
-    title: "Embedding a <Player> into an <iframe>",
-    relativePath: "docs/miscellaneous/snippets/player-in-iframe.mdx",
-    compId: "articles-docs-miscellaneous-snippets-player-in-iframe",
-    crumb: "Snippets",
+    id: "lottie-lottiefiles",
+    title: "Finding Lottie files to use",
+    relativePath: "docs/lottie/lottie-lottiefiles.mdx",
+    compId: "articles-docs-lottie-lottie-lottiefiles",
+    crumb: "Resources",
   },
   {
-    id: "miscellaneous/snippets/combine-compositions",
-    title: "How do I combine compositions?",
-    relativePath: "docs/miscellaneous/snippets/combine-compositions.mdx",
-    compId: "articles-docs-miscellaneous-snippets-combine-compositions",
-    crumb: "FAQ",
+    id: "lottie-remote",
+    title: "Loading Lottie animations from a URL",
+    relativePath: "docs/lottie/lottie-remote.mdx",
+    compId: "articles-docs-lottie-lottie-remote",
+    crumb: "@remotion/lottie",
   },
   {
-    id: "miscellaneous/snippets/align-duration",
-    title: "How do I make the composition the same duration as my video?",
-    relativePath: "docs/miscellaneous/snippets/align-duration.mdx",
-    compId: "articles-docs-miscellaneous-snippets-align-duration",
-    crumb: "FAQ",
+    id: "lottie-staticfile",
+    title: "Loading Lottie animations from staticFile()",
+    relativePath: "docs/lottie/lottie-staticfile.mdx",
+    compId: "articles-docs-lottie-lottie-staticfile",
+    crumb: "@remotion/lottie",
   },
   {
-    id: "miscellaneous/snippets/use-delay-render",
-    title: "useDelayRender()",
-    relativePath: "docs/miscellaneous/snippets/use-delay-render.mdx",
-    compId: "articles-docs-miscellaneous-snippets-use-delay-render",
-    crumb: "Snippets",
+    id: "measure-spring",
+    title: "measureSpring()",
+    relativePath: "docs/measure-spring.mdx",
+    compId: "articles-docs-measure-spring",
+    crumb: "API",
   },
   {
-    id: "miscellaneous/linux-single-process",
-    title: "Multiple cores on Linux",
-    relativePath: "docs/miscellaneous/linux-single-process.mdx",
-    compId: "articles-docs-miscellaneous-linux-single-process",
-    crumb: "Server-side rendering",
+    id: "measuring",
+    title: "Measuring DOM nodes",
+    relativePath: "docs/measuring.mdx",
+    compId: "articles-docs-measuring",
+    crumb: "How To",
   },
   {
-    id: "miscellaneous/vercel-functions",
-    title: "Can I render videos using Vercel Serverless functions?",
-    relativePath: "docs/miscellaneous/vercel-functions.mdx",
-    compId: "articles-docs-miscellaneous-vercel-functions",
-    crumb: "FAQ",
+    id: "media-playback-error",
+    title: "Could not play video/audio with src",
+    relativePath: "docs/media-playback-error.mdx",
+    compId: "articles-docs-media-playback-error",
+    crumb: "Troubleshooting",
   },
   {
-    id: "miscellaneous/chrome-headless-shell",
-    title: "Chrome Headless Shell",
-    relativePath: "docs/miscellaneous/chrome-headless-shell.mdx",
-    compId: "articles-docs-miscellaneous-chrome-headless-shell",
-    crumb: "FAQ",
+    id: "media-utils/index",
+    title: "@remotion/media-utils",
+    relativePath: "docs/media-utils/index.mdx",
+    compId: "articles-docs-media-utils-index",
+    crumb: null,
   },
   {
-    id: "miscellaneous/render-in-browser",
-    title: "Can I render videos in the browser?",
-    relativePath: "docs/miscellaneous/render-in-browser.mdx",
-    compId: "articles-docs-miscellaneous-render-in-browser",
+    id: "miscellaneous/absolute-paths",
+    title: "Files with absolute paths",
+    relativePath: "docs/miscellaneous/absolute-paths.mdx",
+    compId: "articles-docs-miscellaneous-absolute-paths",
     crumb: "FAQ",
   },
   {
@@ -1549,13 +1700,6 @@ export const articles = [
     compId: "articles-docs-miscellaneous-automatic-duration",
     crumb: "FAQ",
   },
-  {
-    id: "typescript-aliases",
-    title: "TypeScript aliases",
-    relativePath: "docs/miscellaneous/ts-aliases.mdx",
-    compId: "articles-docs-miscellaneous-ts-aliases",
-    crumb: "How to",
-  },
   {
     id: "miscellaneous/changing-temp-dir",
     title: "Changing the temporary directory",
@@ -1564,17 +1708,10 @@ export const articles = [
     crumb: "Advanced configuration",
   },
   {
-    id: "embed-studio",
-    title: "Can I embed the Remotion Studio?",
-    relativePath: "docs/miscellaneous/embed-remotion-studio.mdx",
-    compId: "articles-docs-miscellaneous-embed-remotion-studio",
-    crumb: "FAQ",
-  },
-  {
-    id: "miscellaneous/render-on-edge",
-    title: "Can I render videos on the edge?",
-    relativePath: "docs/miscellaneous/render-on-edge.mdx",
-    compId: "articles-docs-miscellaneous-render-on-edge",
+    id: "miscellaneous/chrome-headless-shell",
+    title: "Chrome Headless Shell",
+    relativePath: "docs/miscellaneous/chrome-headless-shell.mdx",
+    compId: "articles-docs-miscellaneous-chrome-headless-shell",
     crumb: "FAQ",
   },
   {
@@ -1591,6 +1728,13 @@ export const articles = [
     compId: "articles-docs-miscellaneous-cloud-gpu",
     crumb: "FAQ",
   },
+  {
+    id: "embed-studio",
+    title: "Can I embed the Remotion Studio?",
+    relativePath: "docs/miscellaneous/embed-remotion-studio.mdx",
+    compId: "articles-docs-miscellaneous-embed-remotion-studio",
+    crumb: "FAQ",
+  },
   {
     id: "miscellaneous/linux-dependencies",
     title: "Linux Dependencies",
@@ -1599,11 +1743,11 @@ export const articles = [
     crumb: "FAQ",
   },
   {
-    id: "miscellaneous/video-formats",
-    title: "Which video formats does Remotion support?",
-    relativePath: "docs/miscellaneous/video-formats.mdx",
-    compId: "articles-docs-miscellaneous-video-formats",
-    crumb: "FAQ",
+    id: "miscellaneous/linux-single-process",
+    title: "Multiple cores on Linux",
+    relativePath: "docs/miscellaneous/linux-single-process.mdx",
+    compId: "articles-docs-miscellaneous-linux-single-process",
+    crumb: "Server-side rendering",
   },
   {
     id: "miscellaneous/live-streaming",
@@ -1613,333 +1757,181 @@ export const articles = [
     crumb: "FAQ",
   },
   {
-    id: "miscellaneous/absolute-paths",
-    title: "Files with absolute paths",
-    relativePath: "docs/miscellaneous/absolute-paths.mdx",
-    compId: "articles-docs-miscellaneous-absolute-paths",
+    id: "miscellaneous/render-in-browser",
+    title: "Can I render videos in the browser?",
+    relativePath: "docs/miscellaneous/render-in-browser.mdx",
+    compId: "articles-docs-miscellaneous-render-in-browser",
     crumb: "FAQ",
   },
   {
-    id: "use-audio-data",
-    title: "useAudioData()",
-    relativePath: "docs/use-audio-data.mdx",
-    compId: "articles-docs-use-audio-data",
-    crumb: "@remotion/media-utils",
-  },
-  {
-    id: "calculate-metadata",
-    title: "calculateMetadata()",
-    relativePath: "docs/calculate-metadata.mdx",
-    compId: "articles-docs-calculate-metadata",
-    crumb: "API",
-  },
-  {
-    id: "passing-props",
-    title: "Passing props to a composition",
-    relativePath: "docs/passing-props.mdx",
-    compId: "articles-docs-passing-props",
-    crumb: "How To",
-  },
-  {
-    id: "standalone",
-    title: "Useful functions outside of Remotion",
-    relativePath: "docs/standalone.mdx",
-    compId: "articles-docs-standalone",
-    crumb: null,
-  },
-  {
-    id: "ssr-node",
-    title: "Rendering using SSR APIs",
-    relativePath: "docs/ssr-node.mdx",
-    compId: "articles-docs-ssr-node",
-    crumb: "Server-side rendering",
-  },
-  {
-    id: "tailwind",
-    title: "@remotion/tailwind",
-    relativePath: "docs/tailwind/overview.mdx",
-    compId: "articles-docs-tailwind-overview",
-    crumb: null,
-  },
-  {
-    id: "tailwind/enable-tailwind",
-    title: "enableTailwind()",
-    relativePath: "docs/tailwind/enable-tailwind.mdx",
-    compId: "articles-docs-tailwind-enable-tailwind",
-    crumb: "@remotion/tailwind",
-  },
-  {
-    id: "upgrading",
-    title: "Upgrading Remotion",
-    relativePath: "docs/upgrading.mdx",
-    compId: "articles-docs-upgrading",
-    crumb: "upgrade remotion",
-  },
-  {
-    id: "getregions",
-    title: "getRegions()",
-    relativePath: "docs/cloudrun/getregions.mdx",
-    compId: "articles-docs-cloudrun-getregions",
-    crumb: "Cloud Run API",
-  },
-  {
-    id: "cloudrun/multiple-buckets",
-    title: "Multiple buckets in Remotion Cloud Run",
-    relativePath: "docs/cloudrun/multiple-buckets.mdx",
-    compId: "articles-docs-cloudrun-multiple-buckets",
-    crumb: "@remotion/cloudrun",
-  },
-  {
-    id: "instancecount",
-    title: "Instance Count",
-    relativePath: "docs/cloudrun/instancecount.mdx",
-    compId: "articles-docs-cloudrun-instancecount",
-    crumb: "Cloud Run",
-  },
-  {
-    id: "cli",
-    title: "@remotion/cloudrun - CLI",
-    relativePath: "docs/cloudrun/cli.mdx",
-    compId: "articles-docs-cloudrun-cli",
-    crumb: null,
-  },
-  {
-    id: "deployservice",
-    title: "deployService()",
-    relativePath: "docs/cloudrun/deployservice.mdx",
-    compId: "articles-docs-cloudrun-deployservice",
-    crumb: "Cloud Run API",
-  },
-  {
-    id: "getsites",
-    title: "getSites()",
-    relativePath: "docs/cloudrun/getsites.mdx",
-    compId: "articles-docs-cloudrun-getsites",
-    crumb: "Cloud Run API",
-  },
-  {
-    id: "deleteservice",
-    title: "deleteService()",
-    relativePath: "docs/cloudrun/deleteservice.mdx",
-    compId: "articles-docs-cloudrun-deleteservice",
-    crumb: "Cloud Run API",
-  },
-  {
-    id: "generate-env",
-    title: "Generate .env File",
-    relativePath: "docs/cloudrun/generate-env.mdx",
-    compId: "articles-docs-cloudrun-generate-env",
-    crumb: "Cloud Run",
-  },
-  {
-    id: "cloudrun/permissions",
-    title: "Permissions",
-    relativePath: "docs/cloudrun/permissions.mdx",
-    compId: "articles-docs-cloudrun-permissions",
-    crumb: "Cloud Run",
-  },
-  {
-    id: "renderstilloncloudrun",
-    title: "renderStillOnCloudrun()",
-    relativePath: "docs/cloudrun/renderstilloncloudrun.mdx",
-    compId: "articles-docs-cloudrun-renderstilloncloudrun",
-    crumb: "Cloud Run API",
-  },
-  {
-    id: "getorcreatebucket",
-    title: "getOrCreateBucket()",
-    relativePath: "docs/cloudrun/getorcreatebucket.mdx",
-    compId: "articles-docs-cloudrun-getorcreatebucket",
-    crumb: "Cloud Run API",
-  },
-  {
-    id: "testpermissions",
-    title: "testPermissions()",
-    relativePath: "docs/cloudrun/testpermissions.mdx",
-    compId: "articles-docs-cloudrun-testpermissions",
-    crumb: "Cloud Run API",
-  },
-  {
-    id: "cloudrun/light-client",
-    title: "Light client",
-    relativePath: "docs/cloudrun/light-client.mdx",
-    compId: "articles-docs-cloudrun-light-client",
-    crumb: "Cloud Run",
-  },
-  {
-    id: "services",
-    title: "npx remotion cloudrun services",
-    relativePath: "docs/cloudrun/cli/services.mdx",
-    compId: "articles-docs-cloudrun-cli-services",
-    crumb: "Cloud Run CLI Reference",
-  },
-  {
-    id: "permissions",
-    title: "npx remotion cloudrun permissions",
-    relativePath: "docs/cloudrun/cli/permissions.mdx",
-    compId: "articles-docs-cloudrun-cli-permissions",
-    crumb: "Cloud Run CLI Reference",
-  },
-  {
-    id: "sites",
-    title: "npx remotion cloudrun sites",
-    relativePath: "docs/cloudrun/cli/sites.mdx",
-    compId: "articles-docs-cloudrun-cli-sites",
-    crumb: "Cloud Run CLI Reference",
+    id: "miscellaneous/render-on-edge",
+    title: "Can I render videos on the edge?",
+    relativePath: "docs/miscellaneous/render-on-edge.mdx",
+    compId: "articles-docs-miscellaneous-render-on-edge",
+    crumb: "FAQ",
   },
   {
-    id: "render",
-    title: "npx remotion cloudrun render",
-    relativePath: "docs/cloudrun/cli/render.mdx",
-    compId: "articles-docs-cloudrun-cli-render",
-    crumb: "Cloud Run CLI Reference",
+    id: "miscellaneous/snippets/accelerated-video",
+    title: "Change the speed of a video over time",
+    relativePath: "docs/miscellaneous/snippets/accelerated-video.mdx",
+    compId: "articles-docs-miscellaneous-snippets-accelerated-video",
+    crumb: "Snippets",
   },
   {
-    id: "regions",
-    title: "npx remotion cloudrun regions",
-    relativePath: "docs/cloudrun/cli/regions.mdx",
-    compId: "articles-docs-cloudrun-cli-regions",
-    crumb: "Cloud Run CLI Reference",
+    id: "miscellaneous/snippets/align-duration",
+    title: "How do I make the composition the same duration as my video?",
+    relativePath: "docs/miscellaneous/snippets/align-duration.mdx",
+    compId: "articles-docs-miscellaneous-snippets-align-duration",
+    crumb: "FAQ",
   },
   {
-    id: "still",
-    title: "npx remotion cloudrun still",
-    relativePath: "docs/cloudrun/cli/still.mdx",
-    compId: "articles-docs-cloudrun-cli-still",
-    crumb: "Cloud Run CLI Reference",
+    id: "miscellaneous/snippets/combine-compositions",
+    title: "How do I combine compositions?",
+    relativePath: "docs/miscellaneous/snippets/combine-compositions.mdx",
+    compId: "articles-docs-miscellaneous-snippets-combine-compositions",
+    crumb: "FAQ",
   },
   {
-    id: "upgrading",
-    title: "Upgrading Cloud Run",
-    relativePath: "docs/cloudrun/upgrading.mdx",
-    compId: "articles-docs-cloudrun-upgrading",
-    crumb: null,
+    id: "miscellaneous/snippets/fps-converter",
+    title: "FPS converter",
+    relativePath: "docs/miscellaneous/snippets/fps-converter.mdx",
+    compId: "articles-docs-miscellaneous-snippets-fps-converter",
+    crumb: "Snippets",
   },
   {
-    id: "region-selection",
-    title: "Region selection",
-    relativePath: "docs/cloudrun/region-selection.mdx",
-    compId: "articles-docs-cloudrun-region-selection",
-    crumb: "Cloud Run",
+    id: "hls",
+    title: "HLS support (HTTP Live Streaming)",
+    relativePath: "docs/miscellaneous/snippets/hls.mdx",
+    compId: "articles-docs-miscellaneous-snippets-hls",
+    crumb: "Video",
   },
   {
-    id: "getservices",
-    title: "getServices()",
-    relativePath: "docs/cloudrun/getservices.mdx",
-    compId: "articles-docs-cloudrun-getservices",
-    crumb: "Cloud Run API",
+    id: "miscellaneous/snippets/offthread-video-while-rendering",
+    title: "<OffthreadVideo /> while rendering",
+    relativePath:
+      "docs/miscellaneous/snippets/offthread-video-while-rendering.mdx",
+    compId:
+      "articles-docs-miscellaneous-snippets-offthread-video-while-rendering",
+    crumb: "Snippets",
   },
   {
-    id: "getserviceinfo",
-    title: "getServiceInfo()",
-    relativePath: "docs/cloudrun/getServiceinfo.mdx",
-    compId: "articles-docs-cloudrun-getServiceinfo",
-    crumb: "Cloud Run API",
+    id: "miscellaneous/snippets/player-in-iframe",
+    title: "Embedding a <Player> into an <iframe>",
+    relativePath: "docs/miscellaneous/snippets/player-in-iframe.mdx",
+    compId: "articles-docs-miscellaneous-snippets-player-in-iframe",
+    crumb: "Snippets",
   },
   {
-    id: "checklist",
-    title: "Production Checklist",
-    relativePath: "docs/cloudrun/checklist.mdx",
-    compId: "articles-docs-cloudrun-checklist",
-    crumb: "Cloud Run",
+    id: "miscellaneous/snippets/use-delay-render",
+    title: "useDelayRender()",
+    relativePath: "docs/miscellaneous/snippets/use-delay-render.mdx",
+    compId: "articles-docs-miscellaneous-snippets-use-delay-render",
+    crumb: "Snippets",
   },
   {
-    id: "cloudrun/api",
-    title: "@remotion/cloudrun",
-    relativePath: "docs/cloudrun/api.mdx",
-    compId: "articles-docs-cloudrun-api",
-    crumb: "Render videos without servers on GCP",
+    id: "typescript-aliases",
+    title: "TypeScript aliases",
+    relativePath: "docs/miscellaneous/ts-aliases.mdx",
+    compId: "articles-docs-miscellaneous-ts-aliases",
+    crumb: "How to",
   },
   {
-    id: "setup",
-    title: "Setup",
-    relativePath: "docs/cloudrun/setup.mdx",
-    compId: "articles-docs-cloudrun-setup",
-    crumb: "Cloud Run",
+    id: "miscellaneous/vercel-functions",
+    title: "Can I render videos using Vercel Serverless functions?",
+    relativePath: "docs/miscellaneous/vercel-functions.mdx",
+    compId: "articles-docs-miscellaneous-vercel-functions",
+    crumb: "FAQ",
   },
   {
-    id: "rendermediaoncloudrun",
-    title: "renderMediaOnCloudrun()",
-    relativePath: "docs/cloudrun/rendermediaoncloudrun.mdx",
-    compId: "articles-docs-cloudrun-rendermediaoncloudrun",
-    crumb: "Cloud Run API",
+    id: "miscellaneous/video-formats",
+    title: "Which video formats does Remotion support?",
+    relativePath: "docs/miscellaneous/video-formats.mdx",
+    compId: "articles-docs-miscellaneous-video-formats",
+    crumb: "FAQ",
   },
   {
-    id: "speculateservicename",
-    title: "speculateServiceName()",
-    relativePath: "docs/cloudrun/speculateservicename.mdx",
-    compId: "articles-docs-cloudrun-speculateservicename",
-    crumb: "Cloud Run API",
+    id: "motion-blur/camera-motion-blur",
+    title: "<CameraMotionBlur>",
+    relativePath: "docs/motion-blur/camera-motion-blur.mdx",
+    compId: "articles-docs-motion-blur-camera-motion-blur",
+    crumb: "Realistic camera effect",
   },
   {
-    id: "cloudrun/limits",
-    title: "Cloud Run Limits",
-    relativePath: "docs/cloudrun/limits.mdx",
-    compId: "articles-docs-cloudrun-limits",
-    crumb: "Cloud Run",
+    id: "motion-blur/index",
+    title: "@remotion/motion-blur",
+    relativePath: "docs/motion-blur/index.mdx",
+    compId: "articles-docs-motion-blur-index",
+    crumb: null,
   },
   {
-    id: "deploysite",
-    title: "deploySite()",
-    relativePath: "docs/cloudrun/deploysite.mdx",
-    compId: "articles-docs-cloudrun-deploysite",
-    crumb: "Cloud Run API",
+    id: "motion-blur/motion-blur",
+    title: "<MotionBlur>",
+    relativePath: "docs/motion-blur/motion-blur.mdx",
+    compId: "articles-docs-motion-blur-motion-blur",
+    crumb: null,
   },
   {
-    id: "uninstall",
-    title: "Uninstall Cloud Run",
-    relativePath: "docs/cloudrun/uninstall.mdx",
-    compId: "articles-docs-cloudrun-uninstall",
+    id: "motion-blur/trail",
+    title: "<Trail>",
+    relativePath: "docs/motion-blur/trail.mdx",
+    compId: "articles-docs-motion-blur-trail",
+    crumb: "Motion blur",
+  },
+  {
+    id: "noise/index",
+    title: "@remotion/noise",
+    relativePath: "docs/noise/index.mdx",
+    compId: "articles-docs-noise-index",
     crumb: null,
   },
   {
-    id: "deletesite",
-    title: "deleteSite()",
-    relativePath: "docs/cloudrun/deletesite.mdx",
-    compId: "articles-docs-cloudrun-deletesite",
-    crumb: "Cloud Run API",
+    id: "noise/noise-2d",
+    title: "noise2D()",
+    relativePath: "docs/noise/noise-2d.mdx",
+    compId: "articles-docs-noise-noise-2d",
+    crumb: "Make some",
   },
   {
-    id: "reusability",
-    title: "Making components reusable",
-    relativePath: "docs/sequences.mdx",
-    compId: "articles-docs-sequences",
-    crumb: "The power of React",
+    id: "noise/noise-3d",
+    title: "noise3D()",
+    relativePath: "docs/noise/noise-3d.mdx",
+    compId: "articles-docs-noise-noise-3d",
+    crumb: "Make some",
   },
   {
-    id: "three-canvas",
-    title: "<ThreeCanvas>",
-    relativePath: "docs/three-canvas.mdx",
-    compId: "articles-docs-three-canvas",
-    crumb: "@remotion/three",
+    id: "noise/noise-4d",
+    title: "noise4D()",
+    relativePath: "docs/noise/noise-4d.mdx",
+    compId: "articles-docs-noise-noise-4d",
+    crumb: "Make some",
   },
   {
-    id: "version-mismatch",
-    title: "Version mismatch",
-    relativePath: "docs/version-mismatch.mdx",
-    compId: "articles-docs-version-mismatch",
-    crumb: "How to fix a",
+    id: "noise-visualization",
+    title: "Noise visualization",
+    relativePath: "docs/noise-visualization.mdx",
+    compId: "articles-docs-noise-visualization",
+    crumb: "Techniques",
   },
   {
-    id: "iframe",
-    title: "<IFrame>",
-    relativePath: "docs/iframe.mdx",
-    compId: "articles-docs-iframe",
-    crumb: "API",
+    id: "non-seekable-media",
+    title: "Non-seekable media",
+    relativePath: "docs/non-seekable-media.mdx",
+    compId: "articles-docs-non-seekable-media",
+    crumb: "Troubleshooting",
   },
   {
-    id: "cloudrun",
-    title: "@remotion/cloudrun",
-    relativePath: "docs/cloudrun.mdx",
-    compId: "articles-docs-cloudrun",
-    crumb: null,
+    id: '"null"',
+    title: "<Experimental.Null>",
+    relativePath: "docs/null.mdx",
+    compId: "articles-docs-null",
+    crumb: "Experimental API",
   },
   {
-    id: "transitioning",
-    title: "Transitions",
-    relativePath: "docs/transitions.mdx",
-    compId: "articles-docs-transitions",
-    crumb: "Techniques",
+    id: "offthreadvideo",
+    title: "<OffthreadVideo>",
+    relativePath: "docs/offthreadvideo.mdx",
+    compId: "articles-docs-offthreadvideo",
+    crumb: "API",
   },
   {
     id: "gl-options",
@@ -1949,172 +1941,172 @@ export const articles = [
     crumb: "Flags",
   },
   {
-    id: "interpolate-colors",
-    title: "interpolateColors()",
-    relativePath: "docs/interpolate-colors.mdx",
-    compId: "articles-docs-interpolate-colors",
-    crumb: "API",
+    id: "overlay",
+    title: "Creating overlays",
+    relativePath: "docs/overlay.mdx",
+    compId: "articles-docs-overlay",
+    crumb: null,
   },
   {
-    id: "using-randomness",
-    title: "Using randomness",
-    relativePath: "docs/using-randomness.mdx",
-    compId: "articles-docs-using-randomness",
-    crumb: "Roll the dice",
+    id: "webpack",
+    title: "Custom Webpack config",
+    relativePath: "docs/overwriting-webpack-config.mdx",
+    compId: "articles-docs-overwriting-webpack-config",
+    crumb: "How To",
   },
   {
-    id: "5-0-migration",
-    title: "v5.0 Migration",
-    relativePath: "docs/5-0-migration.mdx",
-    compId: "articles-docs-5-0-migration",
-    crumb: "Version Upgrade",
+    id: "parameterized-rendering",
+    title: "Parameterized videos",
+    relativePath: "docs/parameterized-rendering.mdx",
+    compId: "articles-docs-parameterized-rendering",
+    crumb: "How To",
   },
   {
-    id: "get-input-props",
-    title: "getInputProps()",
-    relativePath: "docs/get-input-props.mdx",
-    compId: "articles-docs-get-input-props",
-    crumb: "API",
+    id: "passing-props",
+    title: "Passing props to a composition",
+    relativePath: "docs/passing-props.mdx",
+    compId: "articles-docs-passing-props",
+    crumb: "How To",
   },
   {
-    id: "measuring",
-    title: "Measuring DOM nodes",
-    relativePath: "docs/measuring.mdx",
-    compId: "articles-docs-measuring",
-    crumb: "How To",
+    id: "paths/evolve-path",
+    title: "evolvePath()",
+    relativePath: "docs/paths/evolve-path.mdx",
+    compId: "articles-docs-paths-evolve-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "2-0-migration",
-    title: "v2.0 Migration",
-    relativePath: "docs/2-0-migration.mdx",
-    compId: "articles-docs-2-0-migration",
-    crumb: "Version Upgrade",
+    id: "paths/extend-viewbox",
+    title: "extendViewBox()",
+    relativePath: "docs/paths/extend-viewbox.mdx",
+    compId: "articles-docs-paths-extend-viewbox",
+    crumb: "@remotion/paths",
   },
   {
-    id: "getting-started",
-    title: "Creating a new project",
-    relativePath: "docs/getting-started.mdx",
-    compId: "articles-docs-getting-started",
-    crumb: "Let's begin!",
+    id: "paths/get-bounding-box",
+    title: "getBoundingBox()",
+    relativePath: "docs/paths/get-bounding-box.mdx",
+    compId: "articles-docs-paths-get-bounding-box",
+    crumb: "@remotion/paths",
   },
   {
-    id: "google-fonts",
-    title: "@remotion/google-fonts",
-    relativePath: "docs/google-fonts/index.mdx",
-    compId: "articles-docs-google-fonts-index",
-    crumb: null,
+    id: "paths/get-instruction-index-at-length",
+    title: "getInstructionIndexAtLength()",
+    relativePath: "docs/paths/get-instruction-index-at-length.mdx",
+    compId: "articles-docs-paths-get-instruction-index-at-length",
+    crumb: "@remotion/paths",
   },
   {
-    id: "google-fonts/get-available-fonts",
-    title: "getAvailableFonts()",
-    relativePath: "docs/google-fonts/get-available-fonts.mdx",
-    compId: "articles-docs-google-fonts-get-available-fonts",
-    crumb: "@remotion/google-fonts",
+    id: "paths/get-length",
+    title: "getLength()",
+    relativePath: "docs/paths/get-length.mdx",
+    compId: "articles-docs-paths-get-length",
+    crumb: "@remotion/paths",
   },
   {
-    id: "google-fonts/get-info",
-    title: "getInfo()",
-    relativePath: "docs/google-fonts/get-info.mdx",
-    compId: "articles-docs-google-fonts-get-info",
-    crumb: "@remotion/google-fonts",
+    id: "paths/get-parts",
+    title: "getParts()",
+    relativePath: "docs/paths/get-parts.mdx",
+    compId: "articles-docs-paths-get-parts",
+    crumb: "@remotion/paths",
   },
   {
-    id: "google-fonts/load-font",
-    title: "loadFont()",
-    relativePath: "docs/google-fonts/load-font.mdx",
-    compId: "articles-docs-google-fonts-load-font",
-    crumb: "@remotion/google-fonts",
+    id: "paths/get-point-at-length",
+    title: "getPointAtLength()",
+    relativePath: "docs/paths/get-point-at-length.mdx",
+    compId: "articles-docs-paths-get-point-at-length",
+    crumb: "@remotion/paths",
   },
   {
-    id: "media-utils/index",
-    title: "@remotion/media-utils",
-    relativePath: "docs/media-utils/index.mdx",
-    compId: "articles-docs-media-utils-index",
-    crumb: null,
+    id: "paths/get-subpaths",
+    title: "getSubpaths()",
+    relativePath: "docs/paths/get-subpaths.mdx",
+    compId: "articles-docs-paths-get-subpaths",
+    crumb: "@remotion/paths",
   },
   {
-    id: "noise/noise-4d",
-    title: "noise4D()",
-    relativePath: "docs/noise/noise-4d.mdx",
-    compId: "articles-docs-noise-noise-4d",
-    crumb: "Make some",
+    id: "paths/get-tangent-at-length",
+    title: "getTangentAtLength()",
+    relativePath: "docs/paths/get-tangent-at-length.mdx",
+    compId: "articles-docs-paths-get-tangent-at-length",
+    crumb: "@remotion/paths",
   },
   {
-    id: "noise/noise-3d",
-    title: "noise3D()",
-    relativePath: "docs/noise/noise-3d.mdx",
-    compId: "articles-docs-noise-noise-3d",
-    crumb: "Make some",
+    id: "paths/index",
+    title: "@remotion/paths",
+    relativePath: "docs/paths/index.mdx",
+    compId: "articles-docs-paths-index",
+    crumb: "SVG",
   },
   {
-    id: "noise/index",
-    title: "@remotion/noise",
-    relativePath: "docs/noise/index.mdx",
-    compId: "articles-docs-noise-index",
-    crumb: null,
+    id: "paths/interpolate-path",
+    title: "interpolatePath()",
+    relativePath: "docs/paths/interpolate-path.mdx",
+    compId: "articles-docs-paths-interpolate-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "noise/noise-2d",
-    title: "noise2D()",
-    relativePath: "docs/noise/noise-2d.mdx",
-    compId: "articles-docs-noise-noise-2d",
-    crumb: "Make some",
+    id: "paths/normalize-path",
+    title: "normalizePath()",
+    relativePath: "docs/paths/normalize-path.mdx",
+    compId: "articles-docs-paths-normalize-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "security",
-    title: "Security Best Practices",
-    relativePath: "docs/security.mdx",
-    compId: "articles-docs-security",
-    crumb: "FAQ",
+    id: "paths/parse-path",
+    title: "parsePath()",
+    relativePath: "docs/paths/parse-path.mdx",
+    compId: "articles-docs-paths-parse-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "delay-render",
-    title: "delayRender() and continueRender()",
-    relativePath: "docs/delay-render.mdx",
-    compId: "articles-docs-delay-render",
-    crumb: "How to",
+    id: "paths/reduce-instructions",
+    title: "reduceInstructions()",
+    relativePath: "docs/paths/reduce-instructions.mdx",
+    compId: "articles-docs-paths-reduce-instructions",
+    crumb: "@remotion/paths",
   },
   {
-    id: "loop",
-    title: "<Loop>",
-    relativePath: "docs/loop.mdx",
-    compId: "articles-docs-loop",
-    crumb: "API",
+    id: "paths/reset-path",
+    title: "resetPath()",
+    relativePath: "docs/paths/reset-path.mdx",
+    compId: "articles-docs-paths-reset-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "render",
-    title: "Render your video",
-    relativePath: "docs/render.mdx",
-    compId: "articles-docs-render",
-    crumb: "How To",
+    id: "paths/reverse-path",
+    title: "reversePath()",
+    relativePath: "docs/paths/reverse-path.mdx",
+    compId: "articles-docs-paths-reverse-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "ssr-legacy",
-    title: "Server-Side Rendering (v1 and v2)",
-    relativePath: "docs/ssr-legacy.mdx",
-    compId: "articles-docs-ssr-legacy",
-    crumb: "Legacy docs",
+    id: "paths/scale-path",
+    title: "scalePath()",
+    relativePath: "docs/paths/scale-path.mdx",
+    compId: "articles-docs-paths-scale-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "continue-render",
-    title: "continueRender()",
-    relativePath: "docs/continue-render.mdx",
-    compId: "articles-docs-continue-render",
-    crumb: "API",
+    id: "paths/serialize-instructions",
+    title: "serializeInstructions()",
+    relativePath: "docs/paths/serialize-instructions.mdx",
+    compId: "articles-docs-paths-serialize-instructions",
+    crumb: "@remotion/paths",
   },
   {
-    id: "presigned-urls",
-    title: "Upload with a presigned URL",
-    relativePath: "docs/presigned-urls.mdx",
-    compId: "articles-docs-presigned-urls",
-    crumb: "Building video apps",
+    id: "paths/translate-path",
+    title: "translatePath()",
+    relativePath: "docs/paths/translate-path.mdx",
+    compId: "articles-docs-paths-translate-path",
+    crumb: "@remotion/paths",
   },
   {
-    id: "tailwind-legacy",
-    title: "TailwindCSS v2 (Legacy)",
-    relativePath: "docs/tailwind-2.mdx",
-    compId: "articles-docs-tailwind-2",
-    crumb: "Legacy docs",
+    id: "paths/warp-path",
+    title: "warpPath()",
+    relativePath: "docs/paths/warp-path.mdx",
+    compId: "articles-docs-paths-warp-path",
+    crumb: "@remotion/paths",
   },
   {
     id: "performance",
@@ -2124,1436 +2116,1451 @@ export const articles = [
     crumb: "Need for Speed",
   },
   {
-    id: "getregions",
-    title: "getRegions()",
-    relativePath: "docs/lambda/getregions.mdx",
-    compId: "articles-docs-lambda-getregions",
-    crumb: "Lambda API",
-  },
-  {
-    id: "lambda/feb-2023-incident",
-    title: "Upgrade your Lambda functions to prevent breakage",
-    relativePath: "docs/lambda/feb-2023-incident.mdx",
-    compId: "articles-docs-lambda-feb-2023-incident",
-    crumb: "DevOps advisory",
+    id: "player/api",
+    title: "<Player>",
+    relativePath: "docs/player/api.mdx",
+    compId: "articles-docs-player-api",
+    crumb: "@remotion/player",
   },
   {
-    id: "lambda/multiple-buckets",
-    title: "Multiple buckets in Remotion Lambda",
-    relativePath: "docs/lambda/multiple-buckets.mdx",
-    compId: "articles-docs-lambda-multiple-buckets",
-    crumb: "@remotion/lambda",
+    id: "autoplay",
+    title: "Combatting autoplay issues",
+    relativePath: "docs/player/autoplay.mdx",
+    compId: "articles-docs-player-autoplay",
+    crumb: "@remotion/player",
   },
   {
-    id: "cli",
-    title: "@remotion/lambda - CLI",
-    relativePath: "docs/lambda/cli.mdx",
-    compId: "articles-docs-lambda-cli",
-    crumb: null,
+    id: "best-practices",
+    title: "Player - Best practices",
+    relativePath: "docs/player/best-practices.mdx",
+    compId: "articles-docs-player-best-practices",
+    crumb: "@remotion/player",
   },
   {
-    id: "getuserpolicy",
-    title: "getUserPolicy()",
-    relativePath: "docs/lambda/getuserpolicy.mdx",
-    compId: "articles-docs-lambda-getuserpolicy",
-    crumb: "Lambda API",
+    id: "buffer-state",
+    title: "The Player buffer state",
+    relativePath: "docs/player/buffer-state.mdx",
+    compId: "articles-docs-player-buffer-state",
+    crumb: "Best practices",
   },
   {
-    id: "concurrency",
-    title: "Concurrency",
-    relativePath: "docs/lambda/concurrency.mdx",
-    compId: "articles-docs-lambda-concurrency",
-    crumb: "Lambda",
+    id: "current-time",
+    title: "Displaying the current time",
+    relativePath: "docs/player/current-time.mdx",
+    compId: "articles-docs-player-current-time",
+    crumb: "@remotion/player",
   },
   {
-    id: "autodelete",
-    title: "Auto-delete renders",
-    relativePath: "docs/lambda/autodelete.mdx",
-    compId: "articles-docs-lambda-autodelete",
-    crumb: "Lambda API",
+    id: "player/index",
+    title: "@remotion/player",
+    relativePath: "docs/player/index.mdx",
+    compId: "articles-docs-player-index",
+    crumb: null,
   },
   {
-    id: "optimizing-speed",
-    title: "Optimizing for speed",
-    relativePath: "docs/lambda/speed.mdx",
-    compId: "articles-docs-lambda-speed",
-    crumb: "Lambda",
+    id: "installation",
+    title: "Installation",
+    relativePath: "docs/player/installation.mdx",
+    compId: "articles-docs-player-installation",
+    crumb: "@remotion/player",
   },
   {
-    id: "rendervideoonlambda",
-    title: "renderVideoOnLambda()",
-    relativePath: "docs/lambda/rendervideoonlambda.mdx",
-    compId: "articles-docs-lambda-rendervideoonlambda",
-    crumb: "Lambda API",
+    id: "examples",
+    title: "Examples for @remotion/player",
+    relativePath: "docs/player/player-examples.mdx",
+    compId: "articles-docs-player-player-examples",
+    crumb: "@remotion/player",
   },
   {
-    id: "getsites",
-    title: "getSites()",
-    relativePath: "docs/lambda/getsites.mdx",
-    compId: "articles-docs-lambda-getsites",
-    crumb: "Lambda API",
+    id: "integration",
+    title: "Code sharing",
+    relativePath: "docs/player/player-integration.mdx",
+    compId: "articles-docs-player-player-integration",
+    crumb: "@remotion/player",
   },
   {
-    id: "speculatefunctionname",
-    title: "speculateFunctionName()",
-    relativePath: "docs/lambda/speculateFunctionName.mdx",
-    compId: "articles-docs-lambda-speculateFunctionName",
-    crumb: "Lambda API",
+    id: "player/preloading",
+    title: "Preloading assets",
+    relativePath: "docs/player/preloading.mdx",
+    compId: "articles-docs-player-preloading",
+    crumb: "@remotion/player",
   },
   {
-    id: "lambda/permissions",
-    title: "Permissions",
-    relativePath: "docs/lambda/permissions.mdx",
-    compId: "articles-docs-lambda-permissions",
-    crumb: "Lambda",
+    id: "player/premounting",
+    title: "Premounting",
+    relativePath: "docs/player/premounting.mdx",
+    compId: "articles-docs-player-premounting",
+    crumb: "@remotion/player",
   },
   {
-    id: "deployfunction",
-    title: "deployFunction()",
-    relativePath: "docs/lambda/deployfunction.mdx",
-    compId: "articles-docs-lambda-deployfunction",
-    crumb: "Lambda API",
+    id: "scaling",
+    title: "Sizing",
+    relativePath: "docs/player/scaling.mdx",
+    compId: "articles-docs-player-scaling",
+    crumb: "@remotion/player",
   },
   {
-    id: "feb-2022-outage",
-    title: "February 2022 Outage",
-    relativePath: "docs/lambda/feb-2022-outage.mdx",
-    compId: "articles-docs-lambda-feb-2022-outage",
-    crumb: null,
+    id: "thumbnail",
+    title: "<Thumbnail>",
+    relativePath: "docs/player/thumbnail.mdx",
+    compId: "articles-docs-player-thumbnail",
+    crumb: "@remotion/player",
   },
   {
-    id: "getrenderprogress",
-    title: "getRenderProgress()",
-    relativePath: "docs/lambda/getrenderprogress.mdx",
-    compId: "articles-docs-lambda-getrenderprogress",
-    crumb: "Lambda API",
+    id: "prefetch",
+    title: "prefetch()",
+    relativePath: "docs/prefetch.mdx",
+    compId: "articles-docs-prefetch",
+    crumb: "API",
   },
   {
-    id: "authentication",
-    title: "Authentication",
-    relativePath: "docs/lambda/authentication.mdx",
-    compId: "articles-docs-lambda-authentication",
-    crumb: "Lambda",
+    id: "preload-audio",
+    title: "preloadAudio()",
+    relativePath: "docs/preload/preload-audio.mdx",
+    compId: "articles-docs-preload-preload-audio",
+    crumb: "@remotion/preload",
   },
   {
-    id: "lambda/python",
-    title: "Triggering renders from Python",
-    relativePath: "docs/lambda/python.mdx",
-    compId: "articles-docs-lambda-python",
-    crumb: "@remotion/lambda",
+    id: "preload-font",
+    title: "preloadFont()",
+    relativePath: "docs/preload/preload-font.mdx",
+    compId: "articles-docs-preload-preload-font",
+    crumb: "@remotion/preload",
   },
   {
-    id: "lambda/webhooks",
-    title: "Webhooks",
-    relativePath: "docs/lambda/webhooks.mdx",
-    compId: "articles-docs-lambda-webhooks",
-    crumb: "Lambda",
+    id: "preload-image",
+    title: "preloadImage()",
+    relativePath: "docs/preload/preload-image.mdx",
+    compId: "articles-docs-preload-preload-image",
+    crumb: "@remotion/preload",
   },
   {
-    id: "getorcreatebucket",
-    title: "getOrCreateBucket()",
-    relativePath: "docs/lambda/getorcreatebucket.mdx",
-    compId: "articles-docs-lambda-getorcreatebucket",
-    crumb: "Lambda API",
+    id: "preload-video",
+    title: "preloadVideo()",
+    relativePath: "docs/preload/preload-video.mdx",
+    compId: "articles-docs-preload-preload-video",
+    crumb: "@remotion/player",
   },
   {
-    id: "custom-destination",
-    title: "Customizing Lambda output destination",
-    relativePath: "docs/lambda/custom-destination.mdx",
-    compId: "articles-docs-lambda-custom-destination",
-    crumb: "Lambda",
+    id: "preload",
+    title: "@remotion/preload",
+    relativePath: "docs/preload/preload.mdx",
+    compId: "articles-docs-preload-preload",
+    crumb: null,
   },
   {
-    id: "deletefunction",
-    title: "deleteFunction()",
-    relativePath: "docs/lambda/deletefunction.mdx",
-    compId: "articles-docs-lambda-deletefunction",
-    crumb: "Lambda API",
+    id: "prereleases",
+    title: "Testing prereleases",
+    relativePath: "docs/prereleases.mdx",
+    compId: "articles-docs-prereleases",
+    crumb: "Only the brave",
   },
   {
-    id: "estimateprice",
-    title: "estimatePrice()",
-    relativePath: "docs/lambda/estimateprice.mdx",
-    compId: "articles-docs-lambda-estimateprice",
-    crumb: "Lambda API",
+    id: "presigned-urls",
+    title: "Upload with a presigned URL",
+    relativePath: "docs/presigned-urls.mdx",
+    compId: "articles-docs-presigned-urls",
+    crumb: "Building video apps",
   },
   {
-    id: "getrolepolicy",
-    title: "getRolePolicy()",
-    relativePath: "docs/lambda/getrolepolicy.mdx",
-    compId: "articles-docs-lambda-getrolepolicy",
-    crumb: "Lambda API",
+    id: "preview",
+    title: "Preview your video",
+    relativePath: "docs/preview.mdx",
+    compId: "articles-docs-preview",
+    crumb: "Timeline basics",
   },
   {
-    id: "lambda/light-client",
-    title: "Light client",
-    relativePath: "docs/lambda/light-client.mdx",
-    compId: "articles-docs-lambda-light-client",
-    crumb: "Lambda",
+    id: "quality",
+    title: "Quality Guide",
+    relativePath: "docs/quality.mdx",
+    compId: "articles-docs-quality",
+    crumb: "Make it look good",
   },
   {
-    id: "debug",
-    title: "Debugging failed Lambda renders",
-    relativePath: "docs/lambda/troubleshooting/debug.mdx",
-    compId: "articles-docs-lambda-troubleshooting-debug",
-    crumb: "Lambda",
+    id: "random",
+    title: "random()",
+    relativePath: "docs/random.mdx",
+    compId: "articles-docs-random",
+    crumb: "API",
   },
   {
-    id: "unrecognizedclientexception",
-    title: "UnrecognizedClientException",
-    relativePath: "docs/lambda/troubleshooting/unrecognizedclientexception.mdx",
-    compId: "articles-docs-lambda-troubleshooting-unrecognizedclientexception",
-    crumb: "Lambda Troubleshooting",
+    id: "react-18",
+    title: "Upgrade to React 18",
+    relativePath: "docs/react-18.mdx",
+    compId: "articles-docs-react-18",
+    crumb: "The new and shiny",
   },
   {
-    id: "bucket-disallows-acl",
-    title: "The bucket does not allow ACLs",
-    relativePath: "docs/lambda/troubleshooting/bucket-disallows-acl.mdx",
-    compId: "articles-docs-lambda-troubleshooting-bucket-disallows-acl",
-    crumb: "Lambda Troubleshooting",
+    id: "react-native",
+    title: "React Native",
+    relativePath: "docs/react-native.mdx",
+    compId: "articles-docs-react-native",
+    crumb: null,
   },
   {
-    id: "permissions",
-    title: "AWS Permissions Troubleshooting",
-    relativePath: "docs/lambda/troubleshooting/permissions.mdx",
-    compId: "articles-docs-lambda-troubleshooting-permissions",
-    crumb: "Lambda Troubleshooting",
+    id: "recorder/before-you-buy",
+    title: "Before you buy",
+    relativePath: "docs/recorder/before-you-buy.mdx",
+    compId: "articles-docs-recorder-before-you-buy",
+    crumb: "Recorder",
   },
   {
-    id: "lambda/troubleshooting/security-token",
-    title: "'\"The security token included in the request is invalid\"'",
-    relativePath: "docs/lambda/troubleshooting/security-token.mdx",
-    compId: "articles-docs-lambda-troubleshooting-security-token",
-    crumb: "Lambda",
+    id: "recorder/buy",
+    title: "Buy",
+    relativePath: "docs/recorder/buy.mdx",
+    compId: "articles-docs-recorder-buy",
+    crumb: "Recorder",
   },
   {
-    id: "rate-limit",
-    title: "AWS Rate Limit Troubleshooting",
-    relativePath: "docs/lambda/troubleshooting/rate-limit.mdx",
-    compId: "articles-docs-lambda-troubleshooting-rate-limit",
-    crumb: "Lambda Troubleshooting",
+    id: "recorder/captions",
+    title: "Generate captions",
+    relativePath: "docs/recorder/captions.mdx",
+    compId: "articles-docs-recorder-captions",
+    crumb: "Recorder",
   },
   {
-    id: "policies",
-    title: "npx remotion lambda policies",
-    relativePath: "docs/lambda/cli/policies.mdx",
-    compId: "articles-docs-lambda-cli-policies",
-    crumb: "Lambda CLI Reference",
+    id: "recorder/create",
+    title: "Create a new video",
+    relativePath: "docs/recorder/create.mdx",
+    compId: "articles-docs-recorder-create",
+    crumb: "Recorder",
   },
   {
-    id: "quotas",
-    title: "npx remotion lambda quotas",
-    relativePath: "docs/lambda/cli/quotas.mdx",
-    compId: "articles-docs-lambda-cli-quotas",
-    crumb: "Lambda CLI Reference",
+    id: "recorder/editing/b-roll",
+    title: "B-Roll",
+    relativePath: "docs/recorder/editing/b-roll.mdx",
+    compId: "articles-docs-recorder-editing-b-roll",
+    crumb: "Recorder",
   },
   {
-    id: "functions",
-    title: "npx remotion lambda functions",
-    relativePath: "docs/lambda/cli/functions.mdx",
-    compId: "articles-docs-lambda-cli-functions",
-    crumb: "Lambda CLI Reference",
+    id: "recorder/editing/captions",
+    title: "Edit captions",
+    relativePath: "docs/recorder/editing/captions.mdx",
+    compId: "articles-docs-recorder-editing-captions",
+    crumb: "Recorder",
+  },
+  {
+    id: "recorder/editing/chapters",
+    title: "Chapters",
+    relativePath: "docs/recorder/editing/chapters.mdx",
+    compId: "articles-docs-recorder-editing-chapters",
+    crumb: "Recorder",
   },
   {
-    id: "sites",
-    title: "npx remotion lambda sites",
-    relativePath: "docs/lambda/cli/sites.mdx",
-    compId: "articles-docs-lambda-cli-sites",
-    crumb: "Lambda CLI Reference",
+    id: "recorder/editing/cutting-clips",
+    title: "Cutting clips",
+    relativePath: "docs/recorder/editing/cutting-clips.mdx",
+    compId: "articles-docs-recorder-editing-cutting-clips",
+    crumb: "Recorder",
   },
   {
-    id: "render",
-    title: "npx remotion lambda render",
-    relativePath: "docs/lambda/cli/render.mdx",
-    compId: "articles-docs-lambda-cli-render",
-    crumb: "Lambda CLI Reference",
+    id: "recorder/editing/editing",
+    title: "Start editing",
+    relativePath: "docs/recorder/editing/editing.mdx",
+    compId: "articles-docs-recorder-editing-editing",
+    crumb: "Recorder",
   },
   {
-    id: "regions",
-    title: "npx remotion lambda regions",
-    relativePath: "docs/lambda/cli/regions.mdx",
-    compId: "articles-docs-lambda-cli-regions",
-    crumb: "Lambda CLI Reference",
+    id: "recorder/editing/endcard",
+    title: "Endcard",
+    relativePath: "docs/recorder/editing/endcard.mdx",
+    compId: "articles-docs-recorder-editing-endcard",
+    crumb: "Recorder",
   },
   {
-    id: "lambda/cli/compositions",
-    title: "npx remotion lambda compositions",
-    relativePath: "docs/lambda/cli/compositions.mdx",
-    compId: "articles-docs-lambda-cli-compositions",
-    crumb: "Lambda CLI Reference",
+    id: "recorder/editing/layout",
+    title: "Layout",
+    relativePath: "docs/recorder/editing/layout.mdx",
+    compId: "articles-docs-recorder-editing-layout",
+    crumb: "Recorder",
   },
   {
-    id: "still",
-    title: "npx remotion lambda still",
-    relativePath: "docs/lambda/cli/still.mdx",
-    compId: "articles-docs-lambda-cli-still",
-    crumb: "Lambda CLI Reference",
+    id: "recorder/editing/music",
+    title: "Adding music",
+    relativePath: "docs/recorder/editing/music.mdx",
+    compId: "articles-docs-recorder-editing-music",
+    crumb: "Recorder",
   },
   {
-    id: "deleterender",
-    title: "deleteRender()",
-    relativePath: "docs/lambda/deleterender.mdx",
-    compId: "articles-docs-lambda-deleterender",
-    crumb: "Lambda API",
+    id: "recorder/editing/scenes",
+    title: "Scenes",
+    relativePath: "docs/recorder/editing/scenes.mdx",
+    compId: "articles-docs-recorder-editing-scenes",
+    crumb: "Recorder",
   },
   {
-    id: "runtime",
-    title: "Runtime",
-    relativePath: "docs/lambda/runtime.mdx",
-    compId: "articles-docs-lambda-runtime",
-    crumb: "Lambda",
+    id: "recorder/editing/silence-removal",
+    title: "Silence removal",
+    relativePath: "docs/recorder/editing/silence-removal.mdx",
+    compId: "articles-docs-recorder-editing-silence-removal",
+    crumb: "Recorder",
   },
   {
-    id: "upgrading",
-    title: "Upgrading Lambda",
-    relativePath: "docs/lambda/upgrading.mdx",
-    compId: "articles-docs-lambda-upgrading",
-    crumb: null,
+    id: "recorder/editing/transitions",
+    title: "Transitions",
+    relativePath: "docs/recorder/editing/transitions.mdx",
+    compId: "articles-docs-recorder-editing-transitions",
+    crumb: "Recorder",
   },
   {
-    id: "region-selection",
-    title: "Region selection",
-    relativePath: "docs/lambda/region-selection.mdx",
-    compId: "articles-docs-lambda-region-selection",
-    crumb: "Lambda",
+    id: "recorder/exporting",
+    title: "Exporting a video",
+    relativePath: "docs/recorder/exporting.mdx",
+    compId: "articles-docs-recorder-exporting",
+    crumb: "Recorder",
   },
   {
-    id: "renderstillonlambda",
-    title: "renderStillOnLambda()",
-    relativePath: "docs/lambda/renderstillonlambda.mdx",
-    compId: "articles-docs-lambda-renderstillonlambda",
-    crumb: "Lambda API",
+    id: "recorder/external-recordings",
+    title: "External recordings",
+    relativePath: "docs/recorder/external-recordings.mdx",
+    compId: "articles-docs-recorder-external-recordings",
+    crumb: "Recorder",
   },
   {
-    id: "lambda/custom-layers",
-    title: "Custom Layers",
-    relativePath: "docs/lambda/custom-layers.mdx",
-    compId: "articles-docs-lambda-custom-layers",
-    crumb: "Lambda",
+    id: "recorder/gear",
+    title: "Our Gear",
+    relativePath: "docs/recorder/gear.mdx",
+    compId: "articles-docs-recorder-gear",
+    crumb: "Recorder",
   },
   {
-    id: "changelog",
-    title: "Prerelease Changelog",
-    relativePath: "docs/lambda/changelog.mdx",
-    compId: "articles-docs-lambda-changelog",
-    crumb: null,
+    id: "recorder/index",
+    title: "Remotion Recorder",
+    relativePath: "docs/recorder/index.mdx",
+    compId: "articles-docs-recorder-index",
+    crumb: "Templates",
   },
   {
-    id: "optimizing-cost",
-    title: "Optimizing for cost",
-    relativePath: "docs/lambda/cost.mdx",
-    compId: "articles-docs-lambda-cost",
-    crumb: "Lambda",
+    id: "recorder/lambda-rendering",
+    title: "Render on Lambda",
+    relativePath: "docs/recorder/lambda-rendering.mdx",
+    compId: "articles-docs-recorder-lambda-rendering",
+    crumb: "Recorder",
   },
   {
-    id: "lambda/serverless-framework-integration",
-    title: "Using the Serverless Framework with Remotion Lambda",
-    relativePath: "docs/lambda/serverless-framework-integration.mdx",
-    compId: "articles-docs-lambda-serverless-framework-integration",
-    crumb: "@remotion/lambda",
+    id: "recorder/our-recorder",
+    title: "Our Recorder",
+    relativePath: "docs/recorder/our-recorder.mdx",
+    compId: "articles-docs-recorder-our-recorder",
+    crumb: "Recorder",
   },
   {
-    id: "disk-size",
-    title: "Disk size",
-    relativePath: "docs/lambda/disk-size.mdx",
-    compId: "articles-docs-lambda-disk-size",
-    crumb: "Lambda",
+    id: "recorder/record",
+    title: "Record a scene",
+    relativePath: "docs/recorder/record.mdx",
+    compId: "articles-docs-recorder-record",
+    crumb: "Recorder",
   },
   {
-    id: "lambda/without-iam/ec2",
-    title: "Authenticating Lambda with EC2",
-    relativePath: "docs/lambda/without-iam/ec2.mdx",
-    compId: "articles-docs-lambda-without-iam-ec2",
-    crumb: "@remotion/lambda",
+    id: "recorder/roadmap",
+    title: "Roadmap",
+    relativePath: "docs/recorder/roadmap.mdx",
+    compId: "articles-docs-recorder-roadmap",
+    crumb: "Recorder",
   },
   {
-    id: "lambda/without-iam/index",
-    title: "Using Lambda with IAM roles",
-    relativePath: "docs/lambda/without-iam/index.mdx",
-    compId: "articles-docs-lambda-without-iam-index",
-    crumb: "Lambda without IAM",
+    id: "recorder/setup",
+    title: "Setup",
+    relativePath: "docs/recorder/setup.mdx",
+    compId: "articles-docs-recorder-setup",
+    crumb: "Recorder",
   },
   {
-    id: "lambda/without-iam/example",
-    title: "Example setup without IAM user",
-    relativePath: "docs/lambda/without-iam/example.mdx",
-    compId: "articles-docs-lambda-without-iam-example",
-    crumb: "IAM Roles Example",
+    id: "recorder/source-control",
+    title: "Source Control",
+    relativePath: "docs/recorder/source-control.mdx",
+    compId: "articles-docs-recorder-source-control",
+    crumb: "Recorder",
   },
   {
-    id: "downloadvideo",
-    title: "downloadVideo()",
-    relativePath: "docs/lambda/downloadvideo.mdx",
-    compId: "articles-docs-lambda-downloadvideo",
-    crumb: "Lambda API",
+    id: "recorder/support",
+    title: "Get Help",
+    relativePath: "docs/recorder/support.mdx",
+    compId: "articles-docs-recorder-support",
+    crumb: "Recorder",
   },
   {
-    id: "getawsclient",
-    title: "getAwsClient()",
-    relativePath: "docs/lambda/getawsclient.mdx",
-    compId: "articles-docs-lambda-getawsclient",
-    crumb: "Lambda API",
+    id: "recorder/troubleshooting/cannot-read-properties-of-undefined",
+    title: "Cannot read properties of undefined (reading 'decode')",
+    relativePath:
+      "docs/recorder/troubleshooting/cannot-read-properties-of-undefined.mdx",
+    compId:
+      "articles-docs-recorder-troubleshooting-cannot-read-properties-of-undefined",
+    crumb: "Recorder",
   },
   {
-    id: "lambda/php",
-    title: "Triggering renders from PHP",
-    relativePath: "docs/lambda/php.mdx",
-    compId: "articles-docs-lambda-php",
-    crumb: "@remotion/lambda",
+    id: "recorder/troubleshooting/failed-to-execute-get-video-metadata",
+    title: "Failed to execute getVideoMetadata()",
+    relativePath:
+      "docs/recorder/troubleshooting/failed-to-execute-get-video-metadata.mdx",
+    compId:
+      "articles-docs-recorder-troubleshooting-failed-to-execute-get-video-metadata",
+    crumb: "Recorder",
   },
   {
-    id: "rendermediaonlambda",
-    title: "renderMediaOnLambda()",
-    relativePath: "docs/lambda/rendermediaonlambda.mdx",
-    compId: "articles-docs-lambda-rendermediaonlambda",
-    crumb: "Lambda API",
+    id: "recorder/upgrading",
+    title: "Upgrading",
+    relativePath: "docs/recorder/upgrading.mdx",
+    compId: "articles-docs-recorder-upgrading",
+    crumb: "Recorder",
   },
   {
-    id: "simulatepermissions",
-    title: "simulatePermissions()",
-    relativePath: "docs/lambda/simulatepermissions.mdx",
-    compId: "articles-docs-lambda-simulatepermissions",
-    crumb: "Lambda API",
+    id: "register-root",
+    title: "registerRoot()",
+    relativePath: "docs/register-root.mdx",
+    compId: "articles-docs-register-root",
+    crumb: "API",
   },
   {
-    id: "checklist",
-    title: "Production Checklist",
-    relativePath: "docs/lambda/checklist.mdx",
-    compId: "articles-docs-lambda-checklist",
-    crumb: "Lambda",
+    id: "remotion",
+    title: "remotion",
+    relativePath: "docs/remotion.mdx",
+    compId: "articles-docs-remotion",
+    crumb: "Core API Reference",
   },
   {
-    id: "how-lambda-works",
-    title: "How Remotion Lambda works",
-    relativePath: "docs/lambda/how-lambda-works.mdx",
-    compId: "articles-docs-lambda-how-lambda-works",
-    crumb: "Lambda",
+    id: "render-all",
+    title: "Render all compositions",
+    relativePath: "docs/render-all.mdx",
+    compId: "articles-docs-render-all",
+    crumb: "Techniques",
   },
   {
-    id: "presignurl",
-    title: "presignUrl()",
-    relativePath: "docs/lambda/presignurl.mdx",
-    compId: "articles-docs-lambda-presignurl",
-    crumb: "Lambda API",
+    id: "render-as-gif",
+    title: "Rendering GIFs",
+    relativePath: "docs/render-as-gif.mdx",
+    compId: "articles-docs-render-as-gif",
+    crumb: "Techniques",
   },
   {
-    id: "lambda/faq",
-    title: "FAQ",
-    relativePath: "docs/lambda/faq.mdx",
-    compId: "articles-docs-lambda-faq",
-    crumb: "Lambda",
+    id: "render",
+    title: "Render your video",
+    relativePath: "docs/render.mdx",
+    compId: "articles-docs-render",
+    crumb: "How To",
   },
   {
-    id: "lambda/api",
-    title: "@remotion/lambda",
-    relativePath: "docs/lambda/api.mdx",
-    compId: "articles-docs-lambda-api",
-    crumb: "Render videos without servers on AWS",
+    id: "ensure-browser",
+    title: "ensureBrowser()",
+    relativePath: "docs/renderer/ensure-browser.mdx",
+    compId: "articles-docs-renderer-ensure-browser",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "validatewebhooksignature",
-    title: "validateWebhookSignature()",
-    relativePath: "docs/lambda/validatewebhooksignature.mdx",
-    compId: "articles-docs-lambda-validatewebhooksignature",
-    crumb: "Lambda API",
+    id: "ensure-ffmpeg",
+    title: "ensureFfmpeg()",
+    relativePath: "docs/renderer/ensure-ffmpeg.mdx",
+    compId: "articles-docs-renderer-ensure-ffmpeg",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "getfunctioninfo",
-    title: "getFunctionInfo()",
-    relativePath: "docs/lambda/getfunctioninfo.mdx",
-    compId: "articles-docs-lambda-getfunctioninfo",
-    crumb: "Lambda API",
+    id: "ensure-ffprobe",
+    title: "ensureFfprobe()",
+    relativePath: "docs/renderer/ensure-ffprobe.mdx",
+    compId: "articles-docs-renderer-ensure-ffprobe",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "setup",
-    title: "Setup",
-    relativePath: "docs/lambda/setup.mdx",
-    compId: "articles-docs-lambda-setup",
-    crumb: "Lambda",
+    id: "extract-audio",
+    title: "extractAudio()",
+    relativePath: "docs/renderer/extract-audio.mdx",
+    compId: "articles-docs-renderer-extract-audio",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "getfunctions",
-    title: "getFunctions()",
-    relativePath: "docs/lambda/getfunctions.mdx",
-    compId: "articles-docs-lambda-getfunctions",
-    crumb: "Lambda API",
+    id: "get-can-extract-frames-fast",
+    title: "getCanExtractFramesFast()",
+    relativePath: "docs/renderer/get-can-extract-frames-fast.mdx",
+    compId: "articles-docs-renderer-get-can-extract-frames-fast",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "getcompositionsonlambda",
-    title: "getCompositionsOnLambda()",
-    relativePath: "docs/lambda/getcompositionsonlambda.mdx",
-    compId: "articles-docs-lambda-getcompositionsonlambda",
-    crumb: "Lambda API",
+    id: "get-compositions",
+    title: "getCompositions()",
+    relativePath: "docs/renderer/get-compositions.mdx",
+    compId: "articles-docs-renderer-get-compositions",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "downloadmedia",
-    title: "downloadMedia()",
-    relativePath: "docs/lambda/downloadmedia.mdx",
-    compId: "articles-docs-lambda-downloadmedia",
-    crumb: "Lambda API",
+    id: "get-silent-parts",
+    title: "getSilentParts()",
+    relativePath: "docs/renderer/get-silent-parts.mdx",
+    compId: "articles-docs-renderer-get-silent-parts",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "lambda/s3-public-access",
-    title: "Cannot create a S3 bucket using Remotion",
-    relativePath: "docs/lambda/s3-public-access.mdx",
-    compId: "articles-docs-lambda-s3-public-access",
-    crumb: "DevOps advisory",
+    id: "get-video-metadata",
+    title: "getVideoMetadata()",
+    relativePath: "docs/renderer/get-video-metadata.mdx",
+    compId: "articles-docs-renderer-get-video-metadata",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "lambda/limits",
-    title: "Lambda Limits",
-    relativePath: "docs/lambda/limits.mdx",
-    compId: "articles-docs-lambda-limits",
-    crumb: "Lambda",
+    id: "make-cancel-signal",
+    title: "makeCancelSignal()",
+    relativePath: "docs/renderer/make-cancel-signal.mdx",
+    compId: "articles-docs-renderer-make-cancel-signal",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "lambda/sqs",
-    title: "Using Lambda with SQS",
-    relativePath: "docs/lambda/sqs.mdx",
-    compId: "articles-docs-lambda-sqs",
-    crumb: "@remotion/lambda",
+    id: "open-browser",
+    title: "openBrowser()",
+    relativePath: "docs/renderer/open-browser.mdx",
+    compId: "articles-docs-renderer-open-browser",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "lambda/go",
-    title: "Triggering renders from Go",
-    relativePath: "docs/lambda/go.mdx",
-    compId: "articles-docs-lambda-go",
-    crumb: "@remotion/lambda",
+    id: "render-frames",
+    title: "renderFrames()",
+    relativePath: "docs/renderer/render-frames.mdx",
+    compId: "articles-docs-renderer-render-frames",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "deploysite",
-    title: "deploySite()",
-    relativePath: "docs/lambda/deploysite.mdx",
-    compId: "articles-docs-lambda-deploysite",
-    crumb: "Lambda API",
+    id: "render-media",
+    title: "renderMedia()",
+    relativePath: "docs/renderer/render-media.mdx",
+    compId: "articles-docs-renderer-render-media",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "uninstall",
-    title: "Uninstall Lambda",
-    relativePath: "docs/lambda/uninstall.mdx",
-    compId: "articles-docs-lambda-uninstall",
-    crumb: null,
+    id: "render-still",
+    title: "renderStill()",
+    relativePath: "docs/renderer/render-still.mdx",
+    compId: "articles-docs-renderer-render-still",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "lambda/bucket-naming",
-    title: "Bucket names in Remotion Lambda",
-    relativePath: "docs/lambda/bucket-naming.mdx",
-    compId: "articles-docs-lambda-bucket-naming",
-    crumb: "@remotion/lambda",
+    id: "select-composition",
+    title: "selectComposition()",
+    relativePath: "docs/renderer/select-composition.mdx",
+    compId: "articles-docs-renderer-select-composition",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "naming-convention",
-    title: "Function naming convention",
-    relativePath: "docs/lambda/naming-convention.mdx",
-    compId: "articles-docs-lambda-naming-convention",
-    crumb: "Lambda",
+    id: "stitch-frames-to-video",
+    title: "stitchFramesToVideo()",
+    relativePath: "docs/renderer/stitch-frames-to-video.mdx",
+    compId: "articles-docs-renderer-stitch-frames-to-video",
+    crumb: "@remotion/renderer",
   },
   {
-    id: "lambda/insights",
-    title: "Enable Lambda Insights",
-    relativePath: "docs/lambda/insights.mdx",
-    compId: "articles-docs-lambda-insights",
-    crumb: "Lambda",
+    id: "renderer",
+    title: "@remotion/renderer",
+    relativePath: "docs/renderer.mdx",
+    compId: "articles-docs-renderer",
+    crumb: null,
   },
   {
-    id: "deletesite",
-    title: "deleteSite()",
-    relativePath: "docs/lambda/deletesite.mdx",
-    compId: "articles-docs-lambda-deletesite",
-    crumb: "Lambda API",
+    id: "resources",
+    title: "List of resources",
+    relativePath: "docs/resources.mdx",
+    compId: "articles-docs-resources",
+    crumb: "Ecosystem",
   },
   {
-    id: "compare/motion-canvas",
-    title: "How does Remotion compare to Motion Canvas?",
-    relativePath: "docs/compare/motion-canvas.mdx",
-    compId: "articles-docs-compare-motion-canvas",
-    crumb: "FAQ",
+    id: "rive/index",
+    title: "@remotion/rive",
+    relativePath: "docs/rive/index.mdx",
+    compId: "articles-docs-rive-index",
+    crumb: "Integrations",
   },
   {
-    id: "api",
-    title: "API overview",
-    relativePath: "docs/api.mdx",
-    compId: "articles-docs-api",
-    crumb: null,
+    id: "rive/remotionrivecanvas",
+    title: "<RemotionRiveCanvas>",
+    relativePath: "docs/rive/remotionrivecanvas.mdx",
+    compId: "articles-docs-rive-remotionrivecanvas",
+    crumb: "@remotion/rive",
   },
   {
-    id: "staticfile",
-    title: "staticFile()",
-    relativePath: "docs/staticfile.mdx",
-    compId: "articles-docs-staticfile",
-    crumb: "API",
+    id: "scaling",
+    title: "Output scaling",
+    relativePath: "docs/scaling.mdx",
+    compId: "articles-docs-scaling",
+    crumb: "How To",
   },
   {
-    id: "lottie-staticfile",
-    title: "Loading Lottie animations from staticFile()",
-    relativePath: "docs/lottie/lottie-staticfile.mdx",
-    compId: "articles-docs-lottie-lottie-staticfile",
-    crumb: "@remotion/lottie",
+    id: "schemas",
+    title: "Defining a schema for your props",
+    relativePath: "docs/schemas.mdx",
+    compId: "articles-docs-schemas",
+    crumb: "How To",
   },
   {
-    id: "lottie-index",
-    title: "@remotion/lottie",
-    relativePath: "docs/lottie/index.mdx",
-    compId: "articles-docs-lottie-index",
-    crumb: null,
+    id: "security",
+    title: "Security Best Practices",
+    relativePath: "docs/security.mdx",
+    compId: "articles-docs-security",
+    crumb: "FAQ",
   },
   {
-    id: "getlottiemetadata",
-    title: "getLottieMetadata()",
-    relativePath: "docs/lottie/getlottiemetadata.mdx",
-    compId: "articles-docs-lottie-getlottiemetadata",
-    crumb: "@remotion/lottie",
+    id: "sequence",
+    title: "<Sequence>",
+    relativePath: "docs/sequence.mdx",
+    compId: "articles-docs-sequence",
+    crumb: "API",
+  },
+  {
+    id: "reusability",
+    title: "Making components reusable",
+    relativePath: "docs/sequences.mdx",
+    compId: "articles-docs-sequences",
+    crumb: "The power of React",
   },
   {
-    id: "lottie-remote",
-    title: "Loading Lottie animations from a URL",
-    relativePath: "docs/lottie/lottie-remote.mdx",
-    compId: "articles-docs-lottie-lottie-remote",
-    crumb: "@remotion/lottie",
+    id: "series",
+    title: "<Series>",
+    relativePath: "docs/series.mdx",
+    compId: "articles-docs-series",
+    crumb: "API",
   },
   {
-    id: "lottie-comp",
-    title: "<Lottie>",
-    relativePath: "docs/lottie/lottie-comp.mdx",
-    compId: "articles-docs-lottie-lottie-comp",
-    crumb: "@remotion/lottie",
+    id: "shapes/circle",
+    title: "<Circle />",
+    relativePath: "docs/shapes/circle.mdx",
+    compId: "articles-docs-shapes-circle",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "lottie-lottiefiles",
-    title: "Finding Lottie files to use",
-    relativePath: "docs/lottie/lottie-lottiefiles.mdx",
-    compId: "articles-docs-lottie-lottie-lottiefiles",
-    crumb: "Resources",
+    id: "shapes/ellipse",
+    title: "<Ellipse />",
+    relativePath: "docs/shapes/ellipse.mdx",
+    compId: "articles-docs-shapes-ellipse",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "overlay",
-    title: "Creating overlays",
-    relativePath: "docs/overlay.mdx",
-    compId: "articles-docs-overlay",
-    crumb: null,
+    id: "shapes/index",
+    title: "@remotion/shapes",
+    relativePath: "docs/shapes/index.mdx",
+    compId: "articles-docs-shapes-index",
+    crumb: "SVG component library",
   },
   {
-    id: "transparent-videos",
-    title: "Transparent videos",
-    relativePath: "docs/transparent-videos.mdx",
-    compId: "articles-docs-transparent-videos",
-    crumb: "Techniques",
+    id: "shapes/make-circle",
+    title: "makeCircle()",
+    relativePath: "docs/shapes/make-circle.mdx",
+    compId: "articles-docs-shapes-make-circle",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/create",
-    title: "Create a new video",
-    relativePath: "docs/recorder/create.mdx",
-    compId: "articles-docs-recorder-create",
-    crumb: "Recorder",
+    id: "shapes/make-ellipse",
+    title: "makeEllipse()",
+    relativePath: "docs/shapes/make-ellipse.mdx",
+    compId: "articles-docs-shapes-make-ellipse",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/lambda-rendering",
-    title: "Render on Lambda",
-    relativePath: "docs/recorder/lambda-rendering.mdx",
-    compId: "articles-docs-recorder-lambda-rendering",
-    crumb: "Recorder",
+    id: "shapes/make-pie",
+    title: "makePie()",
+    relativePath: "docs/shapes/make-pie.mdx",
+    compId: "articles-docs-shapes-make-pie",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/exporting",
-    title: "Exporting a video",
-    relativePath: "docs/recorder/exporting.mdx",
-    compId: "articles-docs-recorder-exporting",
-    crumb: "Recorder",
+    id: "shapes/make-polygon",
+    title: "makePolygon()",
+    relativePath: "docs/shapes/make-polygon.mdx",
+    compId: "articles-docs-shapes-make-polygon",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/external-recordings",
-    title: "External recordings",
-    relativePath: "docs/recorder/external-recordings.mdx",
-    compId: "articles-docs-recorder-external-recordings",
-    crumb: "Recorder",
+    id: "shapes/make-rect",
+    title: "makeRect()",
+    relativePath: "docs/shapes/make-rect.mdx",
+    compId: "articles-docs-shapes-make-rect",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/captions",
-    title: "Generate captions",
-    relativePath: "docs/recorder/captions.mdx",
-    compId: "articles-docs-recorder-captions",
-    crumb: "Recorder",
+    id: "shapes/make-star",
+    title: "makeStar()",
+    relativePath: "docs/shapes/make-star.mdx",
+    compId: "articles-docs-shapes-make-star",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/index",
-    title: "Remotion Recorder",
-    relativePath: "docs/recorder/index.mdx",
-    compId: "articles-docs-recorder-index",
-    crumb: "Templates",
+    id: "shapes/make-triangle",
+    title: "makeTriangle()",
+    relativePath: "docs/shapes/make-triangle.mdx",
+    compId: "articles-docs-shapes-make-triangle",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/support",
-    title: "Get Help",
-    relativePath: "docs/recorder/support.mdx",
-    compId: "articles-docs-recorder-support",
-    crumb: "Recorder",
+    id: "shapes/pie",
+    title: "<Pie />",
+    relativePath: "docs/shapes/pie.mdx",
+    compId: "articles-docs-shapes-pie",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/source-control",
-    title: "Source Control",
-    relativePath: "docs/recorder/source-control.mdx",
-    compId: "articles-docs-recorder-source-control",
-    crumb: "Recorder",
+    id: "shapes/polygon",
+    title: "<Polygon />",
+    relativePath: "docs/shapes/polygon.mdx",
+    compId: "articles-docs-shapes-polygon",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/buy",
-    title: "Buy",
-    relativePath: "docs/recorder/buy.mdx",
-    compId: "articles-docs-recorder-buy",
-    crumb: "Recorder",
+    id: "shapes/rect",
+    title: "<Rect />",
+    relativePath: "docs/shapes/rect.mdx",
+    compId: "articles-docs-shapes-rect",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/troubleshooting/failed-to-execute-get-video-metadata",
-    title: "Failed to execute getVideoMetadata()",
-    relativePath:
-      "docs/recorder/troubleshooting/failed-to-execute-get-video-metadata.mdx",
-    compId:
-      "articles-docs-recorder-troubleshooting-failed-to-execute-get-video-metadata",
-    crumb: "Recorder",
+    id: "shapes/star",
+    title: "<Star />",
+    relativePath: "docs/shapes/star.mdx",
+    compId: "articles-docs-shapes-star",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/troubleshooting/cannot-read-properties-of-undefined",
-    title: "Cannot read properties of undefined (reading 'decode')",
-    relativePath:
-      "docs/recorder/troubleshooting/cannot-read-properties-of-undefined.mdx",
-    compId:
-      "articles-docs-recorder-troubleshooting-cannot-read-properties-of-undefined",
-    crumb: "Recorder",
+    id: "shapes/triangle",
+    title: "<Triangle />",
+    relativePath: "docs/shapes/triangle.mdx",
+    compId: "articles-docs-shapes-triangle",
+    crumb: "@remotion/shapes",
   },
   {
-    id: "recorder/record",
-    title: "Record a scene",
-    relativePath: "docs/recorder/record.mdx",
-    compId: "articles-docs-recorder-record",
-    crumb: "Recorder",
+    id: "skia/enable-skia",
+    title: "enableSkia()",
+    relativePath: "docs/skia/enable-skia.mdx",
+    compId: "articles-docs-skia-enable-skia",
+    crumb: "@remotion/skia",
   },
   {
-    id: "recorder/gear",
-    title: "Our Gear",
-    relativePath: "docs/recorder/gear.mdx",
-    compId: "articles-docs-recorder-gear",
-    crumb: "Recorder",
+    id: "skia/skia-canvas",
+    title: "<SkiaCanvas />",
+    relativePath: "docs/skia/skia-canvas.mdx",
+    compId: "articles-docs-skia-skia-canvas",
+    crumb: "@remotion/skia",
   },
   {
-    id: "recorder/roadmap",
-    title: "Roadmap",
-    relativePath: "docs/recorder/roadmap.mdx",
-    compId: "articles-docs-recorder-roadmap",
-    crumb: "Recorder",
+    id: "skia",
+    title: "@remotion/skia",
+    relativePath: "docs/skia/skia.mdx",
+    compId: "articles-docs-skia-skia",
+    crumb: null,
   },
   {
-    id: "recorder/upgrading",
-    title: "Upgrading",
-    relativePath: "docs/recorder/upgrading.mdx",
-    compId: "articles-docs-recorder-upgrading",
-    crumb: "Recorder",
+    id: "slow-method-to-extract-frame",
+    title: "Slow method to extract frame",
+    relativePath: "docs/slow-method-to-extract-frame.mdx",
+    compId: "articles-docs-slow-method-to-extract-frame",
+    crumb: "Troubleshooting",
   },
   {
-    id: "recorder/editing/scenes",
-    title: "Scenes",
-    relativePath: "docs/recorder/editing/scenes.mdx",
-    compId: "articles-docs-recorder-editing-scenes",
-    crumb: "Recorder",
+    id: "spline",
+    title: "Import from Spline",
+    relativePath: "docs/spline.mdx",
+    compId: "articles-docs-spline",
+    crumb: "Integrations",
   },
   {
-    id: "recorder/editing/b-roll",
-    title: "B-Roll",
-    relativePath: "docs/recorder/editing/b-roll.mdx",
-    compId: "articles-docs-recorder-editing-b-roll",
-    crumb: "Recorder",
+    id: "spring",
+    title: "spring()",
+    relativePath: "docs/spring.mdx",
+    compId: "articles-docs-spring",
+    crumb: "API",
   },
   {
-    id: "recorder/editing/music",
-    title: "Adding music",
-    relativePath: "docs/recorder/editing/music.mdx",
-    compId: "articles-docs-recorder-editing-music",
-    crumb: "Recorder",
+    id: "ssr-legacy",
+    title: "Server-Side Rendering (v1 and v2)",
+    relativePath: "docs/ssr-legacy.mdx",
+    compId: "articles-docs-ssr-legacy",
+    crumb: "Legacy docs",
   },
   {
-    id: "recorder/editing/chapters",
-    title: "Chapters",
-    relativePath: "docs/recorder/editing/chapters.mdx",
-    compId: "articles-docs-recorder-editing-chapters",
-    crumb: "Recorder",
+    id: "ssr-node",
+    title: "Rendering using SSR APIs",
+    relativePath: "docs/ssr-node.mdx",
+    compId: "articles-docs-ssr-node",
+    crumb: "Server-side rendering",
   },
   {
-    id: "recorder/editing/captions",
-    title: "Edit captions",
-    relativePath: "docs/recorder/editing/captions.mdx",
-    compId: "articles-docs-recorder-editing-captions",
-    crumb: "Recorder",
+    id: "ssr",
+    title: "Server-Side Rendering",
+    relativePath: "docs/ssr.mdx",
+    compId: "articles-docs-ssr",
+    crumb: "The power of",
   },
   {
-    id: "recorder/editing/cutting-clips",
-    title: "Cutting clips",
-    relativePath: "docs/recorder/editing/cutting-clips.mdx",
-    compId: "articles-docs-recorder-editing-cutting-clips",
-    crumb: "Recorder",
+    id: "standalone",
+    title: "Useful functions outside of Remotion",
+    relativePath: "docs/standalone.mdx",
+    compId: "articles-docs-standalone",
+    crumb: null,
   },
   {
-    id: "recorder/editing/editing",
-    title: "Start editing",
-    relativePath: "docs/recorder/editing/editing.mdx",
-    compId: "articles-docs-recorder-editing-editing",
-    crumb: "Recorder",
+    id: "staticfile-relative-paths",
+    title: "staticFile() does not support relative paths",
+    relativePath: "docs/static-file-relative-paths.mdx",
+    compId: "articles-docs-static-file-relative-paths",
+    crumb: "Troubleshooting",
   },
   {
-    id: "recorder/editing/endcard",
-    title: "Endcard",
-    relativePath: "docs/recorder/editing/endcard.mdx",
-    compId: "articles-docs-recorder-editing-endcard",
-    crumb: "Recorder",
+    id: "staticfile-remote-urls",
+    title: "staticFile() does not support remote URLs",
+    relativePath: "docs/static-file-remote-urls.mdx",
+    compId: "articles-docs-static-file-remote-urls",
+    crumb: "Troubleshooting",
   },
   {
-    id: "recorder/editing/transitions",
-    title: "Transitions",
-    relativePath: "docs/recorder/editing/transitions.mdx",
-    compId: "articles-docs-recorder-editing-transitions",
-    crumb: "Recorder",
+    id: "staticfile",
+    title: "staticFile()",
+    relativePath: "docs/staticfile.mdx",
+    compId: "articles-docs-staticfile",
+    crumb: "API",
   },
   {
-    id: "recorder/editing/silence-removal",
-    title: "Silence removal",
-    relativePath: "docs/recorder/editing/silence-removal.mdx",
-    compId: "articles-docs-recorder-editing-silence-removal",
-    crumb: "Recorder",
+    id: "still",
+    title: "<Still>",
+    relativePath: "docs/still.mdx",
+    compId: "articles-docs-still",
+    crumb: "API",
   },
   {
-    id: "recorder/editing/layout",
-    title: "Layout",
-    relativePath: "docs/recorder/editing/layout.mdx",
-    compId: "articles-docs-recorder-editing-layout",
-    crumb: "Recorder",
+    id: "stills",
+    title: "Still images",
+    relativePath: "docs/stills.mdx",
+    compId: "articles-docs-stills",
+    crumb: "How to generate",
   },
   {
-    id: "recorder/our-recorder",
-    title: "Our Recorder",
-    relativePath: "docs/recorder/our-recorder.mdx",
-    compId: "articles-docs-recorder-our-recorder",
-    crumb: "Recorder",
+    id: "studio/api",
+    title: "@remotion/studio",
+    relativePath: "docs/studio/api.mdx",
+    compId: "articles-docs-studio-api",
+    crumb: null,
   },
   {
-    id: "recorder/before-you-buy",
-    title: "Before you buy",
-    relativePath: "docs/recorder/before-you-buy.mdx",
-    compId: "articles-docs-recorder-before-you-buy",
-    crumb: "Recorder",
+    id: "studio/delete-static-file",
+    title: "deleteStaticFile()",
+    relativePath: "docs/studio/delete-static-file.mdx",
+    compId: "articles-docs-studio-delete-static-file",
+    crumb: "@remotion/studio",
   },
   {
-    id: "recorder/setup",
-    title: "Setup",
-    relativePath: "docs/recorder/setup.mdx",
-    compId: "articles-docs-recorder-setup",
-    crumb: "Recorder",
+    id: "studio/deploy-server",
+    title: "Deploy the Remotion Studio on a VPS",
+    relativePath: "docs/studio/deploy-server.mdx",
+    compId: "articles-docs-studio-deploy-server",
+    crumb: "Remotion Studio",
   },
   {
-    id: "fonts-api",
-    title: "@remotion/fonts",
-    relativePath: "docs/fonts-api/index.mdx",
-    compId: "articles-docs-fonts-api-index",
-    crumb: null,
+    id: "studio/deploy-static",
+    title: "Deploy Remotion Studio as static site",
+    relativePath: "docs/studio/deploy-static.mdx",
+    compId: "articles-docs-studio-deploy-static",
+    crumb: "Remotion Studio",
   },
   {
-    id: "fonts-api/load-font",
-    title: "loadFont()",
-    relativePath: "docs/fonts-api/load-font.mdx",
-    compId: "articles-docs-fonts-api-load-font",
-    crumb: "@remotion/fonts",
+    id: "studio/focus-default-props-path",
+    title: "focusDefaultPropsPath()",
+    relativePath: "docs/studio/focus-default-props-path.mdx",
+    compId: "articles-docs-studio-focus-default-props-path",
+    crumb: "@remotion/studio",
   },
   {
-    id: "authoring-packages",
-    title: "Authoring a Remotion library",
-    relativePath: "docs/creating-a-library.mdx",
-    compId: "articles-docs-creating-a-library",
-    crumb: "How to",
+    id: "studio/get-static-files",
+    title: "getStaticFiles()",
+    relativePath: "docs/studio/get-static-files.mdx",
+    compId: "articles-docs-studio-get-static-files",
+    crumb: "@remotion/studio",
   },
   {
-    id: "use-buffer-state",
-    title: "useBufferState()",
-    relativePath: "docs/use-buffer-state.mdx",
-    compId: "articles-docs-use-buffer-state",
-    crumb: "API",
+    id: "studio/quick-switcher",
+    title: "Quick Switcher",
+    relativePath: "docs/studio/quick-switcher.mdx",
+    compId: "articles-docs-studio-quick-switcher",
+    crumb: "Remotion Studio",
   },
   {
-    id: "transforms",
-    title: "Transforms",
-    relativePath: "docs/transforms.mdx",
-    compId: "articles-docs-transforms",
-    crumb: "The fundamentals",
+    id: "studio/reevaluate-composition",
+    title: "reevaluateComposition()",
+    relativePath: "docs/studio/reevaluate-composition.mdx",
+    compId: "articles-docs-studio-reevaluate-composition",
+    crumb: "@remotion/studio",
   },
   {
-    id: "use-current-frame",
-    title: "useCurrentFrame()",
-    relativePath: "docs/use-current-frame.mdx",
-    compId: "articles-docs-use-current-frame",
-    crumb: "API",
+    id: "studio/restart-studio",
+    title: "restartStudio()",
+    relativePath: "docs/studio/restart-studio.mdx",
+    compId: "articles-docs-studio-restart-studio",
+    crumb: "@remotion/studio",
   },
   {
-    id: "enable-scss/overview",
-    title: "@remotion/enable-scss",
-    relativePath: "docs/enable-scss/overview.mdx",
-    compId: "articles-docs-enable-scss-overview",
-    crumb: null,
+    id: "studio/save-default-props",
+    title: "saveDefaultProps()",
+    relativePath: "docs/studio/save-default-props.mdx",
+    compId: "articles-docs-studio-save-default-props",
+    crumb: "@remotion/studio",
   },
   {
-    id: "enable-scss/enable-scss",
-    title: "enableScss()",
-    relativePath: "docs/enable-scss/enable-scss.mdx",
-    compId: "articles-docs-enable-scss-enable-scss",
-    crumb: "@remotion/enable-scss",
+    id: "studio/shortcuts",
+    title: "Keyboard navigation",
+    relativePath: "docs/studio/shortcuts.mdx",
+    compId: "articles-docs-studio-shortcuts",
+    crumb: "Remotion Studio",
   },
   {
-    id: "3-0-migration",
-    title: "v3.0 Migration",
-    relativePath: "docs/3-0-migration.mdx",
-    compId: "articles-docs-3-0-migration",
-    crumb: "Version Upgrade",
+    id: "studio/studio",
+    title: "Starting the Studio",
+    relativePath: "docs/studio/studio.mdx",
+    compId: "articles-docs-studio-studio",
+    crumb: "Timeline basics",
   },
   {
-    id: "quality",
-    title: "Quality Guide",
-    relativePath: "docs/quality.mdx",
-    compId: "articles-docs-quality",
-    crumb: "Make it look good",
+    id: "studio/update-default-props",
+    title: "updateDefaultProps()",
+    relativePath: "docs/studio/update-default-props.mdx",
+    compId: "articles-docs-studio-update-default-props",
+    crumb: "@remotion/studio",
   },
   {
-    id: "composition",
-    title: "<Composition>",
-    relativePath: "docs/composition.mdx",
-    compId: "articles-docs-composition",
-    crumb: "API",
+    id: "studio/watch-public-folder",
+    title: "watchPublicFolder()",
+    relativePath: "docs/studio/watch-public-folder.mdx",
+    compId: "articles-docs-studio-watch-public-folder",
+    crumb: "@remotion/studio",
   },
   {
-    id: "audio",
-    title: "<Audio>",
-    relativePath: "docs/audio.mdx",
-    compId: "articles-docs-audio",
-    crumb: "How to",
+    id: "studio/watch-static-file",
+    title: "watchStaticFile()",
+    relativePath: "docs/studio/watch-static-file.mdx",
+    compId: "articles-docs-studio-watch-static-file",
+    crumb: "@remotion/studio",
   },
   {
-    id: "visualize-audio",
-    title: "visualizeAudio()",
-    relativePath: "docs/visualize-audio.mdx",
-    compId: "articles-docs-visualize-audio",
-    crumb: "@remotion/media-utils",
+    id: "studio/write-static-file",
+    title: "writeStaticFile()",
+    relativePath: "docs/studio/write-static-file.mdx",
+    compId: "articles-docs-studio-write-static-file",
+    crumb: "@remotion/studio",
   },
   {
-    id: "react-18",
-    title: "Upgrade to React 18",
-    relativePath: "docs/react-18.mdx",
-    compId: "articles-docs-react-18",
-    crumb: "The new and shiny",
+    id: "support",
+    title: "Support Policy",
+    relativePath: "docs/support.mdx",
+    compId: "articles-docs-support",
+    crumb: "Let us help you",
   },
   {
-    id: "cloudrun-alpha",
-    title: "Cloud Run Alpha",
-    relativePath: "docs/cloudrun-alpha.mdx",
-    compId: "articles-docs-cloudrun-alpha",
-    crumb: "Version Upgrade",
+    id: "tailwind/enable-tailwind",
+    title: "enableTailwind()",
+    relativePath: "docs/tailwind/enable-tailwind.mdx",
+    compId: "articles-docs-tailwind-enable-tailwind",
+    crumb: "@remotion/tailwind",
   },
   {
-    id: "easing",
-    title: "Easing",
-    relativePath: "docs/easing.mdx",
-    compId: "articles-docs-easing",
-    crumb: "API",
+    id: "tailwind",
+    title: "@remotion/tailwind",
+    relativePath: "docs/tailwind/overview.mdx",
+    compId: "articles-docs-tailwind-overview",
+    crumb: null,
   },
   {
-    id: "resources",
-    title: "List of resources",
-    relativePath: "docs/resources.mdx",
-    compId: "articles-docs-resources",
-    crumb: "Ecosystem",
+    id: "tailwind-legacy",
+    title: "TailwindCSS v2 (Legacy)",
+    relativePath: "docs/tailwind-2.mdx",
+    compId: "articles-docs-tailwind-2",
+    crumb: "Legacy docs",
   },
   {
-    id: "schemas",
-    title: "Defining a schema for your props",
-    relativePath: "docs/schemas.mdx",
-    compId: "articles-docs-schemas",
-    crumb: "How To",
+    id: "tailwind",
+    title: "TailwindCSS",
+    relativePath: "docs/tailwind.mdx",
+    compId: "articles-docs-tailwind",
+    crumb: "text-lg font-bold",
   },
   {
-    id: "slow-method-to-extract-frame",
-    title: "Slow method to extract frame",
-    relativePath: "docs/slow-method-to-extract-frame.mdx",
-    compId: "articles-docs-slow-method-to-extract-frame",
+    id: "target-closed",
+    title: "'Target closed' error message",
+    relativePath: "docs/target-closed.mdx",
+    compId: "articles-docs-target-closed",
     crumb: "Troubleshooting",
   },
   {
-    id: "lambda",
-    title: "@remotion/lambda",
-    relativePath: "docs/lambda.mdx",
-    compId: "articles-docs-lambda",
-    crumb: null,
+    id: "terminology/bundle",
+    title: "Remotion Bundle",
+    relativePath: "docs/terminology/bundle.mdx",
+    compId: "articles-docs-terminology-bundle",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/timings/springtiming",
-    title: "springTiming()",
-    relativePath: "docs/transitions/timings/springtiming.mdx",
-    compId: "articles-docs-transitions-timings-springtiming",
-    crumb: "@remotion/transitions - Timings",
+    id: "terminology/cloud-run-url",
+    title: "Cloud Run URL",
+    relativePath: "docs/terminology/cloud-run-url.mdx",
+    compId: "articles-docs-terminology-cloud-run-url",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/timings/index",
-    title: "Timings",
-    relativePath: "docs/transitions/timings/index.mdx",
-    compId: "articles-docs-transitions-timings-index",
-    crumb: "@remotion/transitions",
+    id: "terminology/composition",
+    title: "Composition",
+    relativePath: "docs/terminology/composition.mdx",
+    compId: "articles-docs-terminology-composition",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/timings/custom",
-    title: "Custom timings",
-    relativePath: "docs/transitions/timings/custom.mdx",
-    compId: "articles-docs-transitions-timings-custom",
-    crumb: "@remotion/transitions",
+    id: "terminology/concurrency",
+    title: "Concurrency",
+    relativePath: "docs/terminology/concurrency.mdx",
+    compId: "articles-docs-terminology-concurrency",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/timings/lineartiming",
-    title: "linearTiming()",
-    relativePath: "docs/transitions/timings/lineartiming.mdx",
-    compId: "articles-docs-transitions-timings-lineartiming",
-    crumb: "@remotion/transitions - Timings",
+    id: "terminology/entry-point",
+    title: "Entry point",
+    relativePath: "docs/terminology/entry-point.mdx",
+    compId: "articles-docs-terminology-entry-point",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/index",
-    title: "@remotion/transitions",
-    relativePath: "docs/transitions/index.mdx",
-    compId: "articles-docs-transitions-index",
-    crumb: null,
+    id: "terminology/input-props",
+    title: "Input Props",
+    relativePath: "docs/terminology/input-props.mdx",
+    compId: "articles-docs-terminology-input-props",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/presentations/clock-wipe",
-    title: "clockWipe()",
-    relativePath: "docs/transitions/presentations/clock-wipe.mdx",
-    compId: "articles-docs-transitions-presentations-clock-wipe",
-    crumb: "@remotion/transitions - Presentations",
+    id: "terminology/player",
+    title: "Remotion Player",
+    relativePath: "docs/terminology/player.mdx",
+    compId: "articles-docs-terminology-player",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/presentations/index",
-    title: "Presentations",
-    relativePath: "docs/transitions/presentations/index.mdx",
-    compId: "articles-docs-transitions-presentations-index",
-    crumb: "@remotion/transitions",
+    id: "terminology/public-dir",
+    title: "Public directory",
+    relativePath: "docs/terminology/public-dir.mdx",
+    compId: "articles-docs-terminology-public-dir",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/presentations/custom",
-    title: "Custom presentations",
-    relativePath: "docs/transitions/presentations/custom.mdx",
-    compId: "articles-docs-transitions-presentations-custom",
-    crumb: "@remotion/transitions",
+    id: "terminology/remotion-root",
+    title: "Remotion Root",
+    relativePath: "docs/terminology/remotion-root.mdx",
+    compId: "articles-docs-terminology-remotion-root",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/presentations/cube",
-    title: "cube()",
-    relativePath: "docs/transitions/presentations/cube.mdx",
-    compId: "articles-docs-transitions-presentations-cube",
-    crumb: "@remotion/transitions - Presentations",
+    id: "terminology/root-file",
+    title: "Root file",
+    relativePath: "docs/terminology/root-file.mdx",
+    compId: "articles-docs-terminology-root-file",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/presentations/wipe",
-    title: "wipe()",
-    relativePath: "docs/transitions/presentations/wipe.mdx",
-    compId: "articles-docs-transitions-presentations-wipe",
-    crumb: "@remotion/transitions - Presentations",
+    id: "terminology/sequence",
+    title: "Sequence",
+    relativePath: "docs/terminology/sequence.mdx",
+    compId: "articles-docs-terminology-sequence",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/presentations/fade",
-    title: "fade()",
-    relativePath: "docs/transitions/presentations/fade.mdx",
-    compId: "articles-docs-transitions-presentations-fade",
-    crumb: "@remotion/transitions - Presentations",
+    id: "terminology/serve-url",
+    title: "Serve URL",
+    relativePath: "docs/terminology/serve-url.mdx",
+    compId: "articles-docs-terminology-serve-url",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/presentations/slide",
-    title: "slide()",
-    relativePath: "docs/transitions/presentations/slide.mdx",
-    compId: "articles-docs-transitions-presentations-slide",
-    crumb: "@remotion/transitions - Presentations",
+    id: "terminology/service-name",
+    title: "Service Name",
+    relativePath: "docs/terminology/service-name.mdx",
+    compId: "articles-docs-terminology-service-name",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/presentations/flip",
-    title: "flip()",
-    relativePath: "docs/transitions/presentations/flip.mdx",
-    compId: "articles-docs-transitions-presentations-flip",
-    crumb: "@remotion/transitions - Presentations",
+    id: "terminology/studio",
+    title: "Remotion Studio",
+    relativePath: "docs/terminology/studio.mdx",
+    compId: "articles-docs-terminology-studio",
+    crumb: "Terminology",
   },
   {
-    id: "transitions/transitionseries",
-    title: "<TransitionSeries>",
-    relativePath: "docs/transitions/transitionseries.mdx",
-    compId: "articles-docs-transitions-transitionseries",
-    crumb: "@remotion/transitions",
+    id: "terminology",
+    title: "Terminology",
+    relativePath: "docs/terminology.mdx",
+    compId: "articles-docs-terminology",
+    crumb: "The Remotion dictionary",
   },
   {
-    id: "transitions/audio-transitions",
-    title: "Transitioning with audio",
-    relativePath: "docs/transitions/audio-transitions.mdx",
-    compId: "articles-docs-transitions-audio-transitions",
-    crumb: "@remotion/transitions",
+    id: "the-fundamentals",
+    title: "The fundamentals",
+    relativePath: "docs/the-fundamentals.mdx",
+    compId: "articles-docs-the-fundamentals",
+    crumb: "Getting started",
   },
   {
-    id: "video-vs-offthreadvideo",
-    title: "<Video> vs. <OffthreadVideo>",
-    relativePath: "docs/video-vs-offthreadvideo.mdx",
-    compId: "articles-docs-video-vs-offthreadvideo",
-    crumb: "Comparison",
+    id: "third-party",
+    title: "Third party integrations",
+    relativePath: "docs/third-party.mdx",
+    compId: "articles-docs-third-party",
+    crumb: "Integrations",
   },
   {
-    id: "ffmpeg",
-    title: "Installing FFmpeg",
-    relativePath: "docs/ffmpeg.mdx",
-    compId: "articles-docs-ffmpeg",
-    crumb: "(you don't have to)",
+    id: "three-canvas",
+    title: "<ThreeCanvas>",
+    relativePath: "docs/three-canvas.mdx",
+    compId: "articles-docs-three-canvas",
+    crumb: "@remotion/three",
   },
   {
-    id: "video",
-    title: "<Video>",
-    relativePath: "docs/video.mdx",
-    compId: "articles-docs-video",
-    crumb: "API",
+    id: "three",
+    title: "@remotion/three",
+    relativePath: "docs/three.mdx",
+    compId: "articles-docs-three",
+    crumb: null,
   },
   {
-    id: "measure-spring",
-    title: "measureSpring()",
-    relativePath: "docs/measure-spring.mdx",
-    compId: "articles-docs-measure-spring",
-    crumb: "API",
+    id: "timeout",
+    title: "Debugging timeouts",
+    relativePath: "docs/timeout.mdx",
+    compId: "articles-docs-timeout",
+    crumb: "Troubleshooting",
   },
   {
-    id: "javascript",
-    title: "Plain JavaScript",
-    relativePath: "docs/jsx-support.mdx",
-    compId: "articles-docs-jsx-support",
-    crumb: "How To",
+    id: "transforms",
+    title: "Transforms",
+    relativePath: "docs/transforms.mdx",
+    compId: "articles-docs-transforms",
+    crumb: "The fundamentals",
   },
   {
-    id: "stills",
-    title: "Still images",
-    relativePath: "docs/stills.mdx",
-    compId: "articles-docs-stills",
-    crumb: "How to generate",
+    id: "transitions/audio-transitions",
+    title: "Transitioning with audio",
+    relativePath: "docs/transitions/audio-transitions.mdx",
+    compId: "articles-docs-transitions-audio-transitions",
+    crumb: "@remotion/transitions",
   },
   {
-    id: "get-help",
-    title: "Get help",
-    relativePath: "docs/ask-for-help.mdx",
-    compId: "articles-docs-ask-for-help",
+    id: "transitions/index",
+    title: "@remotion/transitions",
+    relativePath: "docs/transitions/index.mdx",
+    compId: "articles-docs-transitions-index",
     crumb: null,
   },
   {
-    id: "still",
-    title: "<Still>",
-    relativePath: "docs/still.mdx",
-    compId: "articles-docs-still",
-    crumb: "API",
+    id: "transitions/presentations/clock-wipe",
+    title: "clockWipe()",
+    relativePath: "docs/transitions/presentations/clock-wipe.mdx",
+    compId: "articles-docs-transitions-presentations-clock-wipe",
+    crumb: "@remotion/transitions - Presentations",
   },
   {
-    id: "preload-video",
-    title: "preloadVideo()",
-    relativePath: "docs/preload/preload-video.mdx",
-    compId: "articles-docs-preload-preload-video",
-    crumb: "@remotion/player",
+    id: "transitions/presentations/cube",
+    title: "cube()",
+    relativePath: "docs/transitions/presentations/cube.mdx",
+    compId: "articles-docs-transitions-presentations-cube",
+    crumb: "@remotion/transitions - Presentations",
   },
   {
-    id: "preload-audio",
-    title: "preloadAudio()",
-    relativePath: "docs/preload/preload-audio.mdx",
-    compId: "articles-docs-preload-preload-audio",
-    crumb: "@remotion/preload",
+    id: "transitions/presentations/custom",
+    title: "Custom presentations",
+    relativePath: "docs/transitions/presentations/custom.mdx",
+    compId: "articles-docs-transitions-presentations-custom",
+    crumb: "@remotion/transitions",
   },
   {
-    id: "preload-image",
-    title: "preloadImage()",
-    relativePath: "docs/preload/preload-image.mdx",
-    compId: "articles-docs-preload-preload-image",
-    crumb: "@remotion/preload",
+    id: "transitions/presentations/fade",
+    title: "fade()",
+    relativePath: "docs/transitions/presentations/fade.mdx",
+    compId: "articles-docs-transitions-presentations-fade",
+    crumb: "@remotion/transitions - Presentations",
   },
   {
-    id: "preload-font",
-    title: "preloadFont()",
-    relativePath: "docs/preload/preload-font.mdx",
-    compId: "articles-docs-preload-preload-font",
-    crumb: "@remotion/preload",
+    id: "transitions/presentations/flip",
+    title: "flip()",
+    relativePath: "docs/transitions/presentations/flip.mdx",
+    compId: "articles-docs-transitions-presentations-flip",
+    crumb: "@remotion/transitions - Presentations",
   },
   {
-    id: "preload",
-    title: "@remotion/preload",
-    relativePath: "docs/preload/preload.mdx",
-    compId: "articles-docs-preload-preload",
-    crumb: null,
+    id: "transitions/presentations/index",
+    title: "Presentations",
+    relativePath: "docs/transitions/presentations/index.mdx",
+    compId: "articles-docs-transitions-presentations-index",
+    crumb: "@remotion/transitions",
   },
   {
-    id: "version",
-    title: "VERSION",
-    relativePath: "docs/version.mdx",
-    compId: "articles-docs-version",
-    crumb: "API",
+    id: "transitions/presentations/slide",
+    title: "slide()",
+    relativePath: "docs/transitions/presentations/slide.mdx",
+    compId: "articles-docs-transitions-presentations-slide",
+    crumb: "@remotion/transitions - Presentations",
   },
   {
-    id: "prereleases",
-    title: "Testing prereleases",
-    relativePath: "docs/prereleases.mdx",
-    compId: "articles-docs-prereleases",
-    crumb: "Only the brave",
+    id: "transitions/presentations/wipe",
+    title: "wipe()",
+    relativePath: "docs/transitions/presentations/wipe.mdx",
+    compId: "articles-docs-transitions-presentations-wipe",
+    crumb: "@remotion/transitions - Presentations",
   },
   {
-    id: "artifact",
-    title: "<Artifact>",
-    relativePath: "docs/artifact.mdx",
-    compId: "articles-docs-artifact",
-    crumb: "API",
+    id: "transitions/timings/custom",
+    title: "Custom timings",
+    relativePath: "docs/transitions/timings/custom.mdx",
+    compId: "articles-docs-transitions-timings-custom",
+    crumb: "@remotion/transitions",
   },
   {
-    id: "license",
-    title: "License & Pricing",
-    relativePath: "docs/license.mdx",
-    compId: "articles-docs-license",
-    crumb: null,
+    id: "transitions/timings/index",
+    title: "Timings",
+    relativePath: "docs/transitions/timings/index.mdx",
+    compId: "articles-docs-transitions-timings-index",
+    crumb: "@remotion/transitions",
   },
   {
-    id: "fonts",
-    title: "Using fonts",
-    relativePath: "docs/fonts.mdx",
-    compId: "articles-docs-fonts",
-    crumb: "Techniques",
+    id: "transitions/timings/lineartiming",
+    title: "linearTiming()",
+    relativePath: "docs/transitions/timings/lineartiming.mdx",
+    compId: "articles-docs-transitions-timings-lineartiming",
+    crumb: "@remotion/transitions - Timings",
   },
   {
-    id: "enametoolong",
-    title: "ENAMETOOLONG",
-    relativePath: "docs/enametoolong.mdx",
-    compId: "articles-docs-enametoolong",
-    crumb: "Troubleshooting",
+    id: "transitions/timings/springtiming",
+    title: "springTiming()",
+    relativePath: "docs/transitions/timings/springtiming.mdx",
+    compId: "articles-docs-transitions-timings-springtiming",
+    crumb: "@remotion/transitions - Timings",
   },
   {
-    id: "get-waveform-portion",
-    title: "getWaveformPortion()",
-    relativePath: "docs/get-waveform-portion.mdx",
-    compId: "articles-docs-get-waveform-portion",
-    crumb: "@remotion/media-utils",
+    id: "transitions/transitionseries",
+    title: "<TransitionSeries>",
+    relativePath: "docs/transitions/transitionseries.mdx",
+    compId: "articles-docs-transitions-transitionseries",
+    crumb: "@remotion/transitions",
   },
   {
-    id: "webpack",
-    title: "Custom Webpack config",
-    relativePath: "docs/overwriting-webpack-config.mdx",
-    compId: "articles-docs-overwriting-webpack-config",
-    crumb: "How To",
+    id: "transitioning",
+    title: "Transitions",
+    relativePath: "docs/transitions.mdx",
+    compId: "articles-docs-transitions",
+    crumb: "Techniques",
   },
   {
-    id: '"null"',
-    title: "<Experimental.Null>",
-    relativePath: "docs/null.mdx",
-    compId: "articles-docs-null",
-    crumb: "Experimental API",
+    id: "transparent-videos",
+    title: "Transparent videos",
+    relativePath: "docs/transparent-videos.mdx",
+    compId: "articles-docs-transparent-videos",
+    crumb: "Techniques",
   },
   {
-    id: "parameterized-rendering",
-    title: "Parameterized videos",
-    relativePath: "docs/parameterized-rendering.mdx",
-    compId: "articles-docs-parameterized-rendering",
-    crumb: "How To",
+    id: "troubleshooting/background-image",
+    title: "Flickering when using background-image or mask-image",
+    relativePath: "docs/troubleshooting/background-image.mdx",
+    compId: "articles-docs-troubleshooting-background-image",
+    crumb: "Common mistakes",
   },
   {
-    id: "get-audio-data",
-    title: "getAudioData()",
-    relativePath: "docs/get-audio-data.mdx",
-    compId: "articles-docs-get-audio-data",
-    crumb: "@remotion/media-utils",
+    id: "broken-fast-refresh",
+    title: "Fast Refresh not working",
+    relativePath: "docs/troubleshooting/broken-fast-refresh.mdx",
+    compId: "articles-docs-troubleshooting-broken-fast-refresh",
+    crumb: "Troubleshooting",
   },
   {
-    id: "clipper",
-    title: "<Experimental.Clipper>",
-    relativePath: "docs/clipper.mdx",
-    compId: "articles-docs-clipper",
-    crumb: "Experimental API",
+    id: "troubleshooting/browser-launch",
+    title: "Failed to launch the browser process",
+    relativePath: "docs/troubleshooting/browser-launch.mdx",
+    compId: "articles-docs-troubleshooting-browser-launch",
+    crumb: "Troubleshooting",
   },
   {
-    id: "random",
-    title: "random()",
-    relativePath: "docs/random.mdx",
-    compId: "articles-docs-random",
-    crumb: "API",
+    id: "troubleshooting/bundling-bundle",
+    title: "Calling bundle() in bundled code",
+    relativePath: "docs/troubleshooting/bundling-bundle.mdx",
+    compId: "articles-docs-troubleshooting-bundling-bundle",
+    crumb: "Troubleshooting",
   },
   {
-    id: "remotion",
-    title: "remotion",
-    relativePath: "docs/remotion.mdx",
-    compId: "articles-docs-remotion",
-    crumb: "Core API Reference",
+    id: "troubleshooting/could-not-be-parsed-as-a-value-list",
+    title: "The source provided could not be parsed as a value list",
+    relativePath:
+      "docs/troubleshooting/could-not-be-parsed-as-a-value-list.mdx",
+    compId: "articles-docs-troubleshooting-could-not-be-parsed-as-a-value-list",
+    crumb: "Browser quirks",
   },
   {
-    id: "folder",
-    title: "<Folder>",
-    relativePath: "docs/folder.mdx",
-    compId: "articles-docs-folder",
-    crumb: "API",
+    id: "troubleshooting/debug-failed-render",
+    title: "Debugging render failures",
+    relativePath: "docs/troubleshooting/debug-failed-render.mdx",
+    compId: "articles-docs-troubleshooting-debug-failed-render",
+    crumb: "Troubleshooting",
   },
   {
-    id: "video-manipulation",
-    title: "Video manipulation",
-    relativePath: "docs/video-manipulation.mdx",
-    compId: "articles-docs-video-manipulation",
-    crumb: "How To",
+    id: "defaultprops-too-big",
+    title: "defaultProps too big - could not serialize",
+    relativePath: "docs/troubleshooting/defaultprops-too-big.mdx",
+    compId: "articles-docs-troubleshooting-defaultprops-too-big",
+    crumb: "Troubleshooting",
   },
   {
-    id: "use-offthread-video-texture",
-    title: "useOffthreadVideoTexture()",
-    relativePath: "docs/use-offthread-video-texture.mdx",
-    compId: "articles-docs-use-offthread-video-texture",
-    crumb: "@remotion/three",
+    id: "troubleshooting/delay-render-proxy",
+    title: "Loading <Img> with src http://localhost:3000/proxy",
+    relativePath: "docs/troubleshooting/delay-render-proxy.mdx",
+    compId: "articles-docs-troubleshooting-delay-render-proxy",
+    crumb: "Troubleshooting",
   },
   {
-    id: "animation-math",
-    title: "Animation math",
-    relativePath: "docs/animation-math.mdx",
-    compId: "articles-docs-animation-math",
-    crumb: "Techniques",
+    id: "loading-root-component",
+    title: "Root component Timeout",
+    relativePath: "docs/troubleshooting/loading-root-component.mdx",
+    compId: "articles-docs-troubleshooting-loading-root-component",
+    crumb: "Troubleshooting",
   },
   {
-    id: "render-all",
-    title: "Render all compositions",
-    relativePath: "docs/render-all.mdx",
-    compId: "articles-docs-render-all",
-    crumb: "Techniques",
+    id: "troubleshooting/nextjs-image",
+    title: "Flickering when using Next.js <Image> tag",
+    relativePath: "docs/troubleshooting/nextjs-image.mdx",
+    compId: "articles-docs-troubleshooting-nextjs-image",
+    crumb: "Common mistakes",
   },
   {
-    id: "layout-utils/index",
-    title: "@remotion/layout-utils",
-    relativePath: "docs/layout-utils/index.mdx",
-    compId: "articles-docs-layout-utils-index",
-    crumb: null,
+    id: "rosetta",
+    title: "Apple Silicon under Rosetta",
+    relativePath: "docs/troubleshooting/rosetta.mdx",
+    compId: "articles-docs-troubleshooting-rosetta",
+    crumb: "Troubleshooting",
+  },
+  {
+    id: "troubleshooting/sigkill",
+    title: "Process quit with signal SIGKILL",
+    relativePath: "docs/troubleshooting/sigkill.mdx",
+    compId: "articles-docs-troubleshooting-sigkill",
+    crumb: "Troubleshooting",
   },
   {
-    id: "layout-utils/best-practices",
-    title: "Best practices for @remotion/layout-utils",
-    relativePath: "docs/layout-utils/best-practices.mdx",
-    compId: "articles-docs-layout-utils-best-practices",
-    crumb: "@remotion/layout-utils",
+    id: "troubleshooting/timed-out-page-function",
+    title: "Timed out evaluating page function",
+    relativePath: "docs/troubleshooting/timed-out-page-function.mdx",
+    compId: "articles-docs-troubleshooting-timed-out-page-function",
+    crumb: "Troubleshooting",
   },
   {
-    id: "layout-utils/measure-text",
-    title: "measureText()",
-    relativePath: "docs/layout-utils/measure-text.mdx",
-    compId: "articles-docs-layout-utils-measure-text",
-    crumb: "@remotion/layout-utils",
+    id: "player-flicker",
+    title: "Avoiding flickering in <Player>",
+    relativePath: "docs/troubleshooting/video-flicker.mdx",
+    compId: "articles-docs-troubleshooting-video-flicker",
+    crumb: "Frame-perfection",
   },
   {
-    id: "layout-utils/fill-text-box",
-    title: "fillTextBox()",
-    relativePath: "docs/layout-utils/fill-text-box.mdx",
-    compId: "articles-docs-layout-utils-fill-text-box",
-    crumb: "@remotion/layout-utils",
+    id: "upgrading",
+    title: "Upgrading Remotion",
+    relativePath: "docs/upgrading.mdx",
+    compId: "articles-docs-upgrading",
+    crumb: "upgrade remotion",
   },
   {
-    id: "layout-utils/fit-text",
-    title: "fitText()",
-    relativePath: "docs/layout-utils/fit-text.mdx",
-    compId: "articles-docs-layout-utils-fit-text",
-    crumb: "@remotion/layout-utils",
+    id: "use-audio-data",
+    title: "useAudioData()",
+    relativePath: "docs/use-audio-data.mdx",
+    compId: "articles-docs-use-audio-data",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "buffer-state",
-    title: "Display a buffer state",
-    relativePath: "docs/buffer-state.mdx",
-    compId: "articles-docs-buffer-state",
-    crumb: "Building video apps",
+    id: "use-buffer-state",
+    title: "useBufferState()",
+    relativePath: "docs/use-buffer-state.mdx",
+    compId: "articles-docs-use-buffer-state",
+    crumb: "API",
   },
   {
-    id: "staticfile-remote-urls",
-    title: "staticFile() does not support remote URLs",
-    relativePath: "docs/static-file-remote-urls.mdx",
-    compId: "articles-docs-static-file-remote-urls",
-    crumb: "Troubleshooting",
+    id: "use-current-frame",
+    title: "useCurrentFrame()",
+    relativePath: "docs/use-current-frame.mdx",
+    compId: "articles-docs-use-current-frame",
+    crumb: "API",
   },
   {
-    id: "player/index",
-    title: "@remotion/player",
-    relativePath: "docs/player/index.mdx",
-    compId: "articles-docs-player-index",
-    crumb: null,
+    id: "use-current-scale",
+    title: "useCurrentScale()",
+    relativePath: "docs/use-current-scale.mdx",
+    compId: "articles-docs-use-current-scale",
+    crumb: "API",
   },
   {
-    id: "integration",
-    title: "Code sharing",
-    relativePath: "docs/player/player-integration.mdx",
-    compId: "articles-docs-player-player-integration",
-    crumb: "@remotion/player",
+    id: "use-img-and-iframe",
+    title: "<Img>, <Video> and <Audio>",
+    relativePath: "docs/use-img-and-iframe.mdx",
+    compId: "articles-docs-use-img-and-iframe",
+    crumb: "Best practices",
   },
   {
-    id: "player/preloading",
-    title: "Preloading assets",
-    relativePath: "docs/player/preloading.mdx",
-    compId: "articles-docs-player-preloading",
-    crumb: "@remotion/player",
+    id: "use-offthread-video-texture",
+    title: "useOffthreadVideoTexture()",
+    relativePath: "docs/use-offthread-video-texture.mdx",
+    compId: "articles-docs-use-offthread-video-texture",
+    crumb: "@remotion/three",
   },
   {
-    id: "best-practices",
-    title: "Player - Best practices",
-    relativePath: "docs/player/best-practices.mdx",
-    compId: "articles-docs-player-best-practices",
-    crumb: "@remotion/player",
+    id: "use-video-config",
+    title: "useVideoConfig()",
+    relativePath: "docs/use-video-config.mdx",
+    compId: "articles-docs-use-video-config",
+    crumb: "API",
   },
   {
-    id: "autoplay",
-    title: "Combatting autoplay issues",
-    relativePath: "docs/player/autoplay.mdx",
-    compId: "articles-docs-player-autoplay",
-    crumb: "@remotion/player",
+    id: "use-video-texture",
+    title: "useVideoTexture()",
+    relativePath: "docs/use-video-texture.mdx",
+    compId: "articles-docs-use-video-texture",
+    crumb: "@remotion/three",
   },
   {
-    id: "player/premounting",
-    title: "Premounting",
-    relativePath: "docs/player/premounting.mdx",
-    compId: "articles-docs-player-premounting",
-    crumb: "@remotion/player",
+    id: "using-audio",
+    title: "Using audio",
+    relativePath: "docs/using-audio.mdx",
+    compId: "articles-docs-using-audio",
+    crumb: "Techniques",
   },
   {
-    id: "player/api",
-    title: "<Player>",
-    relativePath: "docs/player/api.mdx",
-    compId: "articles-docs-player-api",
-    crumb: "@remotion/player",
+    id: "using-randomness",
+    title: "Using randomness",
+    relativePath: "docs/using-randomness.mdx",
+    compId: "articles-docs-using-randomness",
+    crumb: "Roll the dice",
   },
   {
-    id: "thumbnail",
-    title: "<Thumbnail>",
-    relativePath: "docs/player/thumbnail.mdx",
-    compId: "articles-docs-player-thumbnail",
-    crumb: "@remotion/player",
+    id: "version-mismatch",
+    title: "Version mismatch",
+    relativePath: "docs/version-mismatch.mdx",
+    compId: "articles-docs-version-mismatch",
+    crumb: "How to fix a",
   },
   {
-    id: "current-time",
-    title: "Displaying the current time",
-    relativePath: "docs/player/current-time.mdx",
-    compId: "articles-docs-player-current-time",
-    crumb: "@remotion/player",
+    id: "version",
+    title: "VERSION",
+    relativePath: "docs/version.mdx",
+    compId: "articles-docs-version",
+    crumb: "API",
   },
   {
-    id: "examples",
-    title: "Examples for @remotion/player",
-    relativePath: "docs/player/player-examples.mdx",
-    compId: "articles-docs-player-player-examples",
-    crumb: "@remotion/player",
+    id: "video-manipulation",
+    title: "Video manipulation",
+    relativePath: "docs/video-manipulation.mdx",
+    compId: "articles-docs-video-manipulation",
+    crumb: "How To",
   },
   {
-    id: "installation",
-    title: "Installation",
-    relativePath: "docs/player/installation.mdx",
-    compId: "articles-docs-player-installation",
-    crumb: "@remotion/player",
+    id: "video-uploads",
+    title: "Handling user video uploads",
+    relativePath: "docs/video-uploads.mdx",
+    compId: "articles-docs-video-uploads",
+    crumb: "Building video apps",
   },
   {
-    id: "buffer-state",
-    title: "The Player buffer state",
-    relativePath: "docs/player/buffer-state.mdx",
-    compId: "articles-docs-player-buffer-state",
-    crumb: "Best practices",
+    id: "video-vs-offthreadvideo",
+    title: "<Video> vs. <OffthreadVideo>",
+    relativePath: "docs/video-vs-offthreadvideo.mdx",
+    compId: "articles-docs-video-vs-offthreadvideo",
+    crumb: "Comparison",
   },
   {
-    id: "scaling",
-    title: "Sizing",
-    relativePath: "docs/player/scaling.mdx",
-    compId: "articles-docs-player-scaling",
-    crumb: "@remotion/player",
+    id: "video",
+    title: "<Video>",
+    relativePath: "docs/video.mdx",
+    compId: "articles-docs-video",
+    crumb: "API",
   },
   {
     id: "visual-editing",
@@ -3563,45 +3570,45 @@ export const articles = [
     crumb: "How To",
   },
   {
-    id: "4-0-migration",
-    title: "v4.0 Migration",
-    relativePath: "docs/4-0-migration.mdx",
-    compId: "articles-docs-4-0-migration",
-    crumb: "Version Upgrade",
+    id: "visualize-audio",
+    title: "visualizeAudio()",
+    relativePath: "docs/visualize-audio.mdx",
+    compId: "articles-docs-visualize-audio",
+    crumb: "@remotion/media-utils",
   },
   {
-    id: "get-gif-duration-in-seconds",
-    title: "getGifDurationInSeconds()",
-    relativePath: "docs/gif/get-gif-duration-in-seconds.mdx",
-    compId: "articles-docs-gif-get-gif-duration-in-seconds",
-    crumb: "@remotion/gif",
+    id: "watchstaticfile",
+    title: "watchStaticFile()",
+    relativePath: "docs/watch-static-file.mdx",
+    compId: "articles-docs-watch-static-file",
+    crumb: "API",
   },
   {
-    id: "gif/preload-gif",
-    title: "preloadGif()",
-    relativePath: "docs/gif/preload-gif.mdx",
-    compId: "articles-docs-gif-preload-gif",
-    crumb: "@remotion/gif",
+    id: "wrong-composition-mount",
+    title: "Wrongly mounted <Composition>",
+    relativePath: "docs/wrong-composition-mount.mdx",
+    compId: "articles-docs-wrong-composition-mount",
+    crumb: "Troubleshooting",
   },
   {
-    id: "gif/index",
-    title: "@remotion/gif",
-    relativePath: "docs/gif/index.mdx",
-    compId: "articles-docs-gif-index",
-    crumb: null,
+    id: "zod-types/index",
+    title: "@remotion/zod-types",
+    relativePath: "docs/zod-types/index.mdx",
+    compId: "articles-docs-zod-types-index",
+    crumb: "Schema",
   },
   {
-    id: "gif/gif",
-    title: "<Gif>",
-    relativePath: "docs/gif/gif.mdx",
-    compId: "articles-docs-gif-gif",
-    crumb: "@remotion/gif",
+    id: "zod-types/z-color",
+    title: "zColor()",
+    relativePath: "docs/zod-types/z-color.mdx",
+    compId: "articles-docs-zod-types-z-color",
+    crumb: null,
   },
   {
-    id: "scaling",
-    title: "Output scaling",
-    relativePath: "docs/scaling.mdx",
-    compId: "articles-docs-scaling",
-    crumb: "How To",
+    id: "zod-types/z-textarea",
+    title: "zTextarea()",
+    relativePath: "docs/zod-types/z-textarea.mdx",
+    compId: "articles-docs-zod-types-z-textarea",
+    crumb: null,
   },
 ];
diff --git a/packages/docs/static/generated/articles-docs-artifacts.png b/packages/docs/static/generated/articles-docs-artifacts.png
new file mode 100644
index 0000000000000000000000000000000000000000..2ddc01c2728aa73f104e04911a0d941bccde884c
GIT binary patch
literal 37466
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
G?Ee5I*Pmhl

literal 0
HcmV?d00001

diff --git a/packages/lambda/src/api/render-still-on-lambda.ts b/packages/lambda/src/api/render-still-on-lambda.ts
index dd4c2d8ce21..73326f45ebd 100644
--- a/packages/lambda/src/api/render-still-on-lambda.ts
+++ b/packages/lambda/src/api/render-still-on-lambda.ts
@@ -5,7 +5,7 @@ import type {
 } from '@remotion/renderer';
 import type {BrowserSafeApis} from '@remotion/renderer/client';
 import {NoReactAPIs} from '@remotion/renderer/pure';
-import type {ReceivedAsset} from '../functions/helpers/overall-render-progress';
+import type {ReceivedArtifact} from '../functions/helpers/overall-render-progress';
 import type {RenderStillLambdaResponsePayload} from '../functions/still';
 import type {AwsRegion} from '../pricing/aws-regions';
 import {callLambdaWithStreaming} from '../shared/call-lambda';
@@ -68,7 +68,7 @@ export type RenderStillOnLambdaOutput = {
 	bucketName: string;
 	renderId: string;
 	cloudWatchLogs: string;
-	receivedAssets: ReceivedAsset[];
+	artifacts: ReceivedArtifact[];
 };
 
 const internalRenderStillOnLambda = async (
@@ -132,7 +132,7 @@ const internalRenderStillOnLambda = async (
 				renderId: res.renderId,
 				rendererFunctionName: null,
 			}),
-			receivedAssets: res.receivedAssets,
+			artifacts: res.receivedArtifacts,
 		};
 	} catch (err) {
 		if ((err as Error).stack?.includes('UnrecognizedClientException')) {
diff --git a/packages/lambda/src/cli/commands/render/progress.ts b/packages/lambda/src/cli/commands/render/progress.ts
index cfd637d60fb..2f29c13b491 100644
--- a/packages/lambda/src/cli/commands/render/progress.ts
+++ b/packages/lambda/src/cli/commands/render/progress.ts
@@ -2,7 +2,7 @@ import {CliInternals} from '@remotion/cli';
 import {RenderInternals} from '@remotion/renderer';
 import {NoReactInternals} from 'remotion/no-react';
 import type {RenderProgress} from '../../../defaults';
-import type {ReceivedAsset} from '../../../functions/helpers/overall-render-progress';
+import type {ReceivedArtifact} from '../../../functions/helpers/overall-render-progress';
 import {truthy} from '../../../shared/truthy';
 
 type LambdaInvokeProgress = {
@@ -201,7 +201,7 @@ const makeTopRow = (overall: RenderProgress) => {
 	return CliInternals.chalk.gray(str);
 };
 
-export const makeArtifactProgress = (artifactProgress: ReceivedAsset[]) => {
+export const makeArtifactProgress = (artifactProgress: ReceivedArtifact[]) => {
 	if (artifactProgress.length === 0) {
 		return null;
 	}
@@ -240,7 +240,7 @@ export const makeProgressString = ({
 		...makeRenderProgress(overall),
 		makeCombinationProgress(overall),
 		downloadInfo ? makeDownloadProgress(downloadInfo) : null,
-		makeArtifactProgress(overall.artifactProgress),
+		makeArtifactProgress(overall.artifacts),
 	]
 		.filter(NoReactInternals.truthy)
 		.join('\n');
diff --git a/packages/lambda/src/cli/commands/still.ts b/packages/lambda/src/cli/commands/still.ts
index 9c17a9ba290..61b2725a243 100644
--- a/packages/lambda/src/cli/commands/still.ts
+++ b/packages/lambda/src/cli/commands/still.ts
@@ -276,7 +276,7 @@ export const stillCommand = async (
 			indent: false,
 			logLevel,
 		},
-		makeArtifactProgress(res.receivedAssets),
+		makeArtifactProgress(res.artifacts),
 	);
 
 	Log.info(
diff --git a/packages/lambda/src/functions/helpers/create-post-render-data.ts b/packages/lambda/src/functions/helpers/create-post-render-data.ts
index f5a550063ed..c2725428ccb 100644
--- a/packages/lambda/src/functions/helpers/create-post-render-data.ts
+++ b/packages/lambda/src/functions/helpers/create-post-render-data.ts
@@ -103,6 +103,6 @@ export const createPostRenderData = ({
 		deleteAfter: renderMetadata.deleteAfter,
 		estimatedBillingDurationInMilliseconds,
 		timeToCombine: timeToCombine ?? null,
-		artifactProgress: overallProgress.receivedAssets,
+		artifactProgress: overallProgress.receivedArtifact,
 	};
 };
diff --git a/packages/lambda/src/functions/helpers/get-progress.ts b/packages/lambda/src/functions/helpers/get-progress.ts
index 0a77693adc6..e22af635f2d 100644
--- a/packages/lambda/src/functions/helpers/get-progress.ts
+++ b/packages/lambda/src/functions/helpers/get-progress.ts
@@ -110,7 +110,7 @@ export const getProgress = async ({
 			compositionValidated: overallProgress.compositionValidated,
 			functionLaunched: overallProgress.functionLaunched,
 			serveUrlOpened: overallProgress.serveUrlOpened,
-			artifactProgress: overallProgress.receivedAssets,
+			artifacts: overallProgress.receivedArtifact,
 		};
 	}
 
@@ -258,6 +258,6 @@ export const getProgress = async ({
 		compositionValidated: overallProgress.compositionValidated,
 		functionLaunched: overallProgress.functionLaunched,
 		serveUrlOpened: overallProgress.serveUrlOpened,
-		artifactProgress: overallProgress.receivedAssets,
+		artifacts: overallProgress.receivedArtifact,
 	};
 };
diff --git a/packages/lambda/src/functions/helpers/overall-render-progress.ts b/packages/lambda/src/functions/helpers/overall-render-progress.ts
index 710ad3c7b44..9d1f821331f 100644
--- a/packages/lambda/src/functions/helpers/overall-render-progress.ts
+++ b/packages/lambda/src/functions/helpers/overall-render-progress.ts
@@ -26,10 +26,10 @@ export type OverallRenderProgress = {
 	functionLaunched: number;
 	serveUrlOpened: number | null;
 	compositionValidated: number | null;
-	receivedAssets: ReceivedAsset[];
+	receivedArtifact: ReceivedArtifact[];
 };
 
-export type ReceivedAsset = {
+export type ReceivedArtifact = {
 	filename: string;
 	sizeInBytes: number;
 	s3Url: string;
@@ -63,8 +63,8 @@ export type OverallProgressHelper = {
 	get: () => OverallRenderProgress;
 	setServeUrlOpened: (timestamp: number) => void;
 	setCompositionValidated: (timestamp: number) => void;
-	addReceivedAsset: (asset: ReceivedAsset) => void;
-	getReceivedAssets: () => ReceivedAsset[];
+	addReceivedArtifact: (asset: ReceivedArtifact) => void;
+	getReceivedArtifacts: () => ReceivedArtifact[];
 };
 
 export const makeInitialOverallRenderProgress = (
@@ -88,7 +88,7 @@ export const makeInitialOverallRenderProgress = (
 		functionLaunched: Date.now(),
 		serveUrlOpened: null,
 		compositionValidated: null,
-		receivedAssets: [],
+		receivedArtifact: [],
 	};
 };
 
@@ -285,12 +285,12 @@ export const makeOverallRenderProgress = ({
 			renderProgress.retries.push(retry);
 			upload();
 		},
-		addReceivedAsset(asset) {
-			renderProgress.receivedAssets.push(asset);
+		addReceivedArtifact(asset) {
+			renderProgress.receivedArtifact.push(asset);
 			upload();
 		},
-		getReceivedAssets() {
-			return renderProgress.receivedAssets;
+		getReceivedArtifacts() {
+			return renderProgress.receivedArtifact;
 		},
 		get: () => renderProgress,
 	};
diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts
index 9c15005c11c..4b4f2bde321 100644
--- a/packages/lambda/src/functions/launch.ts
+++ b/packages/lambda/src/functions/launch.ts
@@ -359,7 +359,7 @@ const innerLaunchHandler = async ({
 	const onArtifact = (artifact: EmittedArtifact): {alreadyExisted: boolean} => {
 		if (
 			overallProgress
-				.getReceivedAssets()
+				.getReceivedArtifacts()
 				.find((a) => a.filename === artifact.filename)
 		) {
 			return {alreadyExisted: true};
@@ -367,7 +367,7 @@ const innerLaunchHandler = async ({
 
 		const region = getCurrentRegionInFunction();
 		const s3Key = artifactName(renderMetadata.renderId, artifact.filename);
-		overallProgress.addReceivedAsset({
+		overallProgress.addReceivedArtifact({
 			filename: artifact.filename,
 			sizeInBytes: artifact.content.length,
 			s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`,
diff --git a/packages/lambda/src/functions/still.ts b/packages/lambda/src/functions/still.ts
index 49219e0a702..7c2e9f5abbf 100644
--- a/packages/lambda/src/functions/still.ts
+++ b/packages/lambda/src/functions/still.ts
@@ -40,7 +40,7 @@ import {getCurrentRegionInFunction} from './helpers/get-current-region';
 import {getOutputUrlFromMetadata} from './helpers/get-output-url-from-metadata';
 import {lambdaWriteFile} from './helpers/io';
 import {onDownloadsHelper} from './helpers/on-downloads-logger';
-import type {ReceivedAsset} from './helpers/overall-render-progress';
+import type {ReceivedArtifact} from './helpers/overall-render-progress';
 import {makeInitialOverallRenderProgress} from './helpers/overall-render-progress';
 import {validateComposition} from './helpers/validate-composition';
 import type {OnStream} from './streaming/streaming';
@@ -196,7 +196,7 @@ const innerStillHandler = async ({
 		throw new Error('Should not download a browser in Lambda');
 	};
 
-	const receivedAssets: ReceivedAsset[] = [];
+	const receivedArtifact: ReceivedArtifact[] = [];
 
 	const {key, renderBucketName, customCredentials} = getExpectedOutName(
 		renderMetadata,
@@ -205,12 +205,12 @@ const innerStillHandler = async ({
 	);
 
 	const onArtifact = (artifact: EmittedArtifact): {alreadyExisted: boolean} => {
-		if (receivedAssets.find((a) => a.filename === artifact.filename)) {
+		if (receivedArtifact.find((a) => a.filename === artifact.filename)) {
 			return {alreadyExisted: true};
 		}
 
 		const s3Key = artifactName(renderMetadata.renderId, artifact.filename);
-		receivedAssets.push({
+		receivedArtifact.push({
 			filename: artifact.filename,
 			sizeInBytes: artifact.content.length,
 			s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`,
@@ -334,7 +334,7 @@ const innerStillHandler = async ({
 		estimatedPrice: formatCostsInfo(estimatedPrice),
 		renderId,
 		outKey,
-		receivedAssets,
+		receivedArtifacts: receivedArtifact,
 	};
 
 	onStream({
@@ -352,7 +352,7 @@ export type RenderStillLambdaResponsePayload = {
 	sizeInBytes: number;
 	estimatedPrice: CostsInfo;
 	renderId: string;
-	receivedAssets: ReceivedAsset[];
+	receivedArtifacts: ReceivedArtifact[];
 };
 
 export const stillHandler = async (
diff --git a/packages/lambda/src/shared/constants.ts b/packages/lambda/src/shared/constants.ts
index 6418b7b2979..ec4ad1d54a4 100644
--- a/packages/lambda/src/shared/constants.ts
+++ b/packages/lambda/src/shared/constants.ts
@@ -14,7 +14,7 @@ import type {
 import type {BrowserSafeApis} from '@remotion/renderer/client';
 import type {ChunkRetry} from '../functions/helpers/get-retry-stats';
 import type {DeleteAfter} from '../functions/helpers/lifecycle';
-import type {ReceivedAsset} from '../functions/helpers/overall-render-progress';
+import type {ReceivedArtifact} from '../functions/helpers/overall-render-progress';
 import type {EnhancedErrorInfo} from '../functions/helpers/write-lambda-error';
 import type {AwsRegion} from '../pricing/aws-regions';
 import type {
@@ -415,7 +415,7 @@ export type PostRenderData = {
 	estimatedBillingDurationInMilliseconds: number;
 	deleteAfter: DeleteAfter | null;
 	timeToCombine: number | null;
-	artifactProgress: ReceivedAsset[];
+	artifactProgress: ReceivedArtifact[];
 };
 
 export type CostsInfo = {
@@ -471,7 +471,7 @@ export type RenderProgress = {
 	functionLaunched: number;
 	serveUrlOpened: number | null;
 	compositionValidated: number | null;
-	artifactProgress: ReceivedAsset[];
+	artifacts: ReceivedArtifact[];
 };
 
 export type Privacy = 'public' | 'private' | 'no-acl';
diff --git a/packages/studio-shared/src/index.ts b/packages/studio-shared/src/index.ts
index 7e66eac1070..3301c871636 100644
--- a/packages/studio-shared/src/index.ts
+++ b/packages/studio-shared/src/index.ts
@@ -54,7 +54,7 @@ export {
 	CopyingState,
 	DownloadProgress,
 	JobProgressCallback,
-	ReceivedAsset,
+	ReceivedArtifact as ReceivedAsset,
 	RenderJob,
 	RenderJobWithCleanup,
 	RenderingProgressInput,
diff --git a/packages/studio-shared/src/render-job.ts b/packages/studio-shared/src/render-job.ts
index 86eefd9e7e2..a849a9547c4 100644
--- a/packages/studio-shared/src/render-job.ts
+++ b/packages/studio-shared/src/render-job.ts
@@ -59,14 +59,14 @@ export type AggregateRenderProgress = {
 	artifactState: ArtifactProgress;
 };
 
-export type ReceivedAsset = {
+export type ReceivedArtifact = {
 	filename: string;
 	absoluteOutputDestination: string;
 	sizeInBytes: number;
 };
 
 export type ArtifactProgress = {
-	received: ReceivedAsset[];
+	received: ReceivedArtifact[];
 };
 
 export type JobProgressCallback = (

From 32a5c6e84b971e9c1b91eabed6b17a5361bc7415 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 11:44:58 +0200
Subject: [PATCH 23/36] document onArtifact

---
 packages/docs/docs/renderer/render-frames.mdx | 4 ++++
 packages/docs/docs/renderer/render-media.mdx  | 4 ++++
 packages/docs/docs/renderer/render-still.mdx  | 4 ++++
 3 files changed, 12 insertions(+)

diff --git a/packages/docs/docs/renderer/render-frames.mdx b/packages/docs/docs/renderer/render-frames.mdx
index e365c82fc3a..c00b4e1ae6f 100644
--- a/packages/docs/docs/renderer/render-frames.mdx
+++ b/packages/docs/docs/renderer/render-frames.mdx
@@ -129,6 +129,10 @@ Disables audio output. This option may only be set in combination with a video c
 
 <Options id="log" />
 
+### `onArtifact?`<AvailableFrom v="4.0.176" />
+
+[Handle an artifact](/docs/artifacts#using-rendermedia-renderstill-or-renderframes) that was emitted by the [`<Artifact>`](/docs/artifact) component.
+
 ### `puppeteerInstance?`
 
 _optional_
diff --git a/packages/docs/docs/renderer/render-media.mdx b/packages/docs/docs/renderer/render-media.mdx
index 4e97b32fa10..8c9d73f2d6c 100644
--- a/packages/docs/docs/renderer/render-media.mdx
+++ b/packages/docs/docs/renderer/render-media.mdx
@@ -102,6 +102,10 @@ A `number` specifying how many render processes should be started in parallel, a
 
 <Options id="log" />
 
+### `onArtifact?`<AvailableFrom v="4.0.176" />
+
+[Handle an artifact](/docs/artifacts#using-rendermedia-renderstill-or-renderframes) that was emitted by the [`<Artifact>`](/docs/artifact) component.
+
 ### `audioCodec?`
 
 _"pcm-16" | "aac" | "mp3" | "opus", available from v3.3.41_
diff --git a/packages/docs/docs/renderer/render-still.mdx b/packages/docs/docs/renderer/render-still.mdx
index 6ebd37f7a82..13cfbfb1e84 100644
--- a/packages/docs/docs/renderer/render-still.mdx
+++ b/packages/docs/docs/renderer/render-still.mdx
@@ -127,6 +127,10 @@ An object containing key-value pairs of environment variables which will be inje
 
 <Options id="log" />
 
+### `onArtifact?`<AvailableFrom v="4.0.176" />
+
+[Handle an artifact](/docs/artifacts#using-rendermedia-renderstill-or-renderframes) that was emitted by the [`<Artifact>`](/docs/artifact) component.
+
 ### `overwrite?`
 
 _optional - default `true`_

From cd3da52774859ba8dd267e482754bc50d4787db5 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 11:45:18 +0200
Subject: [PATCH 24/36] Update artifact.mdx

---
 packages/docs/docs/artifact.mdx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/docs/docs/artifact.mdx b/packages/docs/docs/artifact.mdx
index 4193d3e0e4a..b0cf31c288b 100644
--- a/packages/docs/docs/artifact.mdx
+++ b/packages/docs/docs/artifact.mdx
@@ -6,7 +6,7 @@ crumb: API
 
 # `<Artifact>`<AvailableFrom v="4.0.176"/>
 
-By rendering an `<Artifact>` tag in your Remotion markup, [an extra file will get emitted during rendering](/docs/emitting-artifacts).
+By rendering an `<Artifact>` tag in your Remotion markup, [an extra file will get emitted during rendering](/docs/artifacts).
 
 ```tsx twoslash title="MyComp.tsx"
 import { Artifact, useCurrentFrame } from "remotion";

From 1e46e919bbd88a7507ff613df37acd0ec52a401f Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 11:54:53 +0200
Subject: [PATCH 25/36] upgrade caniuse

---
 package.json   |  2 +-
 pnpm-lock.yaml | 22 +++++++++++-----------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/package.json b/package.json
index de61ae7f9f5..452aad332c0 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
   },
   "pnpm": {
     "overrides": {
-      "caniuse-lite": "1.0.30001577"
+      "caniuse-lite": "1.0.30001636"
     }
   },
   "devDependencies": {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7369b1cebd6..0f5c3899877 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -5,7 +5,7 @@ settings:
   excludeLinksFromLockfile: false
 
 overrides:
-  caniuse-lite: 1.0.30001577
+  caniuse-lite: 1.0.30001636
 
 importers:
 
@@ -11573,7 +11573,7 @@ packages:
       postcss: ^8.1.0
     dependencies:
       browserslist: 4.21.5
-      caniuse-lite: 1.0.30001577
+      caniuse-lite: 1.0.30001636
       fraction.js: 4.2.0
       normalize-range: 0.1.2
       picocolors: 1.0.0
@@ -11589,7 +11589,7 @@ packages:
       postcss: ^8.1.0
     dependencies:
       browserslist: 4.21.5
-      caniuse-lite: 1.0.30001577
+      caniuse-lite: 1.0.30001636
       fraction.js: 4.2.0
       normalize-range: 0.1.2
       picocolors: 1.0.0
@@ -11928,7 +11928,7 @@ packages:
     engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
     hasBin: true
     dependencies:
-      caniuse-lite: 1.0.30001577
+      caniuse-lite: 1.0.30001636
       electron-to-chromium: 1.4.397
       escalade: 3.1.1
       node-releases: 1.1.77
@@ -11939,7 +11939,7 @@ packages:
     engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
     hasBin: true
     dependencies:
-      caniuse-lite: 1.0.30001577
+      caniuse-lite: 1.0.30001636
       electron-to-chromium: 1.4.397
       node-releases: 2.0.10
       update-browserslist-db: 1.0.11(browserslist@4.21.5)
@@ -11949,7 +11949,7 @@ packages:
     engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
     hasBin: true
     dependencies:
-      caniuse-lite: 1.0.30001577
+      caniuse-lite: 1.0.30001636
       electron-to-chromium: 1.4.640
       node-releases: 2.0.14
       update-browserslist-db: 1.0.13(browserslist@4.22.2)
@@ -12104,13 +12104,13 @@ packages:
     resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==}
     dependencies:
       browserslist: 4.21.5
-      caniuse-lite: 1.0.30001577
+      caniuse-lite: 1.0.30001636
       lodash.memoize: 4.1.2
       lodash.uniq: 4.5.0
     dev: false
 
-  /caniuse-lite@1.0.30001577:
-    resolution: {integrity: sha512-rs2ZygrG1PNXMfmncM0B5H1hndY5ZCC9b5TkFaVNfZ+AUlyqcMyVIQtc3fsezi0NUCk5XZfDf9WS6WxMxnfdrg==}
+  /caniuse-lite@1.0.30001636:
+    resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==}
 
   /canvaskit-wasm@0.39.1:
     resolution: {integrity: sha512-Gy3lCmhUdKq+8bvDrs9t8+qf7RvcjuQn+we7vTVVyqgOVO1UVfHpsnBxkTZw+R4ApEJ3D5fKySl9TU11hmjl/A==}
@@ -14012,7 +14012,7 @@ packages:
       '@mdn/browser-compat-data': 5.5.19
       ast-metadata-inferer: 0.8.0
       browserslist: 4.22.2
-      caniuse-lite: 1.0.30001577
+      caniuse-lite: 1.0.30001636
       eslint: 8.56.0
       find-up: 5.0.0
       lodash.memoize: 4.1.2
@@ -17874,7 +17874,7 @@ packages:
       '@next/env': 14.1.4
       '@swc/helpers': 0.5.2
       busboy: 1.6.0
-      caniuse-lite: 1.0.30001577
+      caniuse-lite: 1.0.30001636
       graceful-fs: 4.2.11
       postcss: 8.4.31
       react: 18.3.1

From 74a97cf2f754bb4504c8f8229c0a0686d90dda26 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 11:55:04 +0200
Subject: [PATCH 26/36] serialize right in browser

---
 packages/core/src/Artifact.tsx                | 26 ++++++++++++++-----
 packages/core/src/CompositionManager.tsx      |  1 +
 .../functions/helpers/serialize-artifact.ts   |  8 +++---
 packages/renderer/src/collect-assets.ts       | 17 +++++++-----
 4 files changed, 35 insertions(+), 17 deletions(-)

diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx
index 28f9fe04461..74c6dfc6c30 100644
--- a/packages/core/src/Artifact.tsx
+++ b/packages/core/src/Artifact.tsx
@@ -24,13 +24,25 @@ export const Artifact: React.FC<{
 			return;
 		}
 
-		registerRenderAsset({
-			type: 'artifact',
-			id,
-			content,
-			filename,
-			frame,
-		});
+		if (typeof content === 'string') {
+			registerRenderAsset({
+				type: 'artifact',
+				id,
+				content,
+				filename,
+				frame,
+				binary: false,
+			});
+		} else {
+			registerRenderAsset({
+				type: 'artifact',
+				id,
+				content: btoa(new TextDecoder('utf8').decode(content)),
+				filename,
+				frame,
+				binary: true,
+			});
+		}
 
 		return () => {
 			return unregisterRenderAsset(id);
diff --git a/packages/core/src/CompositionManager.tsx b/packages/core/src/CompositionManager.tsx
index 728851aa192..24ebe63ebb9 100644
--- a/packages/core/src/CompositionManager.tsx
+++ b/packages/core/src/CompositionManager.tsx
@@ -145,6 +145,7 @@ export type ArtifactAsset = {
 	filename: string;
 	content: string | Uint8Array;
 	frame: number;
+	binary: boolean;
 };
 
 export type TRenderAsset = AudioOrVideoAsset | ArtifactAsset;
diff --git a/packages/lambda/src/functions/helpers/serialize-artifact.ts b/packages/lambda/src/functions/helpers/serialize-artifact.ts
index f65a856928c..50e26c89983 100644
--- a/packages/lambda/src/functions/helpers/serialize-artifact.ts
+++ b/packages/lambda/src/functions/helpers/serialize-artifact.ts
@@ -11,8 +11,9 @@ export const deserializeArtifact = (
 	serializedArtifact: SerializedArtifact,
 ): EmittedArtifact => {
 	if (serializedArtifact.binary) {
-		const encoder = new TextEncoder();
-		const content = encoder.encode(atob(serializedArtifact.stringContent));
+		const content = new TextEncoder().encode(
+			atob(serializedArtifact.stringContent),
+		);
 
 		return {
 			filename: serializedArtifact.filename,
@@ -32,8 +33,7 @@ export const serializeArtifact = (
 	artifact: EmittedArtifact,
 ): SerializedArtifact => {
 	if (artifact.content instanceof Uint8Array) {
-		const decoder = new TextDecoder('utf8');
-		const b64encoded = btoa(decoder.decode(artifact.content));
+		const b64encoded = btoa(new TextDecoder('utf8').decode(artifact.content));
 		return {
 			filename: artifact.filename,
 			stringContent: b64encoded,
diff --git a/packages/renderer/src/collect-assets.ts b/packages/renderer/src/collect-assets.ts
index 334d33df73e..279eba2f6ed 100644
--- a/packages/renderer/src/collect-assets.ts
+++ b/packages/renderer/src/collect-assets.ts
@@ -1,4 +1,4 @@
-import type {TRenderAsset} from 'remotion/no-react';
+import type {ArtifactAsset, TRenderAsset} from 'remotion/no-react';
 import type {Page} from './browser/BrowserPage';
 import {puppeteerEvaluateWithCatch} from './puppeteer-evaluate';
 
@@ -26,15 +26,20 @@ export const collectAssets = async ({
 			return asset;
 		}
 
-		const stringOrUintArray =
-			typeof asset.content === 'string'
-				? asset.content
-				: new Uint8Array(Object.values(asset.content));
+		if (typeof asset.content !== 'string') {
+			throw new Error(
+				`Expected string content for artifact ${asset.id}, but got ${asset.content}`,
+			);
+		}
+
+		const stringOrUintArray = asset.binary
+			? new TextEncoder().encode(atob(asset.content as string))
+			: asset.content;
 
 		return {
 			...asset,
 			content: stringOrUintArray,
-		};
+		} as ArtifactAsset;
 	});
 
 	return fixedArtifacts;

From 5d734ca2b405329860e1ba6f44f34ffdbdc19d02 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 12:02:29 +0200
Subject: [PATCH 27/36] emit to out/[composition-id]/artifact

---
 packages/cli/src/on-artifact.ts          | 37 ++++++++++++++++++------
 packages/cli/src/progress-bar.ts         |  6 ++--
 packages/cli/src/render-flows/render.ts  | 14 +++++----
 packages/cli/src/render-flows/still.ts   | 16 ++++++----
 packages/studio-shared/src/render-job.ts |  2 ++
 5 files changed, 52 insertions(+), 23 deletions(-)

diff --git a/packages/cli/src/on-artifact.ts b/packages/cli/src/on-artifact.ts
index 595d3d946fb..0ed587898e7 100644
--- a/packages/cli/src/on-artifact.ts
+++ b/packages/cli/src/on-artifact.ts
@@ -1,26 +1,45 @@
 import type {OnArtifact} from '@remotion/renderer';
 import type {ArtifactProgress} from '@remotion/studio-shared';
-import {writeFileSync} from 'fs';
+import {existsSync, mkdirSync, writeFileSync} from 'fs';
 import path from 'path';
 
-export const handleOnArtifact = (
-	artifactState: ArtifactProgress,
-	onProgress: (artifact: ArtifactProgress) => void,
-) => {
+export const handleOnArtifact = ({
+	artifactState,
+	onProgress,
+	compositionId,
+}: {
+	artifactState: ArtifactProgress;
+	onProgress: (artifact: ArtifactProgress) => void;
+	compositionId: string;
+}) => {
 	const initialProgress = {...artifactState};
 
 	const onArtifact: OnArtifact = (artifact) => {
-		const absoluteOutputDestination = path.join(
-			process.cwd(),
+		// It would be nice in the future to customize the artifact output destination
+
+		const relativeOutputDestination = path.join(
+			'out',
+			compositionId,
 			artifact.filename.replace('/', path.sep),
 		);
+		const defaultOutName = path.join(process.cwd(), relativeOutputDestination);
+
+		if (!existsSync(path.dirname(defaultOutName))) {
+			mkdirSync(path.dirname(defaultOutName), {
+				recursive: true,
+			});
+		}
+
+		const alreadyExisted = existsSync(defaultOutName);
 
-		writeFileSync(artifact.filename, artifact.content);
+		writeFileSync(defaultOutName, artifact.content);
 
 		initialProgress.received.push({
-			absoluteOutputDestination,
+			absoluteOutputDestination: defaultOutName,
 			filename: artifact.filename,
 			sizeInBytes: artifact.content.length,
+			alreadyExisted,
+			relativeOutputDestination,
 		});
 	};
 
diff --git a/packages/cli/src/progress-bar.ts b/packages/cli/src/progress-bar.ts
index 20cc2ca1b18..8a15af1fcc5 100644
--- a/packages/cli/src/progress-bar.ts
+++ b/packages/cli/src/progress-bar.ts
@@ -202,12 +202,12 @@ const makeArtifactProgress = (artifactState: ArtifactProgress) => {
 	return received
 		.map((artifact) => {
 			return [
-				chalk.blue('+'.padEnd(LABEL_WIDTH)),
+				chalk.blue((artifact.alreadyExisted ? '○' : '+').padEnd(LABEL_WIDTH)),
 				chalk.blue(
 					makeHyperlink({
 						url: 'file://' + artifact.absoluteOutputDestination,
-						fallback: artifact.filename,
-						text: artifact.filename,
+						fallback: artifact.absoluteOutputDestination,
+						text: artifact.relativeOutputDestination,
 					}),
 				),
 				chalk.gray(`${formatBytes(artifact.sizeInBytes)}`),
diff --git a/packages/cli/src/render-flows/render.ts b/packages/cli/src/render-flows/render.ts
index 89983aeb578..b657c9e2602 100644
--- a/packages/cli/src/render-flows/render.ts
+++ b/packages/cli/src/render-flows/render.ts
@@ -250,11 +250,6 @@ export const renderVideoFlow = async ({
 		}
 	};
 
-	const {onArtifact} = handleOnArtifact(artifactState, (progress) => {
-		artifactState = progress;
-		updateRenderProgress({newline: false, printToConsole: true});
-	});
-
 	const {urlOrBundle, cleanup: cleanupBundle} = await bundleOnCliOrTakeServeUrl(
 		{
 			fullPath: fullEntryPoint,
@@ -332,6 +327,15 @@ export const renderVideoFlow = async ({
 			onBrowserDownload,
 		});
 
+	const {onArtifact} = handleOnArtifact({
+		artifactState,
+		onProgress: (progress) => {
+			artifactState = progress;
+			updateRenderProgress({newline: false, printToConsole: true});
+		},
+		compositionId,
+	});
+
 	const {value: codec, source: codecReason} =
 		BrowserSafeApis.options.videoCodecOption.getValue(
 			{
diff --git a/packages/cli/src/render-flows/still.ts b/packages/cli/src/render-flows/still.ts
index 8ffa426fa50..f538dc667f9 100644
--- a/packages/cli/src/render-flows/still.ts
+++ b/packages/cli/src/render-flows/still.ts
@@ -318,13 +318,17 @@ export const renderStillFlow = async ({
 		isUsingParallelEncoding: false,
 	});
 
-	const {onArtifact} = handleOnArtifact(aggregate.artifactState, (progress) => {
-		aggregate.artifactState = progress;
+	const {onArtifact} = handleOnArtifact({
+		artifactState: aggregate.artifactState,
+		compositionId,
+		onProgress: (progress) => {
+			aggregate.artifactState = progress;
 
-		updateRenderProgress({
-			newline: false,
-			printToConsole: true,
-		});
+			updateRenderProgress({
+				newline: false,
+				printToConsole: true,
+			});
+		},
 	});
 
 	await RenderInternals.internalRenderStill({
diff --git a/packages/studio-shared/src/render-job.ts b/packages/studio-shared/src/render-job.ts
index a849a9547c4..d22740d133a 100644
--- a/packages/studio-shared/src/render-job.ts
+++ b/packages/studio-shared/src/render-job.ts
@@ -62,7 +62,9 @@ export type AggregateRenderProgress = {
 export type ReceivedArtifact = {
 	filename: string;
 	absoluteOutputDestination: string;
+	relativeOutputDestination: string;
 	sizeInBytes: number;
+	alreadyExisted: boolean;
 };
 
 export type ArtifactProgress = {

From ed893cdcc2d5ab3c0274d801560d7a1fa9f6d32d Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 13:46:00 +0200
Subject: [PATCH 28/36] better error handling on stills

---
 packages/example/runlambda.sh                 |  8 --
 .../lambda/src/api/render-still-on-lambda.ts  |  6 +-
 packages/lambda/src/functions/index.ts        | 79 ++++++++++---------
 packages/lambda/src/functions/still.ts        | 61 ++++++++------
 packages/renderer/src/seek-to-frame.ts        |  2 +-
 5 files changed, 85 insertions(+), 71 deletions(-)

diff --git a/packages/example/runlambda.sh b/packages/example/runlambda.sh
index 0c9bd57010b..cc4b70f427b 100644
--- a/packages/example/runlambda.sh
+++ b/packages/example/runlambda.sh
@@ -1,10 +1,2 @@
-set -e
-cd ..
-cd lambda
-npm run buildlambda
-cd ..
-cd example
-bunx remotion lambda functions rmall -f
-bunx remotion lambda functions deploy --memory=3000 --disk=10000
 bunx remotion lambda sites create --site-name=testbed-v6 --log=verbose --enable-folder-expiry
 bunx remotion lambda still testbed-v6 subtitle --log=verbose --delete-after="1-day" out-there.png
diff --git a/packages/lambda/src/api/render-still-on-lambda.ts b/packages/lambda/src/api/render-still-on-lambda.ts
index 73326f45ebd..fe00d4cfd51 100644
--- a/packages/lambda/src/api/render-still-on-lambda.ts
+++ b/packages/lambda/src/api/render-still-on-lambda.ts
@@ -102,12 +102,16 @@ const internalRenderStillOnLambda = async (
 							});
 						}
 
+						if (message.type === 'error-occurred') {
+							reject(new Error(message.payload.error));
+						}
+
 						if (message.type === 'still-rendered') {
 							resolve(message.payload);
 						}
 					},
 					timeoutInTest: 120000,
-					retriesRemaining: 1,
+					retriesRemaining: input.maxRetries,
 				})
 					.then(() => {
 						reject(new Error('Expected response to be streamed'));
diff --git a/packages/lambda/src/functions/index.ts b/packages/lambda/src/functions/index.ts
index 0923b3e130e..3fccf215c14 100644
--- a/packages/lambda/src/functions/index.ts
+++ b/packages/lambda/src/functions/index.ts
@@ -69,47 +69,50 @@ const innerHandler = async ({
 			params.logLevel,
 		);
 
-		await new Promise((resolve, reject) => {
-			const onStream = (payload: StreamingPayload) => {
-				const message = makeStreamPayload({
-					message: payload,
-				});
-				return new Promise<void>((innerResolve, innerReject) => {
-					responseWriter
-						.write(message)
-						.then(() => {
-							innerResolve();
-						})
-						.catch((err) => {
-							reject(err);
-							innerReject(err);
-						});
-				});
-			};
+		try {
+			await new Promise((resolve, reject) => {
+				const onStream = (payload: StreamingPayload) => {
+					const message = makeStreamPayload({
+						message: payload,
+					});
+					return new Promise<void>((innerResolve, innerReject) => {
+						responseWriter
+							.write(message)
+							.then(() => {
+								innerResolve();
+							})
+							.catch((err) => {
+								reject(err);
+								innerReject(err);
+							});
+					});
+				};
 
-			if (params.streamed) {
-				onStream({
-					type: 'render-id-determined',
-					payload: {renderId},
-				});
-			}
+				if (params.streamed) {
+					onStream({
+						type: 'render-id-determined',
+						payload: {renderId},
+					});
+				}
 
-			stillHandler({
-				expectedBucketOwner: currentUserId,
-				params,
-				renderId,
-				onStream,
-				timeoutInMilliseconds,
-			})
-				.then((r) => {
-					resolve(r);
+				stillHandler({
+					expectedBucketOwner: currentUserId,
+					params,
+					renderId,
+					onStream,
+					timeoutInMilliseconds,
 				})
-				.catch((err) => {
-					reject(err);
-				});
-		});
-
-		await responseWriter.end();
+					.then((r) => {
+						resolve(r);
+					})
+					.catch((err) => {
+						reject(err);
+					});
+			});
+			await responseWriter.end();
+		} catch (err) {
+			console.log({err});
+		}
 
 		return;
 	}
diff --git a/packages/lambda/src/functions/still.ts b/packages/lambda/src/functions/still.ts
index 7c2e9f5abbf..95f07a44ca5 100644
--- a/packages/lambda/src/functions/still.ts
+++ b/packages/lambda/src/functions/still.ts
@@ -6,13 +6,11 @@ import {NoReactInternals} from 'remotion/no-react';
 import {VERSION} from 'remotion/version';
 import {estimatePrice} from '../api/estimate-price';
 import {internalGetOrCreateBucket} from '../api/get-or-create-bucket';
-import {callLambda} from '../shared/call-lambda';
 import {cleanupSerializedInputProps} from '../shared/cleanup-serialized-input-props';
 import {decompressInputProps} from '../shared/compress-props';
 import type {
 	CostsInfo,
 	LambdaPayload,
-	LambdaPayloads,
 	RenderMetadata,
 } from '../shared/constants';
 import {
@@ -43,6 +41,7 @@ import {onDownloadsHelper} from './helpers/on-downloads-logger';
 import type {ReceivedArtifact} from './helpers/overall-render-progress';
 import {makeInitialOverallRenderProgress} from './helpers/overall-render-progress';
 import {validateComposition} from './helpers/validate-composition';
+import {getTmpDirStateIfENoSp} from './helpers/write-lambda-error';
 import type {OnStream} from './streaming/streaming';
 
 type Options = {
@@ -357,9 +356,16 @@ export type RenderStillLambdaResponsePayload = {
 
 export const stillHandler = async (
 	options: Options,
-): Promise<{
-	type: 'success';
-}> => {
+): Promise<
+	| {
+			type: 'success';
+	  }
+	| {
+			type: 'error';
+			message: string;
+			stack: string;
+	  }
+> => {
 	const {params} = options;
 
 	if (params.type !== LambdaRoutines.still) {
@@ -375,10 +381,6 @@ export const stillHandler = async (
 		const isBrowserError = isFlakyError(err as Error);
 		const willRetry = isBrowserError || params.maxRetries > 0;
 
-		if (!willRetry) {
-			throw err;
-		}
-
 		RenderInternals.Log.error(
 			{
 				indent: false,
@@ -389,21 +391,34 @@ export const stillHandler = async (
 			'Will retry.',
 		);
 
-		const retryPayload: LambdaPayloads[LambdaRoutines.still] = {
-			...params,
-			maxRetries: params.maxRetries - 1,
-			attempt: params.attempt + 1,
-		};
-
-		const res = await callLambda({
-			functionName: process.env.AWS_LAMBDA_FUNCTION_NAME as string,
-			payload: retryPayload,
-			region: getCurrentRegionInFunction(),
-			type: LambdaRoutines.still,
-			timeoutInTest: 120000,
-		});
+		if (params.streamed) {
+			await options.onStream({
+				type: 'error-occurred',
+				payload: {
+					error: (err as Error).stack as string,
+					shouldRetry: willRetry,
+					errorInfo: {
+						name: (err as Error).name as string,
+						message: (err as Error).message as string,
+						stack: (err as Error).stack as string,
+						chunk: null,
+						frame: null,
+						type: 'renderer',
+						isFatal: false,
+						tmpDir: getTmpDirStateIfENoSp((err as Error).stack as string),
+						attempt: params.attempt,
+						totalAttempts: 1 + params.maxRetries,
+						willRetry: true,
+					},
+				},
+			});
+		}
 
-		return res;
+		return {
+			type: 'error',
+			message: (err as Error).message,
+			stack: (err as Error).stack as string,
+		};
 	} finally {
 		forgetBrowserEventLoop(
 			options.params.type === LambdaRoutines.still
diff --git a/packages/renderer/src/seek-to-frame.ts b/packages/renderer/src/seek-to-frame.ts
index 1082ec7d0f3..f6d40ae8bca 100644
--- a/packages/renderer/src/seek-to-frame.ts
+++ b/packages/renderer/src/seek-to-frame.ts
@@ -113,7 +113,7 @@ export const waitForReady = ({
 						args: [],
 						frame,
 						page,
-						timeoutInMilliseconds,
+						timeoutInMilliseconds: 5000,
 					})
 						.then((res) => {
 							reject(

From 50960dffb01ef19694aabca53529244475de4798 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 13:50:26 +0200
Subject: [PATCH 29/36] only write to lambda once actually saved

---
 .../functions/helpers/write-lambda-error.ts   |  2 +-
 packages/lambda/src/functions/launch.ts       | 27 ++++++++++++++-----
 packages/lambda/src/functions/still.ts        |  3 +--
 3 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/packages/lambda/src/functions/helpers/write-lambda-error.ts b/packages/lambda/src/functions/helpers/write-lambda-error.ts
index 4f5d3a30ed5..db04989c89b 100644
--- a/packages/lambda/src/functions/helpers/write-lambda-error.ts
+++ b/packages/lambda/src/functions/helpers/write-lambda-error.ts
@@ -3,7 +3,7 @@ import {getFolderFiles} from './get-files-in-folder';
 import {errorIsOutOfSpaceError} from './is-enosp-err';
 
 export type LambdaErrorInfo = {
-	type: 'renderer' | 'browser' | 'stitcher' | 'webhook';
+	type: 'renderer' | 'browser' | 'stitcher' | 'webhook' | 'artifact';
 	message: string;
 	name: string;
 	stack: string;
diff --git a/packages/lambda/src/functions/launch.ts b/packages/lambda/src/functions/launch.ts
index 4b4f2bde321..62d73e407e9 100644
--- a/packages/lambda/src/functions/launch.ts
+++ b/packages/lambda/src/functions/launch.ts
@@ -367,12 +367,6 @@ const innerLaunchHandler = async ({
 
 		const region = getCurrentRegionInFunction();
 		const s3Key = artifactName(renderMetadata.renderId, artifact.filename);
-		overallProgress.addReceivedArtifact({
-			filename: artifact.filename,
-			sizeInBytes: artifact.content.length,
-			s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`,
-			s3Key,
-		});
 
 		const start = Date.now();
 		RenderInternals.Log.info(
@@ -394,9 +388,28 @@ const innerLaunchHandler = async ({
 					{indent: false, logLevel: params.logLevel},
 					`Wrote artifact to S3 in ${Date.now() - start}ms`,
 				);
+				overallProgress.addReceivedArtifact({
+					filename: artifact.filename,
+					sizeInBytes: artifact.content.length,
+					s3Url: `https://s3.${region}.amazonaws.com/${renderBucketName}/${s3Key}`,
+					s3Key,
+				});
 			})
 			.catch((err) => {
-				// TODO: Handle error
+				overallProgress.addErrorWithoutUpload({
+					type: 'artifact',
+					message: (err as Error).message,
+					name: (err as Error).name as string,
+					stack: (err as Error).stack as string,
+					tmpDir: null,
+					frame: artifact.frame,
+					chunk: null,
+					isFatal: false,
+					attempt: 1,
+					willRetry: false,
+					totalAttempts: 1,
+				});
+				overallProgress.upload();
 				RenderInternals.Log.error(
 					{indent: false, logLevel: params.logLevel},
 					'Failed to write artifact to S3',
diff --git a/packages/lambda/src/functions/still.ts b/packages/lambda/src/functions/still.ts
index 95f07a44ca5..49c9d7aeb41 100644
--- a/packages/lambda/src/functions/still.ts
+++ b/packages/lambda/src/functions/still.ts
@@ -238,7 +238,6 @@ const innerStillHandler = async ({
 				);
 			})
 			.catch((err) => {
-				// TODO: Handle error
 				RenderInternals.Log.error(
 					{indent: false, logLevel: lambdaParams.logLevel},
 					'Failed to write artifact to S3',
@@ -402,7 +401,7 @@ export const stillHandler = async (
 						message: (err as Error).message as string,
 						stack: (err as Error).stack as string,
 						chunk: null,
-						frame: null,
+						frame: params.frame,
 						type: 'renderer',
 						isFatal: false,
 						tmpDir: getTmpDirStateIfENoSp((err as Error).stack as string),

From a817062ac4a85332fb2becf684780db20d4b48e5 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 13:53:14 +0200
Subject: [PATCH 30/36] add docs

---
 packages/docs/docs/lambda/getrenderprogress.mdx   | 4 ++++
 packages/docs/docs/lambda/renderstillonlambda.mdx | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/packages/docs/docs/lambda/getrenderprogress.mdx b/packages/docs/docs/lambda/getrenderprogress.mdx
index 4772f48f67b..49561b65116 100644
--- a/packages/docs/docs/lambda/getrenderprogress.mdx
+++ b/packages/docs/docs/lambda/getrenderprogress.mdx
@@ -164,6 +164,10 @@ If the render is in progress, this is `null`. If the render is done, it is an ar
 - `timeInMilliseconds`: The time it took the render that chunk
 - `frameRange`: A tuple containing the first and last frame that was rendered in that chunk.
 
+### `artifacts`<AvailableFrom v="4.0.176"/>
+
+Artifacts that were created so far during the render. [See here for an example of dealing with field.](/docs/artifacts#using-rendermediaonlambda)
+
 ## See also
 
 - [Source code for this function](https://github.com/remotion-dev/remotion/blob/main/packages/lambda/src/api/get-render-progress.ts)
diff --git a/packages/docs/docs/lambda/renderstillonlambda.mdx b/packages/docs/docs/lambda/renderstillonlambda.mdx
index 2f0db3ec37a..ff0f3ae277c 100644
--- a/packages/docs/docs/lambda/renderstillonlambda.mdx
+++ b/packages/docs/docs/lambda/renderstillonlambda.mdx
@@ -292,6 +292,10 @@ _Available from v3.2.10_
 
 A link to CloudWatch (if you haven't disabled it) that you can visit to see the logs for the render.
 
+### `artifacts`<AvailableFrom v="4.0.176"/>
+
+Artifacts that were created so far during the render. [See here for an example of dealing with field.](/docs/artifacts#using-renderstillonlambda)
+
 ## See also
 
 - [Source code for this function](https://github.com/remotion-dev/remotion/blob/main/packages/lambda/src/api/render-still-on-lambda.ts)

From 495a18c47a9c9eff8a43ebe267fa8c6456e9cc0a Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 13:58:36 +0200
Subject: [PATCH 31/36] Good error handling on multiple assets

---
 packages/lambda/src/functions/helpers/stream-renderer.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/lambda/src/functions/helpers/stream-renderer.ts b/packages/lambda/src/functions/helpers/stream-renderer.ts
index ea99ff99f60..33aac979667 100644
--- a/packages/lambda/src/functions/helpers/stream-renderer.ts
+++ b/packages/lambda/src/functions/helpers/stream-renderer.ts
@@ -211,7 +211,7 @@ export const streamRendererFunctionWithRetry = async ({
 
 	if (result.type === 'error') {
 		if (!result.shouldRetry) {
-			throw result.error;
+			throw new Error(result.error);
 		}
 
 		overallProgress.addRetry({

From b09d7f0ca6300e8e280cfa60171ce5c59c86b280 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 14:08:48 +0200
Subject: [PATCH 32/36] Discard changes to packages/example/runlambda.sh

---
 packages/example/runlambda.sh | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/packages/example/runlambda.sh b/packages/example/runlambda.sh
index cc4b70f427b..43c2eb94300 100644
--- a/packages/example/runlambda.sh
+++ b/packages/example/runlambda.sh
@@ -1,2 +1,10 @@
+set -e
+cd ..
+cd lambda
+npm run buildlambda
+cd ..
+cd example
+bunx remotion lambda functions rmall -f
+bunx remotion lambda functions deploy --memory=3000 --disk=10000
 bunx remotion lambda sites create --site-name=testbed-v6 --log=verbose --enable-folder-expiry
-bunx remotion lambda still testbed-v6 subtitle --log=verbose --delete-after="1-day" out-there.png
+bunx remotion lambda render testbed-v6 OffthreadRemoteVideo --log=verbose --delete-after="1-day"

From d532bcb927f8abf0521a2ce44dba0f3dbc4d4d5a Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 14:11:32 +0200
Subject: [PATCH 33/36] update error messages

---
 packages/lambda/src/functions/helpers/stream-renderer.ts | 2 +-
 packages/renderer/src/render-frames.ts                   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/packages/lambda/src/functions/helpers/stream-renderer.ts b/packages/lambda/src/functions/helpers/stream-renderer.ts
index 33aac979667..7f39b5ee604 100644
--- a/packages/lambda/src/functions/helpers/stream-renderer.ts
+++ b/packages/lambda/src/functions/helpers/stream-renderer.ts
@@ -112,7 +112,7 @@ const streamRenderer = ({
 				if (alreadyExisted) {
 					return resolve({
 						type: 'error',
-						error: `Chunk ${payload.chunk} emitted an asset filename ${message.payload.artifact.filename} at frame ${message.payload.artifact.frame} but there is already another artifact with the same name.`,
+						error: `Chunk ${payload.chunk} emitted an asset filename ${message.payload.artifact.filename} at frame ${message.payload.artifact.frame} but there is already another artifact with the same name. https://remotion.dev/docs/artifacts`,
 						shouldRetry: false,
 					});
 				}
diff --git a/packages/renderer/src/render-frames.ts b/packages/renderer/src/render-frames.ts
index b338772df0d..384e6e0405a 100644
--- a/packages/renderer/src/render-frames.ts
+++ b/packages/renderer/src/render-frames.ts
@@ -519,7 +519,7 @@ const innerRenderFrames = async ({
 				if (artifact.filename === previousArtifact.filename) {
 					reject(
 						new Error(
-							`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. An artifact`,
+							`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. Artifacts must have unique names. https://remotion.dev/docs/artifacts`,
 						),
 					);
 					return;

From 517dea3bd51557b89c5b61a0ee3d80552cc5c3bf Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 14:13:17 +0200
Subject: [PATCH 34/36] Update render-still.ts

---
 packages/renderer/src/render-still.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/packages/renderer/src/render-still.ts b/packages/renderer/src/render-still.ts
index 14796a1ce34..c4fac575cd0 100644
--- a/packages/renderer/src/render-still.ts
+++ b/packages/renderer/src/render-still.ts
@@ -343,7 +343,7 @@ const innerRenderStill = async ({
 		for (const previousArtifact of previousArtifactAssets) {
 			if (artifact.filename === previousArtifact.filename) {
 				throw new Error(
-					`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. An artifact`,
+					`An artifact with output "${artifact.filename}" was already registered at frame ${previousArtifact.frame}, but now registered again at frame ${artifact.frame}. Artifacts must have unique names. https://remotion.dev/docs/artifacts`,
 				);
 			}
 		}

From c7ab57e76a7111aaaf685903fbf9592aeda01a99 Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 14:14:13 +0200
Subject: [PATCH 35/36] Update index.ts

---
 packages/studio-shared/src/index.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/packages/studio-shared/src/index.ts b/packages/studio-shared/src/index.ts
index 3301c871636..76d7d358be1 100644
--- a/packages/studio-shared/src/index.ts
+++ b/packages/studio-shared/src/index.ts
@@ -54,7 +54,6 @@ export {
 	CopyingState,
 	DownloadProgress,
 	JobProgressCallback,
-	ReceivedArtifact as ReceivedAsset,
 	RenderJob,
 	RenderJobWithCleanup,
 	RenderingProgressInput,

From 12c63f71e930052f3569dd809286760b2cd6d4ff Mon Sep 17 00:00:00 2001
From: Jonny Burger <jonathanburger11@gmail.com>
Date: Thu, 20 Jun 2024 14:26:07 +0200
Subject: [PATCH 36/36] Update Artifact.tsx

---
 packages/core/src/Artifact.tsx | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/packages/core/src/Artifact.tsx b/packages/core/src/Artifact.tsx
index 74c6dfc6c30..673e19fb786 100644
--- a/packages/core/src/Artifact.tsx
+++ b/packages/core/src/Artifact.tsx
@@ -24,23 +24,23 @@ export const Artifact: React.FC<{
 			return;
 		}
 
-		if (typeof content === 'string') {
+		if (content instanceof Uint8Array) {
 			registerRenderAsset({
 				type: 'artifact',
 				id,
-				content,
+				content: btoa(new TextDecoder('utf8').decode(content)),
 				filename,
 				frame,
-				binary: false,
+				binary: true,
 			});
 		} else {
 			registerRenderAsset({
 				type: 'artifact',
 				id,
-				content: btoa(new TextDecoder('utf8').decode(content)),
+				content,
 				filename,
 				frame,
-				binary: true,
+				binary: false,
 			});
 		}