Skip to content
barchukmovPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Loadout

A toolbox for After Effects that happens at the cursor. Press a key anywhere in AE and the tool opens under your mouse — no panel to aim at, no trip to a menu, no dialog to dismiss.

Windows only, After Effects 2024+. GPL-3.0-or-later.


Download

Latest release →

You are on Take Then
Windows loadout-setup-<version>.exe Run it. It installs the plugin (admin) and the panel, and registers an Add/Remove Programs entry. Restart After Effects.
macOS loadout-<version>-macos.zip Unzip, run install.sh, restart After Effects. It clears the download quarantine flag — an unsigned plugin will not load otherwise — and the popups will ask for Accessibility permission once. See macOS: Gatekeeper and Accessibility.

You do not need the panel to use Loadout: the plugin ships with working default shortcuts (Alt+Z easing, Alt+A anchor point) and reads them from its own defaults until the panel writes a prefs file. The panel is where you change them.

The macOS build has never been compiled or run. It was written on Windows against Apple's documentation. There is no mac release to download yet, and the row above describes what one will look like — see Building if you want to be the one who tries it.

Uninstalling, building from source, and installing without the setup .exe are all under Installing.


The idea: a tool

Every tool in Loadout is the same shape, and that shape is the whole product:

a key → something appears under the cursor → one click decides it.

Concretely, a tool is:

A shortcut Registered with Windows, so it reaches the tool before After Effects sees the key — and only while AE is the active application.
A helper at the cursor A small raylib window that opens where the mouse already is, plus a hint bar pinned to AE's own window listing what the modifiers do.
Hooks into AE The tool's actual edit, run as ExtendScript on AE's main thread, in one undo step.

Two rules fall out of that and hold for every tool:

  • Left-click commits, right-click cancels. Nothing else closes a tool — not focus loss, not Esc. (Esc is swallowed on purpose: After Effects aborts any running script the moment it sees one.)
  • The tool never guesses. If it can't do the thing correctly it says so, naming the actual reason, rather than doing something approximate quietly.

Tools are declared in one place — src/js/lib/tools.ts — which is what the panel renders, what the shortcut defaults come from, and what the settings list is built out of. Adding a tool is adding an entry there (see Adding a tool).


The tools

Shortcuts are written the Windows way below. On macOS Alt is Option and the defaults are the same physical keys; the one modifier that genuinely differs is the easing slider's precision drag, which is Ctrl on Windows and ⌘ on macOS — because Ctrl-click there is a right-click, and right-click is how the popups cancel.

Easing — Alt+Z

A slider that eases the selected keyframes, with live preview in the comp.

  • No clicking, no dragging — the slider tracks the cursor from the first frame. Left-click commits; right-click puts every key back exactly as it was, interpolation types and tangents included.
  • Alt = ease in only · Shift = ease out only · Ctrl = precision (1/30 speed) drag.
  • Smart interiors: keys inside a monotonic run get temporal auto-bezier instead of the ease — for spatial properties (Position, point controls) a key counts as "passing through" when its incoming and outgoing motion agree within 90° (positive dot product); for everything else, when no dimension reverses direction. Press A to switch it off and ease the whole selection flat; the preview re-applies as you toggle.
  • Hold button sets just the outgoing handle to Hold.

Anchor point — Alt+A

A 3×3 grid: click a target to snap the layer's anchor point there — corner, edge midpoint, or centre — without the layer moving on screen.

