Skip to content

Instantly share code, notes, and snippets.

@nathanshipley
Created June 11, 2025 20:50
Show Gist options
  • Save nathanshipley/fb7ff367364f12923952cf8510558e70 to your computer and use it in GitHub Desktop.
Save nathanshipley/fb7ff367364f12923952cf8510558e70 to your computer and use it in GitHub Desktop.
Position Keyframes to Text Converter
// After Effects Script: Position Keyframes to Text Converter
// This script converts position keyframes from selected layers to text format
(function() {
// Check if a composition is active
var comp = app.project.activeItem;
if (!comp || !(comp instanceof CompItem)) {
alert("Please select a composition first.");
return;
}
// Check if any layers are selected
var selectedLayers = comp.selectedLayers;
if (selectedLayers.length === 0) {
alert("Please select at least one layer.");
return;
}
// Store the output text
var outputText = "";
// Begin undo group - this will allow undoing all operations with one undo
app.beginUndoGroup("Extract Position Keyframes to Text");
try {
// Process each selected layer
for (var i = 0; i < selectedLayers.length; i++) {
var layer = selectedLayers[i];
var position = layer.property("Transform").property("Position");
// Check if position property exists
if (!position) {
continue;
}
// Store original expression state
var originalExpression = position.expression;
var hadExpression = (originalExpression && originalExpression !== "");
// Add a dummy expression if no expression exists
if (!hadExpression) {
position.expression = "value";
}
// Convert expression to keyframes
position.selected = true;
app.executeCommand(app.findMenuCommandId("Convert Expression to Keyframes"));
// Now extract the keyframe data
var keyframeData = extractKeyframeData(layer, comp);
outputText += keyframeData;
}
} finally {
// End undo group
app.endUndoGroup();
}
// Create a window to display the results
showResultsWindow(outputText);
function extractKeyframeData(layer, comp) {
var position = layer.property("Transform").property("Position");
var data = "";
// Header information
data += "Adobe After Effects 8.0 Keyframe Data\n\n";
data += "\tUnits Per Second\t" + Math.round(comp.frameRate) + "\n";
// Get source dimensions (using layer dimensions as approximation)
var sourceWidth = layer.width || 100;
var sourceHeight = layer.height || 100;
data += "\tSource Width\t" + Math.round(sourceWidth) + "\n";
data += "\tSource Height\t" + Math.round(sourceHeight) + "\n";
data += "\tSource Pixel Aspect Ratio\t1\n";
data += "\tComp Pixel Aspect Ratio\t1\n\n";
data += "Transform\tPosition\n";
data += "\tFrame\tX pixels\tY pixels\tZ pixels\t\n";
// Get keyframe times and values
var numKeys = position.numKeys;
var fps = comp.frameRate;
var startTime = comp.workAreaStart;
var endTime = startTime + comp.workAreaDuration;
if (numKeys > 0) {
// If there are keyframes, use them
var firstKeyTime = position.keyTime(1);
var lastKeyTime = position.keyTime(numKeys);
// Sample from first key to last key
var currentTime = firstKeyTime;
var frameNum = 0;
while (currentTime <= lastKeyTime) {
var value = position.valueAtTime(currentTime, false);
var x = value[0];
var y = value[1];
var z = (value.length > 2) ? value[2] : 0;
data += "\t" + frameNum + "\t" + x + "\t" + y + "\t" + z + "\t\n";
frameNum++;
currentTime = firstKeyTime + (frameNum / fps);
}
} else {
// If no keyframes, sample the entire work area
var totalFrames = Math.floor(comp.workAreaDuration * fps);
for (var frame = 0; frame <= totalFrames; frame++) {
var time = startTime + (frame / fps);
var value = position.valueAtTime(time, false);
var x = value[0];
var y = value[1];
var z = (value.length > 2) ? value[2] : 0;
data += "\t" + frame + "\t" + x + "\t" + y + "\t" + z + "\t\n";
}
}
data += "\n\nEnd of Keyframe Data\n";
return data;
}
function showResultsWindow(text) {
// Create UI window
var dialog = new Window("dialog", "Position Keyframe Data");
dialog.orientation = "column";
dialog.alignChildren = "fill";
dialog.preferredSize.width = 600;
dialog.preferredSize.height = 500;
// Add instruction text
var instructions = dialog.add("statictext", undefined, "Copy the text below (Ctrl/Cmd+A to select all, Ctrl/Cmd+C to copy):");
// Create a panel to hold the text (prevents edittext issues)
var textPanel = dialog.add("panel");
textPanel.alignChildren = "fill";
// Add text area with the keyframe data
var textArea = textPanel.add("edittext", undefined, text, {
multiline: true,
scrolling: true,
readonly: false
});
textArea.preferredSize.height = 400;
// Add buttons
var buttonGroup = dialog.add("group");
buttonGroup.alignment = "center";
var copyInstructionBtn = buttonGroup.add("button", undefined, "Copy Instructions");
var closeBtn = buttonGroup.add("button", undefined, "Close");
// Button functionality
copyInstructionBtn.onClick = function() {
// Show copy instructions
alert("To copy all text:\n\n" +
"1. Click inside the text area\n" +
"2. Press Ctrl/Cmd + A to select all\n" +
"3. Press Ctrl/Cmd + C to copy\n\n" +
"After closing this window, you can press Ctrl/Cmd + Z to undo\n" +
"the keyframe baking and restore your original expressions.");
};
closeBtn.onClick = function() {
dialog.close();
};
// Auto-select all text when window opens
textArea.active = true;
// Show the dialog
dialog.show();
// Try to select all text after showing
textArea.textselection = [0, text.length];
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment