after-effects-extendscript
Skill1 starsChanged 4 months agoMITView on GitHub
Build and edit After Effects motion graphics from scratch with ExtendScript (.jsx) — comps, text, typing-on effects, expression-driven tracking, keyframes with easy-ease, shape/polystar elements (sparkles, glints), and timed audio. Use whenever the task is to author or change an AE script, build a title / splash / lower-third / logo animation, sync animation to a sound effect, or drive layers by expression. Encodes the hard-won rules: ExtendScript is ES3 (no const/let/arrow/template-literals), the property matchname cheatsheet, the sourceRectAtTime layout+tracking pattern, Source Text typing expressions, per-dimension KeyframeEase, server-side font resolution with graceful fallback, AE-can't-import-Opus (convert with ffmpeg), and the ffmpeg silencedetect → audio-sync recipe.
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 19
After Effects is scripted with ExtendScript against the AE scripting DOM (app, comp, layers, properties).
- Very specific: line 25
Run: AE → File > Scripts > Run Script File...
- Distinctive: line 39
CONFIG at the top: Every tunable (strings, fonts, colors as hex, sizes, timings) in one object.
- Distinctive: line 44
Use matchnames (stable across locales), not display names:
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
After Effects via ExtendScript (.jsx)
After Effects is scripted with ExtendScript against the AE scripting DOM (app, comp, layers, properties). A .jsx builds a comp deterministically and is fully parameterized — the right tool for repeatable, brand-locked motion (intros, titles, logo animations) where you want a CONFIG block instead of hand-keyframing.
There is no AE MCP. You write the script; the user runs it in AE and renders/previews to verify. Treat the render (a frame or clip the user shares) as the feedback loop — build, ask for a render, read it, fix, repeat. There is also no automated screenshot; favor a CONFIG block + an end-of-run alert() summary so the user can self-tune.
Run + verify model
- Run: AE →
File > Scripts > Run Script File...→ pick the.jsx. No project/comp setup needed if the script builds its own. - Syntax-check locally before handing off:
noderejects the.jsxextension, socp script.jsx _check.js && node --check _check.js && rm _check.js. This catches parse errors; it does NOT validate AE DOM calls. - Headless render (optional):
aerenderCLI can render a comp for an automated check, but it needs AE installed and is heavy — default to the user previewing.
ExtendScript is ES3 — write accordingly
varonly. Noconst/let, arrow functions, template literals, destructuring, orfor...of. Use function declarations,forloops, and string concatenation.===,try/catch,Array.pushare fine; avoidArray.forEach/map(spotty).- Wrap mutations in
app.beginUndoGroup("...")/app.endUndoGroup(), and the whole body intry/catchending with analert()so failures surface. - Build expression strings by concatenation and inject CONFIG numbers/strings at build time (expressions can't read your CONFIG). Escape injected strings (
'"' + s.replace(/"/g,'\\"') + '"'). Newlines in expression source:\r.
Core principles
- Anchor at the origin, then measure. For text layout set
Anchor Point = [0,0](baseline-left), thenlayer.sourceRectAtTime(0, false)→{left, top, width, height}relative to that anchor. Left-edge placement:position.x = desiredLeft - rect.left; baselineposition.y = baselineY. Vertical-center off the tallest glyph:baselineY = compH/2 - (rect.top + rect.height/2). The samesourceRectAtTimeconvention holds in expressions, so build-time layout and expression-time tracking agree. - Prefer expressions over keyframes for anything derived. Typing-on, edge-tracking, blinking — all are one expression and need no rebuild when strings change.
- CONFIG at the top. Every tunable (strings, fonts, colors as hex, sizes, timings) in one object. Colors: store hex, convert to 0–1 RGB. This is what makes the script reusable and brand-switchable.
- Verify state before reading children.
app.projectalways exists. For multi-page/precomp work, set the active item before reading its layers.
Property matchname cheatsheet
Use matchnames (stable across locales), not display names:
- Transform:
ADBE Transform Group→ADBE Anchor Point,ADBE Position,ADBE Scale,ADBE Rotate Z(2D rotation),ADBE Opacity. - Text source:
layer.property("ADBE Text Properties").property("ADBE Text Document")—.valueis aTextDocument; mutate it thensetValue(doc). Put a typing expression on this property's.expression. - Shape layer:
ADBE Root Vectors Group→addProperty("ADBE Vector Group")→.property("ADBE Vectors Group")→addProperty("ADBE Vector Shape - Star")(or...- Rect,...- Ellipse) +addProperty("ADBE Vector Graphic - Fill"). Fill color via.property("ADBE Vector Fill Color").setValue([r,g,b,1]). - Polystar:
ADBE Vector Star Type(1 = star, 2 = polygon),ADBE Vector Star Points,ADBE Vector Star Outer Radius,ADBE Vector Star Inner Radius, and the misspelledADBE Vector Star Outer Roundess/ADBE Vector Star Inner Roundess(wrap in try/catch). A 4-point star with inner radius ≈ 0.16×outer = a sparkle/glint.
TextDocument gotchas
- Set
applyFill=true+fillColor=[r,g,b],applyStroke=false,justification(useParagraphJustification.LEFT_JUSTIFYfor left-anchored growth), thentext, thensetValue(doc). - AE style names use spaces:
"Semi Bold","Extra Bold"— not"SemiBold".
Fonts: PostScript names + graceful fallback
TextDocument.font wants a PostScript name, not a family name. Resolve family→PS via app.fonts.getFontsByFamilyNameAndStyleName(family, style) or by scanning app.fonts.allFonts when that API exists; otherwise trust your first guess (AE substitutes a missing font rather than erroring). Pattern: resolvePS(family, style, guessList, fallbackPS) returning a PS name; fallbacks like Consolas (mono) / ArialMT (sans). See assets/helpers.jsx.
AE renders only SYSTEM-installed fonts. A brand's web font (e.g. one loaded via a Google Fonts @import in a webapp) is invisible to AE — it silently falls back (you'll see Arial-BoldMT in the build alert). To actually match an in-app wordmark, install the font on the OS first. Windows per-user install (no admin), then restart AE: copy the static .ttfs to %LOCALAPPDATA%\Microsoft\Windows\Fonts and add HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Fonts values like "Inter Bold (TrueType)" = the full font path. Have the script's alert print the resolved PS name and warn if the family didn't resolve, so a silent substitution is caught.
Keyframes + easy-ease
Set values with prop.setValueAtTime(t, v), then per key: setInterpolationTypeAtKey(i, BEZIER, BEZIER) and setTemporalEaseAtKey(i, easeIn, easeOut) where each ease array has one new KeyframeEase(0, 33) per value dimension — 1 for opacity/rotation, 2 for Scale [x,y]. Exception: spatial properties (Position) take a SINGLE element regardless of dimensionality — passing 2 to a keyframed Position throws Unable to call "setTemporalEaseAtKey" ... Value array does not have 1 elements. Gate on prop.isSpatial (use 1) vs prop.value.length. Wrong length throws. easeAll(prop) in assets/helpers.jsx handles this.
Patterns
- Typing-on: Source Text expression
n=(time<ts)?0:Math.floor((time-ts)/iv)+1; s.substr(0,n). Measure the full string first for layout, then apply the expression. - Edge tracking: drive a follower's
position.xoff the typed layer'ssourceRectAtTimeso a closing bracket / caret tracks the growing text. Seereference/audio-sync.mdandassets/helpers.jsx(edgeTrackExpr). - Blink: opacity expression gated to a window:
(time<ts||time>te)?0:(Math.floor((time-ts)*hz*2)%2===0?100:0). - Sparkle/glint: a polystar that scales
0→peak→0, slow-rotates, and flashes opacity, timed to a beat (e.g. a ding).makeSparkle+animSparklein helpers. - Audio sync: see
reference/audio-sync.md— convert non-WAV with ffmpeg, extract onsets withsilencedetect, import +layer.startTime, lock the typing interval to the recording.
Portability + render
- Resolve bundled assets relative to the script:
(new File($.fileName)).parent.fsName + "/sfx/". Bundle SFX/images next to the.jsx. - Transparent background = no solid layer (comps are transparent by default). Render with an alpha codec (ProRes 4444 / PNG sequence) and Audio Output On.
Committing AE scripts (secret scanners)
High-entropy asset filename literals (e.g. keystrokes-14.wav) inside a .jsx can trip a secret scanner's generic-api-key rule. Path-allowlist the script in your scanner config rather than renaming the asset.
Keep a canonical example
Maintain one CONFIG-driven reference script in your repo and point new work at it. A good intro/splash reference exercises the whole toolkit in one file: transparent 1920×1080 / 30fps, a two-tone-capable wordmark, expression typing + an edge-tracked bracket + a blinking caret, timed keystroke/enter/ding SFX locked to the typing, and a polystar sparkle on the ding. When the code and this skill disagree, the code wins — update the skill.
In this repository8 other files it gives its agents.
- claude-code-hooksSkill
- cloudflare-pages-deploySkill
- figma-design-buildSkill
- kie-image-editSkill
- langgraph-agentSkill
- langsmith-evalsSkill
- n8n-figma-workflow-mapSkill
- video-app-walkthroughSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