The 3×3 grid isn't new; several tools have shipped one for years. What they don't do well is answer the question the grid depends on: what is this thing's bounding box, really? Most answer "the layer's source rect" and stop — which is wrong the moment a mask is involved, wrong for text with strokes, and undefined for a shape group nested behind a repeater. So the centre of gravity here is a bounds engine, and the grid is its first consumer:

  • Mask-aware: bounds are the intersection of the source rect and the mask coverage — for raster layers too, since a mask larger than the footage doesn't add pixels. Curvature is solved properly (a bezier segment bulges outside its vertices, so the box comes from the curve's actual turning points, not min/max over the control points), and mask mode and expansion are honoured.
  • Correct on rotated layers. The usual compensation, Δposition = Δanchor × scale, is only right at zero rotation; this uses the full R(θ)·S·Δanchor. On a 45° layer the difference is obvious.
  • Shape groups: select a group instead of a layer and the group's own anchor moves, with its bounds built from the paths inside it.
  • It refuses rather than guessing. A position expression, an expression-driven anchor, a 3D layer, a repeater or trim path in the group — each gets a specific message naming the actual problem. Where the answer is merely approximate (subtract masks, animated scale between keyframes) it says so instead of refusing. Details land in %TEMP%\loadout-anchor.log.
  • One undo step, across as many selected layers as you like.

The panel

A settings panel (Window > Extensions > Loadout) listing every tool with its shortcut, its modifier legend, and its own settings — plus the keyboard map, which shows where Loadout's keys land among After Effects' own and warns when one takes a key AE was using.

The panel is optional. The tools work with it never opened; it writes two JSON files and that is the entire link between the two halves.


How it's put together

Two halves, decoupled through a couple of JSON files in %APPDATA%. There is no server, no port, and no runtime link between them — the prefs files are the contract.

  • AEGP plugin (AEGP/, C++ → Loadout.aex): registers the global hotkeys, draws the popups (a panel-sized raylib window centered on the cursor, created fresh per popup and fully torn down on dismiss), and applies the changes by running the typed ExtendScript functions (src/jsx/) through AEGP_ExecuteScript on AE's main thread. It ships as a single file — the font, the icons and the compiled jsx bundle are compiled in as resources, so there is no folder of assets that can get separated from the plugin.
  • CEP panel (src/js/, Svelte + TypeScript via bolt-cep): purely the settings UI.
  • Prefs: %APPDATA%\loadout-hotkeys.json (the shortcut table) and loadout-settings.json (tool and plugin settings). CEP writes them; the AEGP reads the table at AE launch — so hotkeys are live from startup with no panel running — and re-reads it when its directory watch sees the file change. The write is the notification.

The anchor tool's bounds engine (src/jsx/anchor/) is deliberately kept free of AE API calls below its adapter module, so it can be reasoned about — and reused by other tools — outside After Effects.

