mcpField
A
add-effect

add-effect

github.com/remotion-dev開発68.6k830 回インストール今日更新
リポジトリ

add-effect について

`@remotion/effects` にエフェクトを追加する開発者向けスキルです。実装からパッケージ登録、テスト、ドキュメント、デモ、プレビュー素材の準備までを案内します。

主な機能

  1. 01エフェクト実装の規約
  2. 02パッケージエントリーの登録
  3. 03ビルドとテストの手順
  4. 04ドキュメントとデモの手順
  5. 05プレビュー構成の要件

活用シーン

  1. 01`@remotion/effects` への WebGL2 エフェクト追加
  2. 02新しいエフェクトのパッケージ登録
  3. 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.ts
    • packages/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 に再びアルファを乗算する必要があります。

コメント