From 4a6945b3533aa0e1ea0256b147a5365d4953e9c6 Mon Sep 17 00:00:00 2001 From: cbaucom Date: Fri, 12 Jun 2026 14:28:10 -0400 Subject: [PATCH 1/5] Swap soundtrack to @remotion/media Audio for export compatibility Html5Audio is unsupported by @remotion/web-renderer; @remotion/media's Audio works in both the Player preview and in-browser rendering. The ducking envelope now feeds the volume prop as a pure frame callback. Co-Authored-By: Claude Fable 5 --- package.json | 1 + pnpm-lock.yaml | 52 ++++++++++++++++++++++++ src/composition/SlideshowComposition.tsx | 17 +++++--- src/composition/soundtrackVolume.ts | 12 +----- 4 files changed, 66 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index 4239106..70aad7a 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@remotion/media": "^4.0.289", "@remotion/player": "^4.0.289", "@remotion/transitions": "^4.0.289", + "@remotion/web-renderer": "4.0.475", "@tailwindcss/vite": "^4.3.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9fa2f6e..f3231c3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: '@remotion/transitions': specifier: ^4.0.289 version: 4.0.475(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@remotion/web-renderer': + specifier: 4.0.475 + version: 4.0.475(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@tailwindcss/vite': specifier: ^4.3.0 version: 4.3.0(vite@8.0.16(@types/node@24.13.1)(esbuild@0.27.7)(jiti@2.7.0)) @@ -573,6 +576,21 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@mediabunny/aac-encoder@1.45.0': + resolution: {integrity: sha512-vLQw8cY7Me6pvTTMkMhOiH9UCuINzfTOETCeDxbGNeNfDqc/7QlxloUH1Ylp/Zz2ek0O8kc6YdygV2vWAPakrA==} + peerDependencies: + mediabunny: ^1.46.0 + + '@mediabunny/flac-encoder@1.45.0': + resolution: {integrity: sha512-LfKbAMZVkxRS7PpEIVnWOY/l0KcHv+rjO7pYY3O0TPCZvbHWfrnQjn8JPacPIfuq6Yv7r4f8lhcl7yHSynoRkQ==} + peerDependencies: + mediabunny: ^1.46.0 + + '@mediabunny/mp3-encoder@1.45.0': + resolution: {integrity: sha512-Bobi6AaQYEc7TWmPJ8Q0/hcUtBN7pLUC2qjoC7oZR4FcGqGztby6k7A1SWlmswoMOEIhYsOrgDaemrSDAC0QVQ==} + peerDependencies: + mediabunny: ^1.46.0 + '@modelcontextprotocol/sdk@1.29.0': resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} engines: {node: '>=18'} @@ -1318,6 +1336,9 @@ packages: '@remotion/google-fonts@4.0.475': resolution: {integrity: sha512-YJR0foFqmO9Jkufz37X1iDiS1HjKqRrp3y7SASolz9FxO8U4Ukp+JRe+KZTAkeLkPMaqi/fXuDljqhVKUjcYRQ==} + '@remotion/licensing@4.0.475': + resolution: {integrity: sha512-ZBDKg+iRa/mVoX/EWM40MypICKokWcG4c4RKQ4QKmy9JgJssbnFSy9mikPKMi/64J3KPEfAT1hv4JRKlNdoX5g==} + '@remotion/media@4.0.475': resolution: {integrity: sha512-X4RqOqPDwmNsfig6x950TxWtfb4nUWsyPSTT/RTJiw13lEaNNesr8Ob2Bm1NWYmYQLqHmt+kZbJPF6zBt/yDyA==} peerDependencies: @@ -1345,6 +1366,12 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' + '@remotion/web-renderer@4.0.475': + resolution: {integrity: sha512-3vftJV5R95PQuH/fk9qW4/e9u5MQ13d2UgmQYIiuWtLuQLXz6k0wDywpha3E3+Hg1yRb7wvod+j3eF9gzGRFEA==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + '@rolldown/binding-android-arm64@1.0.3': resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4164,6 +4191,18 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@mediabunny/aac-encoder@1.45.0(mediabunny@1.46.0)': + dependencies: + mediabunny: 1.46.0 + + '@mediabunny/flac-encoder@1.45.0(mediabunny@1.46.0)': + dependencies: + mediabunny: 1.46.0 + + '@mediabunny/mp3-encoder@1.45.0(mediabunny@1.46.0)': + dependencies: + mediabunny: 1.46.0 + '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': dependencies: '@hono/node-server': 1.19.14(hono@4.12.25) @@ -4975,6 +5014,8 @@ snapshots: - react - react-dom + '@remotion/licensing@4.0.475': {} + '@remotion/media@4.0.475(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: mediabunny: 1.46.0 @@ -5006,6 +5047,17 @@ snapshots: react-dom: 19.2.7(react@19.2.7) remotion: 4.0.475(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@remotion/web-renderer@4.0.475(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@mediabunny/aac-encoder': 1.45.0(mediabunny@1.46.0) + '@mediabunny/flac-encoder': 1.45.0(mediabunny@1.46.0) + '@mediabunny/mp3-encoder': 1.45.0(mediabunny@1.46.0) + '@remotion/licensing': 4.0.475 + mediabunny: 1.46.0 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + remotion: 4.0.475(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@rolldown/binding-android-arm64@1.0.3': optional: true diff --git a/src/composition/SlideshowComposition.tsx b/src/composition/SlideshowComposition.tsx index f3181f7..aae4396 100644 --- a/src/composition/SlideshowComposition.tsx +++ b/src/composition/SlideshowComposition.tsx @@ -1,6 +1,6 @@ -import React from 'react' -import { AbsoluteFill, Html5Audio, Img, interpolate, useCurrentFrame } from 'remotion' -import { Video } from '@remotion/media' +import React, { useCallback } from 'react' +import { AbsoluteFill, Img, interpolate, useCurrentFrame } from 'remotion' +import { Audio, Video } from '@remotion/media' import { TransitionSeries, linearTiming } from '@remotion/transitions' import { fade } from '@remotion/transitions/fade' import type { TransitionPresentation, TransitionPresentationComponentProps } from '@remotion/transitions' @@ -9,7 +9,7 @@ import type { RenderPlanEntry, RenderPlan } from '../sequence-planner/types' import type { TransitionType } from '../timeline-core/settings' import { isTitleSlide } from '../timeline-core/types' import type { MediaSlide, TitleSlide } from '../timeline-core/types' -import { useSoundtrackVolume } from './soundtrackVolume' +import { volumeAtFrame } from './soundtrackVolume' type MediaRenderPlanEntry = Omit & { slide: MediaSlide } type TitleRenderPlanEntry = Omit & { slide: TitleSlide } @@ -88,9 +88,14 @@ export function SlideshowComposition({ plan }: SlideshowProps) { ) } +// @remotion/media's Audio (not Html5Audio) — required for renderMediaOnWeb export. function SoundtrackAudio({ track }: { track: NonNullable }) { - const volume = useSoundtrackVolume(track.duckingEnvelope) - return + const { duckingEnvelope } = track + const volume = useCallback( + (frame: number) => volumeAtFrame(duckingEnvelope, frame), + [duckingEnvelope], + ) + return