@remotion/gif: Integrate effect system with <Gif>#7480
Conversation
Adds `_experimentalEffects` support, runs the chain at intrinsic GIF dimensions, and adds a Studio testbed composition. Fixes #7477. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Important
The runEffectChain rejection handler in canvas.tsx re-throws inside .then, surfacing failures only as delayRender timeouts (and as unhandled promise rejections in dev). Worth aligning with the Solid / RemotionRiveCanvas pattern of cancelRender(err) before merge.
TL;DR — Wires @remotion/gif into the canvas effect system: <Gif> accepts _experimentalEffects, threads memoized definitions to <Sequence> for Studio controls, and runs the chain at the GIF's intrinsic pixel dimensions in canvas.tsx before compositing through the existing fit logic. Also adds a gif-effects-testbed composition mirroring RiveEffectsTestbed.
Key changes
- Effect prop and memoization on
<Gif>— adds_experimentalEffects?: EffectsProp, runsuseMemoizedEffectDefinitions/useMemoizedEffects, and forwards the definitions to<Sequence>so Studio surfaces effect controls. - Two-stage compositing in
canvas.tsx— when effects are set, draws the current GIF frame into a frame-sizedsourceCanvas, runsrunEffectChaininto a frame-sizedeffectOutputCanvas, then composites onto the layout-sized output viacalcArgs(fit, ...). The no-effects path short-circuits to the previous singledrawImage. delayRenderintegration — wraps each chain run in adelayRender('Rendering <Gif/> effect chain')handle so rendering blocks on chain completion; cleanup always releases the handle.gif-effects-testbed— new composition inpackages/exampleexercising baseline, tint, halftone, animated blur, animated wave, and a stacked tint+wave+blur chain, registered under the existing Rive folder inRoot.tsx.
Summary | 6 files | 1 commit | base: main ← gif/effects-7477
Effect chain error path swallows failures
Before:
runEffectChainrejections in<Solid>/<RemotionRiveCanvas>route throughcancelRender(err)/setError, surfacing the original error to React/the renderer.
After: In<Gif>'scanvas.tsx, the.catchhandler re-throws inside.then, producing an unhandled promise rejection and leaving thedelayRenderhandle to be released only by effect cleanup.
Practically this means that on a real chain failure (e.g. WebGL context lost, shader compile failure), the renderer will hang the frame until the delayRender timeout fires with a generic timeout message rather than the underlying error. The fix is to destructure cancelRender from useDelayRender() and call cancelRender(err) in the .catch, mirroring packages/core/src/effects/Solid.tsx.
Effects run at GIF intrinsic resolution
Before: The single
drawImagealready usedcalcArgs(fit, imageData, {width: size.width, height: size.height}), compositing at element size in one step.
After: When effects are present, the chain runs atframeSize(the decoded GIF pixel dims) and the result is upscaled tolayoutSizeviacalcArgs. The no-effects branch is preserved unchanged in behavior aside from now preferring the explicitwidth/heightprops overuseElementSizefor the layout size (a consistency fix with the canvas drawing buffer).
This is a sensible tradeoff — effects on a 480×270 GIF stay cheap regardless of composition resolution — but it does mean spatially-scaled effects (wave amplitude, halftone dotSize, blur radius) are expressed in source pixels and will be magnified by the fit upscale. Worth a brief note in the eventual docs page.
packages/gif/src/canvas.tsx · packages/example/src/Gif/GifEffects.tsx
Claude Opus | 𝕏
Move Rive, Gif, AnimatedImage, and video effect compositions out of unrelated folders into a dedicated Effects folder. Co-authored-by: Cursor <cursoragent@cursor.com>
Omit `_experimentalEffects` from inherited `SequenceProps` before re-adding it as `EffectsProp`, avoiding an intersection with `EffectDefinition[]` that broke effect factories in the example package. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Fixes react-hooks/exhaustive-deps lint failure in CI. Co-authored-by: Cursor <cursoragent@cursor.com>

Summary
_experimentalEffectsto<Gif>withuseMemoizedEffectDefinitions/useMemoizedEffects, and passes effect definitions to<Sequence>for Studio controls.canvas.tsxto runrunEffectChainat the GIF's intrinsic pixel dimensions (withdelayRender/continueRender), then composites the result onto the layout canvas with the existingfitlogic. Short-circuits to the previous draw path when no effects are set.gif-effects-testbedcomposition inpackages/example(mirrors the Rive effects testbed).Fixes #7477
Test plan
gif-effects-testbedand verify baseline, tint, halftone, blur, wave, and stacked effects render correctlygif-effects-testbedwithnpx remotion renderand compare output to previewMade with Cursor