Skip to content

Instantly share code, notes, and snippets.

@gordielachance
Created May 22, 2026 11:33
Show Gist options
  • Select an option

  • Save gordielachance/11b948223305035ad3f1735e5f84d0ea to your computer and use it in GitHub Desktop.

Select an option

Save gordielachance/11b948223305035ad3f1735e5f84d0ea to your computer and use it in GitHub Desktop.
After Effects JSX Script - Offset markers
/*
After Effects JSX Script
Offset markers V2
Author: G.Breant + Claude
*/
(function offsetMarkers() {
var comp = app.project.activeItem;
if (!comp || !(comp instanceof CompItem)) {
alert("Open a composition first.");
return;
}
// ── UI ────────────────────────────────────────────────────────────────
var dlg = new Window("dialog", "Offset Markers");
dlg.orientation = "column";
dlg.alignChildren = ["fill", "top"];
dlg.margins = 18;
dlg.spacing = 12;
var framesGrp = dlg.add("group");
framesGrp.add("statictext", undefined, "Offset (frames):");
var framesInput = framesGrp.add("edittext", undefined, "10");
framesInput.preferredSize.width = 60;
var dirGrp = dlg.add("group");
dirGrp.add("statictext", undefined, "Active area:");
var dirMenu = dirGrp.add("dropdownlist", undefined,
["All", "Before playhead only", "After playhead only", "Work area"]);
dirMenu.selection = 2;
dirMenu.preferredSize.width = 180;
var targetGrp = dlg.add("group");
targetGrp.add("statictext", undefined, "Target:");
var targetMenu = targetGrp.add("dropdownlist", undefined,
["Comp + all layers", "Comp markers only", "Layer markers only"]);
targetMenu.selection = 1;
targetMenu.preferredSize.width = 180;
var btnGrp = dlg.add("group");
btnGrp.alignment = "right";
var cancelBtn = btnGrp.add("button", undefined, "Cancel", { name: "cancel" });
var okBtn = btnGrp.add("button", undefined, "OK", { name: "ok" });
cancelBtn.onClick = function () { dlg.close(); };
okBtn.onClick = function () {
var frames = parseInt(framesInput.text, 10);
if (isNaN(frames) || frames === 0) {
alert("Enter a non-zero integer number of frames.");
return;
}
var dir = dirMenu.selection.index; // 0 both | 1 before | 2 after | 3 work area
var target = targetMenu.selection.index; // 0 both | 1 comp | 2 layers
var fps = comp.frameRate;
var playhead = comp.time; // seconds
var offsetSec = frames / fps; // convert frames → seconds
// ── work area bounds (only computed when needed) ───────────────
var workAreaStart = comp.workAreaStart;
var workAreaEnd = comp.workAreaStart + comp.workAreaDuration;
// ── helper: should this marker time be shifted? ────────────────
function shouldOffset(markerTime) {
if (dir === 0) return true;
if (dir === 1) return markerTime < playhead;
if (dir === 2) return markerTime >= playhead;
if (dir === 3) return markerTime >= workAreaStart && markerTime < workAreaEnd;
}
// ── helper: shift a MarkerValueCollection ──────────────────────
function shiftMarkers(markerCollection) {
var indices = [];
for (var i = 1; i <= markerCollection.numKeys; i++) {
indices.push(i);
}
// Iterate in reverse when moving forward to avoid key collisions
if (offsetSec > 0) indices.reverse();
for (var j = 0; j < indices.length; j++) {
var idx = indices[j];
var t = markerCollection.keyTime(idx);
if (shouldOffset(t)) {
var mv = markerCollection.keyValue(idx);
markerCollection.removeKey(idx);
markerCollection.setValueAtTime(t + offsetSec, mv);
}
}
}
app.beginUndoGroup("Offset Markers");
// ── Comp markers ───────────────────────────────────────────────
if (target === 0 || target === 1) {
shiftMarkers(comp.markerProperty);
}
// ── Layer markers ──────────────────────────────────────────────
if (target === 0 || target === 2) {
for (var li = 1; li <= comp.numLayers; li++) {
var layer = comp.layer(li);
if (layer.marker && layer.marker.numKeys > 0) {
shiftMarkers(layer.marker);
}
}
}
app.endUndoGroup();
dlg.close();
alert("Done. Markers offset by " + frames + " frame(s).");
};
dlg.show();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment