add-effect について
`@remotion/effects` にエフェクトを追加する開発者向けスキルです。実装からパッケージ登録、テスト、ドキュメント、デモ、プレビュー素材の準備までを案内します。
主な機能
- 01エフェクト実装の規約
- 02パッケージエントリーの登録
- 03ビルドとテストの手順
- 04ドキュメントとデモの手順
- 05プレビュー構成の要件
活用シーン
- 01`@remotion/effects` への WebGL2 エフェクト追加
- 02新しいエフェクトのパッケージ登録
- 03エフェクトのドキュメントとデモ作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ remotion-dev/ remotion --skill add-effectインストール後、Claude Code で/add-effectを入力して読み込まれたか確認します。
詳細
@remotion/effects に新しいエフェクトを追加する
@remotion/effects に新しいエフェクトを追加する際に、このスキルを使用してください。
1. エフェクトの構成を選ぶ
- 新しいエフェクトでは、WebGL2 バックエンドを優先してください。エフェクトを WebGL で表現できない場合に限り、2D を使用してください。
- シンプルなエフェクトには、
packages/effects/src/<effect-name>.tsに単一のファイルを使用してください。 - 複数のシェーダー、ランタイムヘルパー、または複数のファイルが必要なエフェクトには、
packages/effects/src/<effect-name>/にフォルダーを作成し、トップレベルの再エクスポートファイルを追加してください。 - パッケージですでに使われている命名規則に従ってください。
- ファイル/サブパス:ケバブケース(
chromatic-aberration) - 関数:キャメルケース(
chromaticAberration) - 型:パラメーターにはパスカルケース(
ChromaticAberrationParams) - エフェクト型文字列:
remotion/<kebab-case-name>
- ファイル/サブパス:ケバブケース(
2. エフェクトを実装する
エフェクトファイルでは、次のようにします。
remotionからSequenceSchemaとInternalsをインポートします。const {createEffect, createWebGL2ContextError} = Internals;を使用します。- デフォルト値を
const値として定義します。 satisfies SequenceSchemaを使ってスキーマを定義します。これらのフィールドは Studio のビジュアル編集に表示されます。- パラメーターの型をエクスポートします。
resolve()ヘルパーでデフォルト値を解決します。- 次のヘルパーを使ってパラメーターを検証します。
packages/effects/src/validate-effect-param.tspackages/effects/src/color-utils.ts
- WebGL2 を取得できない場合は、
createWebGL2ContextError('<effect name> effect')をスローします。 documentationLinkをhttps://www.remotion.dev/docs/effects/<slug>に設定します。- 解決済みのすべてのパラメーターを
calculateKey()に含めます。
WebGL2 エフェクトでは、次の一般的な構造を使用してください。
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,
preserveDrawingBuffer: true,
});
if (!gl) {
throw createWebGL2ContextError('my effect effect');
}
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
return createMyEffectState(gl, VERTEX_SHADER, FRAGMENT_SHADER);
},
apply: ({source, width, height, params, state, flipSourceY}) => {
const r = resolve(params);
state.gl.viewport(0, 0, width, height);
state.gl.bindFramebuffer(state.gl.FRAMEBUFFER, null);
state.gl.activeTexture(state.gl.TEXTURE0);
state.gl.bindTexture(state.gl.TEXTURE_2D, state.texture);
state.gl.pixelStorei(state.gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
state.gl.texImage2D(
state.gl.TEXTURE_2D,
0,
state.gl.RGBA,
state.gl.RGBA,
state.gl.UNSIGNED_BYTE,
source as TexImageSource,
);
state.gl.useProgram(state.program);
if (state.uSource) state.gl.uniform1i(state.uSource, 0);
if (state.uAmount) state.gl.uniform1f(state.uAmount, r.amount);
state.gl.bindVertexArray(state.vao);
state.gl.drawArrays(state.gl.TRIANGLE_STRIP, 0, 4);
},
cleanup: ({gl, program, vao, vbo, texture}) => {
gl.deleteTexture(texture);
gl.deleteBuffer(vbo);
gl.deleteProgram(program);
gl.deleteVertexArray(vao);
},
schema: myEffectSchema,
validateParams: validateMyEffectParams,
});
新しいヘルパーを追加する前に、既存の WebGL2 エフェクト(halftone.ts、blur/blur-runtime.ts、chromatic-aberration/chromatic-aberration-runtime.ts、wave/wave-runtime.ts など)を確認してください。上記のテンプレートにある createMyEffectState() は、これらのファイルで使用されているシェーダーのコンパイル、プログラムのリンク、フルスクリーンクワッド、テクスチャ、ユニフォームロケーションの設定を表します。
3. パッケージのエントリーポイントを登録する
次を更新します。
packages/effects/bundle.ts— 新しいsrc/<effect-name>.tsエントリーポイントを追加します。packages/effects/package.json:exports["./<effect-name>"]を追加します。typesVersionsのエントリーを追加します。
フォルダーを使った実装の場合は、そのフォルダーから再エクスポートするトップレベルファイルを追加します。
export {myEffect, type MyEffectParams} from './my-effect/index.js';
4. ビルドと検証を行う
既存のテストとビルドを実行します。
cd packages/effects
bun test src/test
bunx turbo make --filter="@remotion/effects"
5. ドキュメントを追加する
packages/docs/docs/effects/<effect-name>.mdx を作成します。
既存のエフェクトページに従ってください。
- フロントマター:
slug、title、sidebar_label、crumb: '@remotion/effects'。 image:はbun render-cards.tsを実行した後にのみ追加します。- H1:
# effectName()<AvailableFrom v="..." />。 _Part of the [@remotion/effects](/docs/effects/api) package._を含めます。- 短い説明を追加します。
<EffectsDemo type="effects-<effect-name>" />を追加します。title="MyComp.tsx"を指定した twoslash の例を追加します。- 各オプションをそれぞれ独立した
###見出しで説明し、オプションのパラメーターには?を付けます。 disabled?セクションを追加します。- 「関連項目」セクションを追加します。
次を更新します。
packages/docs/sidebars.ts—'effects/<effect-name>'をアルファベット順に追加します。packages/docs/docs/effects/table-of-contents.tsx— 適切なカテゴリにカードを追加します。- カードジェネレーターを実行して
packages/docs/src/data/articles.tsを更新します。手作業で変更しないでください。
ドキュメントの文面には writing-docs スキルを使用してください。
6. インタラクティブなドキュメントデモを追加する
packages/docs/components/effects/effects-<effect-name>-preview.tsx を作成します。
他のエフェクトと同じプレビューソースを使用します。
import {myEffect} from '@remotion/effects/my-effect';
import React from 'react';
import {CanvasImage} from 'remotion';
import {EFFECTS_PREVIEW_IMAGE_SRC} from './effects-preview-image';
export const EffectsMyEffectPreview: React.FC<{
readonly amount: number;
}> = ({amount}) => {
return (
<CanvasImage
src={EFFECTS_PREVIEW_IMAGE_SRC}
width={1280}
height={720}
fit="cover"
effects={[myEffect({amount})]}
/>
);
};
ドキュメントのエフェクトプレビューでは fit="cover" を使用し、共有プレビュー画像が 16:9 のキャンバスを埋め、透明な帯が残らないようにしてください。
デモを packages/docs/components/effects-demos/registry.ts に登録します。
- プレビューコンポーネントと、実際のエフェクトスキーマをインポートします(または
effect().definition.schemaから取得します)。 id: 'effects-<effect-name>'のエントリーを追加します。initialValuesは、スキーマのデフォルト値がundefinedである必須フィールドにのみ追加します。
デモの詳細については docs-demo スキルを使用してください。
7. 目次プレビューのコンポジションを追加してレンダリングする
目次カードは、手書きのアセットではなく、packages/docs にある実際の Remotion コンポジションから生成する必要があります。
プレビューアセットは必ず PNG ファイルとしてレンダリングしてください。
packages/docs/src/remotion/Root.tsx に、effect-previews フォルダーの下に Still を追加します。
<Still
id="effects-my-effect-preview"
component={EffectsMyEffectPreview}
width={1280}
height={720}
defaultProps={{
amount: 1,
}}
/>
プレビューコンポーネントの CanvasImage と同じ width および height を使います。
プレビューコンポーネントが共有のドキュメント用プレビュー画像を使う場合は、
CanvasImage の fit="cover" を維持してください。16:9 のプレビューコンポーネントを
異なるアスペクト比にレンダリングすると、生成された目次画像に黒帯が入ることがあります。
その後、packages/docs からレンダリングします。
bunx remotion still src/remotion/entry.ts effects-my-effect-preview static/img/effects-my-effect-preview.png --overwrite --image-format=png
次の両方をコミットします。
packages/docs/src/remotion/Root.tsxのコンポジションエントリーpackages/docs/static/img/内のレンダリング済み画像
8. ドキュメントカードを生成する
次を実行します。
cd packages/docs
bun render-cards.ts
生成された packages/docs/static/generated/articles-docs-effects-<effect-name>.png と、新しい image: フロントマター行をコミットします。
render-cards.ts が他の不足しているカードをついでに生成した場合、今回の変更に含まれないファイルは削除してください。
9. Remotion スキルを更新する
エージェント向けの Remotion スキルを、新しいエフェクトに合わせて更新します。
新しいエフェクトによって一般的な使用方法、インポート規約、インストール手順、またはカスタムエフェクトの推奨事項が変わる場合に限り、packages/skills/skills/remotion-markup/effects.md を更新してください。エフェクトの一覧全体をここに重複して記載しないでください。正規の一覧として、ドキュメントの目次を使用します。
10. フォーマット、ビルド、検証
次を実行します。
cd packages/effects
bunx oxfmt src --write
cd ../..
bun run build
bun run formatting
ドキュメントのソースを変更した場合は、bun run formatting によって packages/docs/src がフォーマットされます。MDX のみを編集した場合は、ドキュメントページにフォーマッターを実行しないでください。
コミットする前に、次を確認します。
git diff --check
git status --short
よくある落とし穴
package.jsonのexportsとtypesVersionsを忘れないでください。@remotion/effects/my-effectのようなサブパスインポートは、これらに依存します。bundle.tsを忘れないでください。忘れると、ESM サブパスがビルドされません。packages/docs/src/remotionに一時的なレンダリング用エントリーポイントを残さないでください。- エフェクトの目次プレビューに手書きの SVG を使わないでください。
- エフェクトが意図的にアルファ値を変更する場合を除き、アルファ値を維持してください。
- ピクセル計算では、キャンバスが乗算済みアルファを格納する点に注意してください。
- WebGL の色計算では、多くの場合、輝度やしきい値の計算前にサンプリングした RGB の乗算済みアルファを解除し、その後、出力 RGB に再びアルファを乗算する必要があります。