数値の式をコンポの各フレームでベイクする練習
2026年10月3日確認:下のスクリプトは、アクティブなコンポ内の数値の式を対象にします。
文字・シェイプ・既存キー付きの式などを含むコンポでは処理を始めず、標準機能で対象を分けて確認してください。
別名保存した1秒・30fpsの練習コンポでソリッドの回転にtime * 10;を設定し、既存キーが0個であることを確認します。
下のコードをbake-numeric.jsxとしてUTF-8で保存し、「ファイル」→「スクリプト」→「スクリプトファイルを実行」から実行します。
サンプリングはコンポ時刻0秒から最終フレームまでで、1秒の終了境界にはキーを作りません。
期待する結果は30個のキー、0秒で0度、0.5秒で5度、29/30秒で約9.667度です。
ワークエリアや選択したキーの範囲ではなく、コンポ全体を読み取ります。
すべての対象の式を評価してから書き込むため、途中の書き込みで依存する式の入力を変えながら読み取る処理を避けます。
数値1つ、2D・3Dの数値配列、色の4要素配列が対象で、最大3000フレーム・20プロパティ・合計20000サンプルまでです。
元の式の文字列は残して無効化し、キーの時間方向の補間はリニアにします。
フレーム間の曲線、空間の軌道、モーションブラーのサブフレーム評価まで元の式と一致することは保証しません。
コンポの再生範囲外では式が無効のままなので、長さを延ばす場合も再確認が必要です。
処理中の例外で一部のキーだけが残る場合があります。
Ctrl+Z/Command+Zまたは元コピーで、キー0個・式有効・元の値へ戻ることを確認してください。
式を有効にするだけでは追加したキーが残るため、元状態への復元にはなりません。
コードはJavaScriptの構文・模擬APIと保存したAPI資料で確認していますが、AE本体での適用・映像の一致・Undoの実行確認は未完了です。
| 入力・条件 | 操作・結果 | 確認する点 |
|---|---|---|
| 1秒・30fps | 0〜29/30秒 | 30個のキー |
| 既存キー・文字・ロック | 変更前に停止 | 無言で一部だけ処理しない |
| 元の式へ戻す | Undo/元コピー | 追加キーも戻す |
仕様の照合には、コミュニティで保守されているProperty API参照を参照しました。
エクスプレッションのベイク処理について解説します。
式の評価結果をキーフレームへ変換する手順を説明します。
処理時間や描画結果の変化は、練習コピーで比較してください。
標準機能でベイクする手順
- レイヤーを展開し、エクスプレッションが適用されているプロパティを選択します。
- メニューの「アニメーション」>「キーフレーム補助」>「エクスプレッションをキーフレームに変換」を実行します。複数のプロパティを同時に選択して変換することも可能です。
スクリプトで一括ベイクする場合
アクティブなコンポの数値の式を、各フレームでサンプリングする例です。
対象外の値型や既存のキーがある場合は停止します。
標準機能で対象を分けて変換してください。
(function () {
var comp = app.project && app.project.activeItem;
if (!(comp instanceof CompItem)) { alert("Open a composition."); return; }
var targets = [], plans = [], times = [], frame = comp.frameDuration, i, j;
function dimensions(type) {
if (type === PropertyValueType.OneD) { return 1; }
if (type === PropertyValueType.TwoD || type === PropertyValueType.TwoD_SPATIAL) { return 2; }
if (type === PropertyValueType.ThreeD || type === PropertyValueType.ThreeD_SPATIAL) { return 3; }
if (type === PropertyValueType.COLOR) { return 4; }
return 0;
}
function collect(group, locked) {
for (var k = 1; k <= group.numProperties; k++) {
var prop = group.property(k);
if (prop.propertyType !== PropertyType.PROPERTY) { collect(prop, locked); continue; }
if (!prop.canSetExpression || !prop.expressionEnabled) { continue; }
if (locked || prop.numKeys !== 0 || prop.expressionError || !dimensions(prop.propertyValueType)) {
throw new Error("Only unlocked numeric expressions without existing keys are supported.");
}
targets.push(prop);
}
}
try {
if (!isFinite(frame) || frame <= 0 || !isFinite(comp.duration) || comp.duration <= 0) { throw new Error("Invalid duration."); }
var count = Math.ceil(comp.duration / frame - 0.000001);
if (count < 1 || count > 3000) { throw new Error("Use 1 to 3000 frames."); }
for (i = 1; i <= comp.numLayers; i++) { collect(comp.layer(i), comp.layer(i).locked); }
if (!targets.length || targets.length > 20 || targets.length * count > 20000) { throw new Error("Too many properties or samples, or no target."); }
for (i = 0; i < count; i++) { times.push(i * frame); }
// Sample every target before writing keys, including dependent expressions.
for (i = 0; i < targets.length; i++) {
var prop = targets[i], values = [], size = dimensions(prop.propertyValueType);
for (j = 0; j < times.length; j++) {
var value = prop.valueAtTime(times[j], false), copy;
if (prop.expressionError) { throw new Error(prop.expressionError); }
if (size === 1) {
if (typeof value !== "number" || !isFinite(value)) { throw new Error("Invalid scalar sample."); }
copy = value;
} else {
if (!value || value.length !== size) { throw new Error("Invalid vector sample."); }
copy = [];
for (var n = 0; n < size; n++) {
if (typeof value[n] !== "number" || !isFinite(value[n])) { throw new Error("Invalid vector sample."); }
copy.push(value[n]);
}
}
values.push(copy);
}
plans.push({prop:prop, values:values});
}
} catch (error) { alert("Nothing changed: " + error.toString()); return; }
app.beginUndoGroup("Bake numeric expressions at comp frames");
try {
for (i = 0; i < plans.length; i++) {
var plan = plans[i]; plan.prop.setValuesAtTimes(times, plan.values);
for (j = 1; j <= plan.prop.numKeys; j++) {
plan.prop.setInterpolationTypeAtKey(j, KeyframeInterpolationType.LINEAR, KeyframeInterpolationType.LINEAR);
}
plan.prop.expressionEnabled = false; // Keep the original expression text.
}
} catch (error) { alert("Stopped: " + error.toString() + "\nUndo or reopen your practice copy."); }
finally { app.endUndoGroup(); }
})();
After Effectsのほかの操作は、まとめからご覧いただけます。

旧実装の参考コードは、GitHubのMOTOKI-LLC/cg-method-codeにもまとめています。
エクスプレッションの書き方や、スクリプト化に関する記事は、ほかにもあります。