CLAUDE.md has the deeper architecture notes and a list of hard-won gotchas (DWM transparency on hybrid GPUs, ExtendScript's ternary parsing, MSBuild quoting) — worth reading before touching the popup or the build.

Repo map

Path What's in it
AEGP/ The C++ plugin: hotkey engine, popups, script runner
AEGP/tools/ Dev-only: the standalone popup runner and the AE-window probe (README)
src/js/ The CEP panel (Svelte). lib/tools.ts is the tool registry
src/jsx/ ExtendScript: the ease implementation and the anchor tool
installer/ Payload staging, install/uninstall scripts, Inno Setup wrapper
scripts/ One-time setup (AE SDK, panel signer) and the release script
tests/ The manual QA pass and its fixture project (README)

Adding a tool

A tool that needs no UI of its own — most of them — takes no C++ change:

  1. Export a function from src/jsx/aeft/aeft.ts (or a module it re-exports).
  2. Add an entry to TOOLS in src/js/lib/tools.ts: its id is the exported function's name, plus a name, a tagline, an icon path, a default shortcut, its hint lines, and any settings.
  3. Build. The panel lists it, gives it its key, and merges that binding into any existing user's table on next open.

A tool only touches C++ when it wants a native popup: add a handler to kNativeTools in AEGP/HotkeyOverlay_Win32.cpp, keyed by the same id, and give it a hint-bar legend next to the loop that implements it.


Building

Prerequisites, both platforms: After Effects 2024+, CMake 3.20+, Node.js 18+ and pnpm. Then Visual Studio 2022 with the C++ desktop workload on Windows, or the Xcode command line tools (xcode-select --install) on macOS — the mac build needs Rez from those to assemble the PiPL.

On the state of the macOS build. It is written but has never been compiled or run: it was developed entirely on Windows, against Apple's documentation. Treat it as a first draft that needs a Mac to become real. tests/README.md lists every assumption in it that has never been executed, in the order worth checking. The Windows build is unaffected and is verified on every change.

1. The Adobe SDK (one-time)

Adobe's licence doesn't allow redistributing the SDK, so this repo doesn't carry it. Download it (free, no account) from developer.adobe.com/after-effects, then:

pnpm run sdk -- -Path "$env:USERPROFILE\Downloads\AfterEffectsSDK-Win.zip"

That unpacks it if needed, finds the headers, Util/ and the PiPL tool wherever the SDK's layout has moved them to, copies them into AEGP/, and verifies the result. Run it with no -Path and it looks in Downloads and Desktop for you. Everything it copies is gitignored.

2. The panel signer (only for packaging a release)

CEP checks an extension's signature at load, so a panel other people can install has to be signed — by ZXPSignCmd, an Adobe binary that isn't ours to redistribute either. pnpm build and pnpm dev don't need it; pnpm run installer and pnpm run release do.

pnpm run signer

It tells you where to download it (Adobe's CEP resources) and installs it into tools/ when you point it at the download. A copy also ships inside node_modules (vite-cep-plugin), which the build falls back to, so a fresh clone usually needs no download at all. The installer build checks for it up front rather than failing at the signing step.

Signing itself is scripts/sign-zxp.ps1, run for you by the installer build (or on its own with pnpm run sign). It generates tools/loadout-cert.p12 on first use and reuses it forever after — keep that file. CEP identifies an installed extension by the certificate that signed it, so a release signed with a different one is, to CEP, a different extension that users have to remove the old copy of before installing. The certificate is self-signed (CEP requires a valid certificate, not a CA-issued one) and good for ten years. There is no timestamp on the signature: ZXPSignCmd 4.1.103 can't produce one on Windows against any timestamp server tried, so the signature is valid until the certificate expires rather than indefinitely.

3. Build

pnpm install
pnpm build          # CEP bundle + plugin, straight into AE's Plug-ins folder
pnpm run build:aegp # just the C++ plugin (Windows)
pnpm run build:mac  # just the C++ plugin (macOS)
pnpm dev            # the panel in a browser, with HMR

The C++ build is one CMake description for both platforms (AEGP/CMakeLists.txt); build.ps1 is a wrapper that pins the Visual Studio generator and the v143 toolset, then installs the result. That pin matters — AE 2026 refuses to load an .aex built with v145, saying only "No loaders recognized this plugin".

The first plugin build fetches and compiles raylib via CMake FetchContent into the build directory (not tracked). On Windows the default -OutDir writes into C:\Program Files\Adobe\Adobe After Effects 2026\Support Files\Plug-ins\AEGP\, which needs an elevated shell — pass another -OutDir to build elsewhere. On macOS the build lands in AEGP/build-mac/ and installing is a separate step (see below), because writing into /Applications needs sudo and a build shouldn't.

For iterating on the popups without AE in the loop, see AEGP/tools/README.md; for what to check before a release, tests/README.md.


Installing

Loadout is two payloads with two homes, and the installer is the only thing that knows about both:

On Windows:

Payload Goes to Needs
Loadout.aex — one file; the font, icons and compiled ExtendScript are resources inside it …\Adobe After Effects <ver>\Support Files\Plug-ins\AEGP\Loadout\ admin
The CEP settings panel (signed .zxp) %APPDATA%\Adobe\CEP\extensions\Loadout per-user
loadout-hotkeys.json, loadout-settings.json %APPDATA% written by the panel

On macOS:

Payload Goes to Needs
Loadout.plugin — a bundle; the font, icons and compiled ExtendScript are in its Resources /Applications/Adobe After Effects <ver>/Plug-ins/loadout/ sudo
The CEP settings panel (signed .zxp) ~/Library/Application Support/Adobe/CEP/extensions/com.barchukmov.loadout per-user
loadout-hotkeys.json, loadout-settings.json ~/Library/Application Support written by the panel

The prefs files are user data and survive uninstall unless you ask for them to go. The same signed .zxp installs on both platforms — a .zxp declares host applications, not an OS.

macOS: Gatekeeper and Accessibility

Two things are different on a Mac, and both are consequences of not having an Apple Developer ID.

The release is an unsigned zip, not a .pkg. macOS quarantines everything in a downloaded archive and refuses to load a quarantined unsigned plugin, so installer/mac/install.sh clears that flag from what it installs (xattr -dr com.apple.quarantine). The script is short and worth reading before you sudo it. An unsigned .pkg would be worse, not better: Gatekeeper blocks those outright, with a dialog offering no way forward.

The popups ask for Accessibility permission, once, the first time one opens (System Settings → Privacy & Security → Accessibility, then restart AE). It buys one thing: keeping the popup's own keys and clicks away from After Effects underneath. Decline and the tools still work, but Escape while a popup is up aborts the ExtendScript the popup is running — AE's own behaviour, and the source of its "Unable to execute script at line N" alert — and the click that commits also lands in your timeline.

sudo ./install.sh          # from the unpacked zip
sudo ./uninstall.sh        # add --remove-prefs to drop the hotkey table too

Build the zip with pnpm run package:mac. It needs a signed dist/zxp/loadout.zxp to package a panel other people can load; without one it falls back to the unsigned panel and says so. That signing currently happens only on the Windows box, since that is where tools/loadout-cert.p12 lives.

Building the installer

pnpm run installer

Runs pnpm zxp (panel + signed .zxp), builds the plugin into installer/payload/ instead of AE's plugin folder, and compiles dist/installer/loadout-setup-<version>.exe with Inno Setup 6 (winget install JRSoftware.InnoSetup). Without Inno it stages the payload and stops — still a complete install, just driven by hand.

Installing without the setup .exe

From an elevated shell, with After Effects closed:

powershell -ExecutionPolicy Bypass -File installer\install.ps1
powershell -ExecutionPolicy Bypass -File installer\uninstall.ps1

Useful switches: -SkipCep (plugin only), -AeRoot <path> (one AE version instead of every detected one), -RemoveSettings and -RemoveDevBuild on uninstall. The setup .exe is a thin wrapper around exactly these scripts.

Two things the scripts refuse to do quietly: they will not install while AfterFX.exe is running (AE holds an open handle on every loaded .aex), and they will not install alongside a flat dev build from AEGP/build.ps1 — both copies would load and fight over the same global hotkeys. -Force overrides either.


Releasing

One version number, in package.json. Everything downstream reads it: the .zxp manifest, the .aex (stamped into a generated header at build time and logged at load, so "which build is loaded?" is always answerable), and the installer.

pnpm run release -- -Version 0.2.0

Refuses on a dirty tree or an existing tag, sets the version, builds the whole release, then commits and tags v0.2.0. Pushing is left to you.

Add the release notes to CHANGELOG.md under a ## [0.2.0] — YYYY-MM-DD heading before tagging: pushing the tag fires .github/workflows/release.yml, which opens a draft GitHub Release with that section as its body. The workflow cannot build or attach anything — the Adobe SDK and the signing certificate are both local-only, by licence and by design — so the artifacts go up from your machine:

git push; git push origin v0.2.0
gh release upload v0.2.0 dist/installer/loadout-setup-0.2.0.exe
gh release edit v0.2.0 --draft=false

Draft rather than published is deliberate: a release that goes public with no files attached is the failure this is shaped to prevent.


License

GPL-3.0-or-later — see LICENSE, which also carries an additional permission under GPL-3.0 section 7 for linking against the Adobe After Effects SDK and the Adobe CEP libraries. A plug-in cannot exist without them and Adobe does not license them freely, so without that permission nobody could lawfully redistribute a compiled build. Third-party components (including the MIT-licensed bolt-cep scaffold this project started from) are listed in THIRD-PARTY-NOTICES.md. The SDK itself is required to build but is licensed separately by Adobe and not included.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages