Skip to main content

createEffect()v4.0.479

Creates an effect factory that can be passed to the effects prop of Remotion canvas components.

Use it when you want to publish your own effect, or define a project-specific effect that works in the same effects array as @remotion/effects.

Example​

palette-map.ts
import {createEffect, type InteractivitySchema} from 'remotion'; type Rgb = readonly [number, number, number]; type PaletteMapParams = { readonly palette?: readonly string[]; readonly amount?: number; }; const DEFAULT_PALETTE = ['#111827', '#06b6d4', '#facc15'] as const; const paletteMapSchema = { palette: { type: 'array', item: { type: 'color', }, default: DEFAULT_PALETTE, newItemDefault: '#ffffff', minLength: 1, description: 'Palette', }, amount: { type: 'number', min: 0, max: 1, step: 0.01, default: 1, description: 'Amount', hiddenFromList: false, }, } as const satisfies InteractivitySchema; const parseCssColor = (color: string, target: HTMLCanvasElement): Rgb => { const canvas = target.ownerDocument.createElement('canvas'); canvas.width = 1; canvas.height = 1; const ctx = canvas.getContext('2d'); if (!ctx) { throw new Error('Could not get a 2D context'); } ctx.fillStyle = color; ctx.fillRect(0, 0, 1, 1); const data = ctx.getImageData(0, 0, 1, 1).data; return [data[0], data[1], data[2]]; }; const distanceSquared = (a: Rgb, b: Rgb): number => { return ( (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2 ); }; const findClosestColor = (color: Rgb, palette: Rgb[]): Rgb => { let closest = palette[0] ?? color; let closestDistance = distanceSquared(color, closest); for (const candidate of palette) { const distance = distanceSquared(color, candidate); if (distance < closestDistance) { closest = candidate; closestDistance = distance; } } return closest; }; export const paletteMap = createEffect<PaletteMapParams, null>({ type: 'com.example.paletteMap', label: 'paletteMap()', documentationLink: null, backend: '2d', calculateKey: ({palette = DEFAULT_PALETTE, amount = 1}) => { return `palette-map-${palette.join('-')}-${amount}`; }, setup: () => null, apply: ({source, target, width, height, params}) => { const ctx = target.getContext('2d'); if (!ctx) { throw new Error('Could not get a 2D context'); } const palette = (params.palette ?? DEFAULT_PALETTE).map((color) => parseCssColor(color, target), ); const amount = params.amount ?? 1; ctx.clearRect(0, 0, width, height); ctx.drawImage(source, 0, 0, width, height); const imageData = ctx.getImageData(0, 0, width, height); const {data} = imageData; for (let i = 0; i < data.length; i += 4) { const closest = findClosestColor([data[i], data[i + 1], data[i + 2]], palette); data[i] = data[i] + (closest[0] - data[i]) * amount; data[i + 1] = data[i + 1] + (closest[1] - data[i + 1]) * amount; data[i + 2] = data[i + 2] + (closest[2] - data[i + 2]) * amount; } ctx.putImageData(imageData, 0, 0); }, cleanup: () => undefined, schema: paletteMapSchema, validateParams: ({palette = DEFAULT_PALETTE, amount = 1}) => { if ( !Array.isArray(palette) || palette.length === 0 || !palette.every((color) => typeof color === 'string') ) { throw new TypeError('palette must be a non-empty array of CSS colors'); } if ( typeof amount !== 'number' || !Number.isFinite(amount) || amount < 0 || amount > 1 ) { throw new TypeError('amount must be a number between 0 and 1'); } }, });
Drag current effect onto a layer in the Studio

Use the returned factory in an effects array:

MyComp.tsx
import {CanvasImage, staticFile} from 'remotion'; import {paletteMap} from './palette-map'; export const MyComp: React.FC = () => { return ( <CanvasImage src={staticFile('image.png')} effects={[ paletteMap({ palette: ['#1d3557', '#f1faee', '#e63946'], amount: 0.8, }), ]} /> ); };

API​

import {createEffect} from 'remotion';

type​

A stable identifier for the effect.

Use a reverse-DNS identifier such as "com.example.paletteMap".

label​

The label shown in Remotion Studio.

A URL to the effect documentation. Pass null if no documentation page exists.

backend​

The rendering backend. Must be "2d", "webgl2" or "webgpu".

Effects are grouped by adjacent backend while rendering.

calculateKey​

Returns a stable string for a given parameter object.

The key is used to decide whether an effect instance is equivalent to a previous one.

setup​

Runs when the effect needs state for a target canvas.

Return any state needed by apply(). Return null if the effect does not need state.

apply​

Transforms the source image into the target canvas.

cleanup​

Receives the state returned by setup() when the effect chain is cleaned up.

schema​

An InteractivitySchema for the effect parameters shown in Remotion Studio.

createEffect() automatically adds a disabled boolean control to the schema.

validateParams​

Called when the returned effect factory is invoked.

Throw an error if a required parameter is missing or a value is invalid.

Apply Params​

source​

The current image to read from.

target​

The canvas to write the transformed image into.

state​

The value returned by setup().

params​

The parameter object passed to the effect factory.

width​

The width of the effect pass in pixels.

height​

The height of the effect pass in pixels.

gpuDevice​

A WebGPU device when a WebGPU effect is in the chain. Otherwise null.

flipSourceY​

For WebGL2 effects, tells whether DOM-style canvas sources should be flipped while uploading them as textures.

Returned Factory​

The return value is an EffectFactory.

If all parameters are optional, the factory can be called without arguments. If the parameter type has required fields, the factory requires an argument.

disabled?​

Every effect factory also accepts disabled.

When true, the effect is skipped. Default: false.

Compatibility​

BrowsersServersEnvironments
Chrome
Firefox
Safari
Node.js
Bun
Serverless Functions

Defining an effect does not require a browser. Applying it requires a canvas component and support for the selected 2d, webgl2 or webgpu backend. See Using WebGL and WebGPU during renders.

See also​