
Add Effect
Follow Remotion’s contributor checklist when adding a new effect to @remotion/effects with WebGL2, Studio schema, docs, and tests.
Install
npx skills add https://github.com/remotion-dev/remotion --skill add-effectWhat is this skill?
- End-to-end contributor workflow: implementation, package exports, docs, demos, preview images, skill updates, tests, for
- WebGL2 backend preferred; 2D only when WebGL cannot express the effect
- Naming conventions: kebab-case files, camelCase functions, PascalCase params, remotion/<kebab-case> type strings
- Studio integration via SequenceSchema fields and validate-effect-param / color-utils helpers
- Single-file vs folder layout rules under packages/effects/src/
Adoption & trust: 101 installs on skills.sh; 49.4k GitHub stars.
Recommended Skills
Find Skillsvercel-labs/skills
Skill Creatoranthropics/skills
Lark Skill Makerlarksuite/cli
Skills Clixixu-me/skills
Write A Skillmattpocock/skills
Using Superpowersobra/superpowers
Journey fit
Primary fit
Effect authoring is core build work on the Remotion monorepo—implementation and package surface before any launch-facing render pipeline. Frontend subphase fits React/Remotion video components, shaders, and Studio-editable params—not generic backend APIs.
SKILL.md
READMESKILL.md - Add Effect
# Add a new `@remotion/effects` effect Use this skill when adding a new effect to `@remotion/effects`. ## 1. Pick the effect shape - Prefer the WebGL2 backend for new effects. Use 2D only when WebGL cannot express the effect. - Use a single file at `packages/effects/src/<effect-name>.ts` for simple effects. - Use a folder at `packages/effects/src/<effect-name>/` plus a top-level re-export file when the effect needs multiple shaders, runtime helpers, or multiple files. - Follow naming already used by the package: - File/subpath: kebab-case (`chromatic-aberration`) - Function: camelCase (`chromaticAberration`) - Type: PascalCase params (`ChromaticAberrationParams`) - Effect type string: `remotion/<kebab-case-name>` ## 2. Implement the effect In the effect file: - Import `SequenceSchema` and `Internals` from `remotion`. - Use `const {createEffect, createWebGL2ContextError} = Internals;`. - Define defaults as `const` values. - Define a schema with `satisfies SequenceSchema`; these fields appear in Studio visual editing. - Export the params type. - Resolve defaults in a `resolve()` helper. - Validate params using helpers from: - `packages/effects/src/validate-effect-param.ts` - `packages/effects/src/color-utils.ts` - Throw `createWebGL2ContextError('<effect name> effect')` if WebGL2 cannot be acquired. - Set `documentationLink` to `https://www.remotion.dev/docs/effects/<slug>`. - Include every resolved parameter in `calculateKey()`. For WebGL2 effects, use this general structure: ```ts import type {SequenceSchema} from 'remotion'; import {Internals} from 'remotion'; import {assertOptionalFiniteNumber, validateUnitInterval} from './color-utils.js'; import {assertEffectParamsObject} from './validate-effect-param.js'; const {createEffect, createWebGL2ContextError} = Internals; const DEFAULT_AMOUNT = 1 as const; const myEffectSchema = { amount: { type: 'number', min: 0, max: 1, step: 0.01, default: DEFAULT_AMOUNT, description: 'Amount', }, } as const satisfies SequenceSchema; export type MyEffectParams = { readonly amount?: number; }; type MyEffectResolved = { amount: number; }; const resolve = (p: MyEffectParams): MyEffectResolved => ({ amount: p.amount ?? DEFAULT_AMOUNT, }); const validateMyEffectParams = (params: MyEffectParams): void => { assertEffectParamsObject(params, 'My effect'); assertOptionalFiniteNumber(params.amount, 'amount'); validateUnitInterval(params.amount ?? DEFAULT_AMOUNT, 'amount'); }; type MyEffectState = { readonly gl: WebGL2RenderingContext; readonly program: WebGLProgram; readonly vao: WebGLVertexArrayObject; readonly vbo: WebGLBuffer; readonly texture: WebGLTexture; readonly uSource: WebGLUniformLocation | null; readonly uAmount: WebGLUniformLocation | null; }; const VERTEX_SHADER = /* glsl */ `#version 300 es in vec2 aPos; in vec2 aUv; out vec2 vUv; void main() { vUv = aUv; gl_Position = vec4(aPos, 0.0, 1.0); } `; const FRAGMENT_SHADER = /* glsl */ `#version 300 es precision highp float; in vec2 vUv; out vec4 fragColor; uniform sampler2D uSource; uniform float uAmount; void main() { vec4 color = texture(uSource, vUv); fragColor = vec4(color.rgb * uAmount, color.a); } `; // Follow existing helpers in halftone.ts or a runtime file for shader // compilation, program linking, fullscreen-quad setup, and texture setup. export const myEffect = createEffect<MyEffectParams, MyEffectState>({ type: 'remotion/my-effect', label: 'My Effect', documentationLink: 'https://www.remotion.dev/docs/effects/my-effect', backend: 'webgl2', calculateKey: (params) => { const r = resolve(params); return `my-effect-${r.amount}`; }, setup: (target) => { const gl = target.getContext('webgl2', { premultipliedAlpha: true, alpha: true, preserveDrawingB