Merge enhance/filmcraft-avi-backend into enhance/avi-import

# Conflicts:
#	crates/export/tests/it/movie_bytes.rs
This commit is contained in:
tylerjenningsw
2026-10-29 18:58:57 -04:00
272 changed files with 13767 additions and 1294 deletions
+12 -5
View File
@@ -272,10 +272,10 @@ build real projects through these interfaces; they are worked examples of everyt
then `keys.velocity {"outSpeed":0,"outInfluence":33.33}`.
* **Gradients**: `set_property {"path":"contents/gfill/colors","value":["#0080ff","#ffff00"]}` or
`{"colors":[[0,"#0080ff"],[1,[1,1,0]]],"opacities":[[0,1],[1,0.5]]}`.
* **Image sequences**: `file.import {"paths":["/r/shot_0001.png"]}` imports the file's whole
numbered run as one item (several picked frames: that range; a folder: each run in it;
`"sequence": false`: one still; dropped files, `"drag": true`, import as stills unless
`"sequence": true`, as in After Effects; `"alphabetical": true`: Force Alphabetical Order, the files
* **Image sequences**: `file.import {"paths":["/r/shot_0001.png"], "sequence": true}` imports the
file's whole numbered run as one item (several picked frames: that range; a folder: each run in
it, also without `sequence`). Without `"sequence": true` a picked or dropped numbered file imports
as one still, as in After Effects; `"alphabetical": true`: Force Alphabetical Order, the files
play one after another) at `frameRate` or 30 fps (Settings ▸ Import). Missing frames show
colour bars, as in After Effects. `file.interpretFootage {"items":[id], "frameRate":12,
"startTimecode":1001}` conforms them and sets Override Start (the item and layers that ran to its end get the new
@@ -540,7 +540,7 @@ indices): character attributes
3. `layer.setText {"range":[6,11], "size":40, "fill":"#ff5500"}` styles the selection (the
Character panel does the same); with an empty range it sets the style the next typed text takes.
4. `edit.copy`, `edit.paste`, `edit.pasteTextMatchFormatting` and `edit.pasteTextFormattingOnly`
work on the selected text; `text.endEdit` commits (an empty Type-tool layer is removed).
work on the selected text; `text.endEdit` commits (an empty Type-tool layer is kept, as After Effects does).
Fonts: `text.fonts` (MCP `list_fonts`) lists every family a text layer can use, bundled and installed, with its
styles, origin (`bundled`, `system`, `user`), `variable` (it has variation axes) and own-language name
@@ -622,6 +622,13 @@ Extension points and the bundled extensions: [plugins.md](plugins.md).
Dragged onto another property's name or value, in the Timeline or Effect Controls
(`effectControls.prop.<uid>.value[.<d>]`), it runs `prop.pickWhip`: the reference replaces the
property's expression, or is added as one, and the expression is enabled.
* Parent and Track Matte for several layers: on a selected layer's row, the Timeline's Parent
menu and pick whip (`timeline.layer.<id>.parent`, `timeline.layer.<id>.pickWhip`) and its Track
Matte menu and pick whip (`timeline.layer.<id>.trackMatte`,
`timeline.layer.<id>.trackMattePickWhip`) set every selected layer's (on an unselected row,
that layer's), in one undo step. Directly: `layer.setParent {layers, parent}` and
`layer.setTrackMatte {layers, matte, kind}`, where `kind: "preserve"` keeps each layer's
own matte mode.
* A property's menu (right-click its name; entries `timeline.prop.<uid>.menu.<entry>`): Reset
(`prop.reset`: no expression, and the property's default value, an effect parameter's or a
Transform property's; an animated property keeps its keyframes and gets the default as a
+12 -7
View File
@@ -371,12 +371,12 @@ Playground cannons without interacting forces evolve every particle independentl
backend (reached through `EffectHost::particles` when the GPU compositor is active) simulates one
particle per invocation from the CPU's birth schedule and keeps per-key state checkpoints on the
GPU, like `SimCache`; the CPU simulation stays the oracle (tests compare ids and positions).
CC Particle World samples its Birth Rate at every step (`EffectHost::param_at`, #1373), so a
keyed rate only decides the births of its own time; the rate is left out of the simulation key,
and a checkpoint is resumed only with the same rate history (the CPU state's `hist`, the GPU
CC Particle World, CC Particle Systems II and CC Mr. Mercury sample their Birth Rate at every
step (`EffectHost::param_at`, #1373), so a keyed rate only decides the births of its own time;
the rate is left out of the simulation key, and a checkpoint is resumed only with the same rate history (the CPU state's `hist`, the GPU
checkpoint's births). The rate's keyframes are read at the step's exact layer time
(`EvalCtx::value_at_layer_time`; mapping it to comp time and back rounds under a fractional
stretch), a rate without keyframes or an expression isn't sampled, and a Particle World layer with
stretch), a rate without keyframes or an expression isn't sampled, and a layer of one of them with
expressions isn't layer cached (its key can't see what an expression returned at earlier times).
`effectcraft-cli bench --gpu` reports CPU vs GPU ms/frame, speed-up, Auto's ms/frame, pixel
agreement and the steady-state upload / readback MB per frame for every comp plus an
@@ -401,7 +401,10 @@ hierarchical block-matching optical flow and a bidirectional warp (Pixel Motion,
its nested layers straight into the parent with concatenated transforms (one resample), so
nested blend modes and adjustment layers act on the parent's layers; nested 3D layers use the
parent's camera and lights and, when the precomp layer is 3D, are depth-sorted with the parent's
3D run. Masks, effects or styles force a flattened render. Text layers are always rasterised at
3D run. Essential Properties overrides (properties, Font and Media Replacement) travel with the
collapsed context and are applied while the nested layers are evaluated, so they do not force a
flattened render or clip at the precomp's frame. Masks, effects or styles force a flattened
render. Text layers are always rasterised at
their on-screen scale, as text is vector in After Effects; on shape layers and vector footage the
switch is Continuously Rasterize and does the same. Quality: Draft samples
nearest-neighbour, Wireframe draws the layer bounds. Slip edit (`layer.slip`, Alt+PageUp/Down,
@@ -468,8 +471,10 @@ effects crate, hence the `effects → track` edge.
`essential` field lists its exposed controls (properties, Media Replacement, groups, comments).
Every precomp layer of such a comp gets an Essential Properties group (`GroupKind::Essential`,
children `eg<control id>`), kept in step by `Session::edit`; editing a child overrides it for
that instance. The renderer draws an instance with overrides from a copy of the project with the
overridden source properties set (`essential::with_overrides`). Templates are `.ectemplate`
that instance. A collapsed instance carries its property, Font and Media Replacement overrides
into the nested render context (`Collapsed`, `EvalCtx::{property_overrides, media_overrides}`);
other instances are drawn from a copy of the project with the overridden source properties set
(`essential::with_overrides`). Templates are `.ectemplate`
ZIPs: `manifest.json` (controls), `project.ecproj` (the comp and its dependencies), `media/…` and
`poster.png`; importing offsets every id past the project's (`essential::offset_ids`).
**Responsive Design — Time**: a time-stretched precomp maps time piecewise so its protected
+1 -1
View File
@@ -103,7 +103,7 @@ Points are logical window coordinates. A target is `{id}` (the element's centre;
| `ui.click` | `{id \| x,y, button?: left\|right\|middle, count?, modifiers?}` |
| `ui.move` | `{id \| x,y}` |
| `ui.drag` | `{from: target, to: target, steps? (12), modifiers?}`: press, move, release |
| `ui.scroll` | `{id \| x,y, dx, dy}` |
| `ui.scroll` | `{id \| x,y, dx?, dy?, unit?: point\|line, source?: wheel\|trackpad, pinch?, modifiers?}`: `line` is a mouse wheel (zooms the viewers); `point` (default) is a pixel-precise scroll; `source: trackpad` pans the viewers (as on macOS); with no `source` the viewers take a scroll in points as a wheel until one moves on both axes (a trackpad: from then on it pans); `pinch` is a trackpad pinch's zoom factor |
| `ui.key` | `{key}`, for example `Space`, `F9`, `Cmd+Z`, `Cmd+Shift+Z`, `PageDown`, `Escape` |
| `ui.type` | `{text}`: text input into the focused field |
+2 -2
View File
@@ -16,7 +16,7 @@ After Effects', and does the preset library exist. The per-effect catalogue is t
| On the GPU | **280** (92%) | Measured: registry flags; GPU vs CPU agreement tested per effect |
| 32-bit float, no clamping | 306 | Measured: registry flags |
| **Rendered output matches After Effects** | **partly measured** | Some colour effects are checked against After Effects 2026 renders of our own test projects: Tint, Tritone, Black & White, Exposure, Levels, Curves, Luma Key, Extract, 4-Color Gradient and Fill within 1–3/255, Brightness & Contrast up to 6.2/255 (Color Correction, Generate and Keying rows below); Radial Blur, Glow, Venetian Blinds, Radial Wipe, Fractal / Turbulent Noise and Transform are checked the same way (rows below); every other effect is unmeasured. `effectcraft_effects::fidelity::compare_frames` exists for the corpus ([fidelity/README.md](fidelity/README.md)); simulation effects are pinned by golden hashes of our own output (M13.27) |
| Animation presets | **54** original presets (8 text-animator, 23 Transitions, 23 Backgrounds) against After Effects' **621** `.ffx` presets | Measured: `crates/engine/presets/text_animators.json`, `crates/engine/presets/transitions.json`, `crates/engine/presets/backgrounds.json`; the After Effects `Presets` folder |
| Animation presets | **76** original presets (8 text-animator, 23 Transitions, 23 Backgrounds, 22 Behaviors) against After Effects' **621** `.ffx` presets | Measured: `crates/engine/presets/text_animators.json`, `crates/engine/presets/transitions.json`, `crates/engine/presets/backgrounds.json`, `crates/engine/presets/behaviors.json`; the After Effects `Presets` folder |
| 26.x additions | Curl Noise (26.3) present (#463 open); effect colour labels (26.5) present in Effect Controls only, not in the Timeline; the rebuilt Effect Controls (26.5) missing | Release notes |
**Breadth ≈ 85%** (presets weigh in; the effects themselves are ≈ 100%). **Ready ≈ 40%**: until a
@@ -56,7 +56,7 @@ implemented under a generic name ("Key Light").
**≈ 70–130 h**: a fidelity corpus case per effect at default and one non-default setting, compared
with After Effects renders, and the fixes they turn up (40–80 h, most of it parallel, but every
reference render needs After Effects on the owner's Mac); an original preset library covering
After Effects' preset categories (Behaviors, Shapes, Synthetics, Text, Image effects remain; Backgrounds and
After Effects' preset categories (Shapes, Synthetics, Text, Image effects remain; Backgrounds, Behaviors and
Transitions exist) 15–35 h; the open effect bugs 10–20 h. Moving Fractal and the analysis effects to
the GPU is performance, not parity.
+4 -3
View File
@@ -3,11 +3,11 @@
<!-- Generated from the effect registry (crates/effects/src/catalog.rs); do not edit by hand.
Regenerate: UPDATE_DOCS=1 cargo test -p effectcraft-effects --lib effects_doc_is_current -->
EffectCraft ships 307 effects with 3032 parameters in total, grouped into the same categories as the Effects & Presets panel. Every effect is our own implementation, written from public behaviour descriptions and standard image-processing literature. Parameter names, order, popup options, units, defaults and ranges follow the reference application so that projects, expressions and muscle memory carry over.
EffectCraft ships 308 effects with 3033 parameters in total, grouped into the same categories as the Effects & Presets panel. Every effect is our own implementation, written from public behaviour descriptions and standard image-processing literature. Parameter names, order, popup options, units, defaults and ranges follow the reference application so that projects, expressions and muscle memory carry over.
- **GPU**: 280 effects also run on the GPU compositor with identical results.
- **32**: 307 effects process 32-bit float (HDR, overbright) pixels without clamping.
- **Status**: 307 implemented in full, 0 partial (what is missing is listed).
- **32**: 308 effects process 32-bit float (HDR, overbright) pixels without clamping.
- **Status**: 308 implemented in full, 0 partial (what is missing is listed).
Effects are addressed by id (`ec.<category>.<name>`) or display name in commands, scripts and the control channel, and every parameter by its id (`effects/#1/blurriness`).
@@ -16,6 +16,7 @@ Effects are addressed by id (`ec.<category>.<name>`) or display name in commands
| Effect | Id | Params | GPU | 32 | Status |
|---|---|---:|:-:|:-:|---|
| 3D Channel Extract | `ec.3d.channelextract` | 6 | GPU | 32 | Implemented |
| 3D Layer Instance | `ec.3d.instance3d` | 1 | | 32 | Implemented |
| Cryptomatte | `ec.3d.cryptomatte` | 4 | GPU | 32 | Implemented |
| Depth Matte | `ec.3d.depthmatte` | 3 | GPU | 32 | Implemented |
| Depth of Field | `ec.3d.depthoffield` | 4 | GPU | 32 | Implemented |
+49
View File
@@ -71,6 +71,55 @@ After the fix, all ten tangents equal AE's to the printed 0.001 px, and the two
test `spatial_auto_bezier_tangents_follow_after_effects` checks the rule on other original keys,
including a timing change. AE's output stays local; only the script and these scores are tracked.
## Justify All with the every-line composer
Measured on 19 October 2026 with After Effects 2026 (26.2) on box text (EXPORTER read-back t6):
with Justify All, AE stretches the paragraph's last line too and breaks the lines so that it is not
a lone word (4 / 4 / 4 words where the other justifications break 6 / 6 / 1). EffectCraft's
every-line composer ignored the last line's slack and left "twelve" alone on a stretched line; it now
counts that slack for Justify All, and every word starts within 1 px of AE's. Regression test:
`justify_all_balances_the_lines_with_the_every_line_composer`.
## Right-to-left point text with a justified paragraph
Measured on 19 October 2026 with After Effects 2026 (26.2) (EXPORTER read-back t7): right-to-left
point text whose paragraph has a justified code (justify last left / centre / right, justify all)
sits flush right at its anchor, like right-aligned text. EffectCraft placed it by the last-line
side (left / centre / left). Now flush right; left-to-right point text is unchanged (not measured).
Regression test: `rtl_point_text_with_a_justified_paragraph_is_flush_at_its_start`. Still open:
AE wraps right-to-left box text slightly wider (its word gaps are 1–2 px narrower than in
left-to-right text with the same font), so a 300 px box fits a word more per line.
## Text animator tracking
Measured on 19 October 2026 with After Effects 2026 (26.2): a text animator's Tracking Amount is in
pixels, not 1/1000 em like the Character panel's tracking (Amount 30 adds 30 px between neighbouring
characters at 80 px and at 40 px, half before and half after each), and a Subtract range selector
over characters the first selector leaves out keeps them at 0 instead of tracking them negatively.
With both, the EXPORTER's t8 layer D (Tracking 30 on characters 2–4 by index, a Subtract selector
over the last 20 %) puts every glyph where AE does (it ended 88 px short). The "Tracking In" text
preset now animates 40 → 0 px (was 400 → 0 thousandths of an em). Lottie export is unchanged (not
checked against a player). Regression tests: `animator_tracking_is_in_pixels_as_in_after_effects`,
`modes_combine`.
## Per-character 3D rotation order
Measured on 19 October 2026 with After Effects 2026 (26.2) (case 931: "H" at 400 px, per-character
3D, animator X / Y / Z Rotation 40 / 30 / 20): a character turns as a 3D layer does, Rx · Ry · Rz
(Z first). EffectCraft composed Rz · Ry · Rx, so the glyph faced another way (23 of 5184 grid points
off, max 255); now 3 at the anti-aliased edge (mean 0.015). Regression test:
`per_character_rotation_turns_z_first_as_a_3d_layer`.
## Rectangle path start point
Measured on 19 October 2026 with After Effects 2026 (26.2) on 400 × 300 shape rectangles with Trim
Paths: a plain rectangle starts at its top-right corner and runs clockwise (0–25 % covers the right
edge and 50 px of the bottom); with Roundness 40 it starts where the top-right arc meets the right
edge. Start 70 / End 30 draws 30–70 %. EffectCraft started at the top-left corner (and the rounded
rectangle elsewhere), so trimmed rectangles drew the wrong segment (86 of 5184 grid points off,
now 5 at stroke edges). `effectcraft_path::rect` now draws AE's order, which also gives Convert to
Bezier AE's vertex order. Regression test: `rectangles_start_where_after_effects_starts_them`.
## Still unmeasured
Rendered frame comparisons now have a deterministic `effectcraft_effects::fidelity::compare_frames`
+4 -3
View File
@@ -129,9 +129,10 @@ including files overwritten at the same path and byte size.
and 3D models are not sources, and a composition can't be nested in itself. Alt-dragging onto a
layer is one layer at a time, as in After Effects.
Agents: `layer.replaceSource {"item": id, "layers": [ids]}`.
- **File ▸ Replace Footage ▸ File…** (Ctrl+H) points the item at another file. Pick a numbered
file to replace it with that file's whole sequence (`file.replaceFootage {"path": …,
"sequence": false}` for one frame).
- **File ▸ Replace Footage ▸ File…** (Ctrl+H) points the item at another file. A still stays a
still, and a sequence item keeps bringing the new numbered file's run; pass
`sequence: true` to bring a numbered still's whole run (`file.replaceFootage {"path": …,
"sequence": true}`), or `sequence: false` for one frame.
- **Replace with Precomposition (used N times)**, right-clicking a footage item or solid in the
Project panel, puts the item in a new composition of its size,
pixel aspect and frame rate, in the same folder, and switches every layer in every composition
+7 -2
View File
@@ -63,11 +63,11 @@ are grouped by area; the issue tracker has the rest.
| 4 | **No CI gate on pull requests** | `.github/workflows` has release, packaging-lint, FreeBSD and Windows ARM64 jobs only; `cargo xtask ci` runs locally | Regressions reach main unseen; 49 community PRs landed in one batch (#598) | 4–8 h | G3 · this file |
| 5 | **Playback and scrubbing feel** | #595 stutter on cached frames (partly addressed: nearest-first RAM preview prefetch, cache bars at 1/3 resolution), #304, #504 / #335 audio late or crackling, #586 puppet lag (maintainers asked for details); #329 slow scrubbing (#784) and #554 closed | Preview is where motion designers spend their day | 25–50 h | G5 · [hardware-parity.md](hardware-parity.md) |
| 6 | **Linux and Windows interaction bugs** | Open: #488 rename arrows (hardening landed, reporter asked for details), #492 anchor drag, #268 file drops on Wayland (winit), #613 Direct3D 12 compositor. Now closed: #491, #493, #467, #320, #302, #596 | Many reports come from Linux; some Windows GPUs fail to start the compositor | 25–45 h | G2 · [ui-parity.md](ui-parity.md) |
| 7 | **Animation presets: 54 against 621** | After Effects ships 621 `.ffx` presets (Behaviors, Backgrounds, Shapes, Synthetics, Text, Transitions…); we ship 54 original presets (8 text-animator; 23 Transitions: wipes, clock and fan reveals, blinds, dissolves, card flips, CC wipes, fades, #757; 23 Backgrounds: drifting gradients, fog, cloud and smoke, plasma and magma, cells, hexagons, scrolling grids and checks, pulse rings, bokeh, light sweep, #1096, driven by expressions so they keep moving, not seamless loops) and read only our `.ecpreset`. Missing: Behaviors, Shapes, Synthetics, Image and more Text | Presets are how many designers start; tutorials assume them | 15–35 h (original presets; `.ffx` reading is out of clean-room scope) | G8 · [effects-parity.md](effects-parity.md) |
| 7 | **Animation presets: 76 against 621** | After Effects ships 621 `.ffx` presets (Behaviors, Backgrounds, Shapes, Synthetics, Text, Transitions…); we ship 76 original presets (8 text-animator; 23 Transitions: wipes, clock and fan reveals, blinds, dissolves, card flips, CC wipes, fades, #757; 23 Backgrounds: drifting gradients, fog, cloud and smoke, plasma and magma, cells, hexagons, scrolling grids and checks, pulse rings, bokeh, light sweep, #1096, driven by expressions so they keep moving, not seamless loops; 22 Behaviors: wiggle, shake, float, orbit, bounce, drift, pendulum, spin, pulse, heartbeat, blink, strobe, flicker on transform and opacity, #1510) and read only our `.ecpreset`. Missing: Shapes, Synthetics, Image and more Text | Presets are how many designers start; tutorials assume them | 15–35 h (original presets; `.ffx` reading is out of clean-room scope) | G8 · [effects-parity.md](effects-parity.md) |
| 8 | **Mask and shape bugs** | Open: #1004 mask vertex multi-select, #999 auto-pan, #1042 / #1033 vertex nodes under the bounding box, #959 / #960 shape group toggles and operators. Now closed: #510, #513, #527, #509, #303, #553, #932 (Convert To Bezier Path on a shape layer's parametric path) | Masks are core compositing | 10–20 h | G2 · [target-app-parity.md](target-app-parity.md) |
| 9 | **Effect behaviour bugs** | #580, #463 Curl Noise, #369 Motion Tile output size, #943 Turbulent Displace Evolution, #473 Cryptomatte selection (quoted names with commas fixed, 3b7f1ace; UI and GPU time open), #474 multi-layer EXR (decode shared, faffad3a; f16 storage and cache sizing open). Closed: #526, #514, #533–#536 | Each one breaks a look someone built | 10–20 h | G1/G2 · [effects-parity.md](effects-parity.md) |
| 10 | **Colour pipeline differences** | #340 ProRes 4444 tagged bt709 but sRGB-encoded (open); ACES 2.0 / OCIO 2.5 configs (26.5) missing; now closed: #494 32-bpc blend modes, #495 and #750 Working Gamma 2.4, #546 / #547 viewer colour | Wrong colour in delivery is a client-visible failure | 15–30 h | G1 · [file-format-parity.md](file-format-parity.md) |
| 11 | **Text rendering and layout** | Open: #453 CJK text in comps, #316 paragraph alignment inverted (maintainers' tests show Left/Right correct; asked for a project), #417 soft at Fit (mostly fixed by #444), #550 toolbar font options. Closed: #483, #583 | Text is in almost every motion graphic | 10–20 h | G2 · [ui-parity.md](ui-parity.md) |
| 11 | **Text rendering and layout** | #453 CJK text in comps: the Han fallback now reaches the installed Chinese faces; open: #316 paragraph alignment inverted (maintainers' tests show Left/Right correct; asked for a project), #417 soft at Fit (mostly fixed by #444), #550 toolbar font options. Closed: #483, #583 | Text is in almost every motion graphic | 10–20 h | G2 · [ui-parity.md](ui-parity.md) |
| 12 | **3D interaction and depth** | No 3D transform gizmos (#392, #347); camera aperture (#260); reflections (#261); extruded surface materials beyond Front / Bevel / Side / Back Color (fb5a5e9a; #315 closed); #307 reopened (the viewer's renderer button now shows and switches the renderer, #645); Z position (#489) closed | Advanced 3D is After Effects' growth area since 2023 | 20–35 h | G2 · [ui-parity.md](ui-parity.md) |
| 13 | **3D model formats and materials** | After Effects 26 imports FBX, USD/USDZ, STL besides glTF and OBJ (`usd_plugins`), and Substance 3D materials with mesh displacement (26.0–26.2); we read glTF, OBJ, STL (binary and ASCII, with facet colours) and FBX 7.x (binary and ASCII: meshes, hierarchy, Lambert/Phong materials and textures; no FBX animation or skinning yet) | Product shots and 3D scenes come as FBX/USDZ | 25–45 h | — · [file-format-parity.md](file-format-parity.md) |
| 14 | **Object Matte** (26.2, disk cache in 26.5) | First slice (#783): the Object Matte effect (Effect ▸ Matte) and `objectMatte.apply` turn a point (and optional box) on a reference frame into a matte and follow the object up to 600 frames each way, or segment every frame on its own; Roto Brush's engine, cache and model (MobileSAM from Settings ▸ Roto Brush, classical graph cut without it). Since then: the Object Matte tool in the viewer (click / drag a box, hover crosshair), the box as the model's box prompt, background tracking with progress and cancel, Freeze, and segmentations kept in the disk cache (26.5). Missing: a comparison with After Effects ([plan](fidelity/object-matte.md)) | The new one-click roto | 10–20 h | G7 · [target-app-parity.md](target-app-parity.md) |
@@ -169,6 +169,11 @@ The most important missing piece: it turns every other estimate here into a meas
a Radial Stroke swatch on a selected shape layer and get the Gradient Editor on its Gradient Fill
/ Gradient Stroke Colors (OK saves); without such a gradient the swatch still opens the options,
and the words, Solid swatches and Alt-click are unchanged.
- Regression evidence for [#1306](https://github.com/storytold/effectcraft/issues/1306) (from
#1072): the `ui_expression_values` tests, checked headlessly on Windows, read the colour the
values are painted in. Expression-driven vectors, angles and scalars are red in the Timeline,
Effect Controls and Properties panel, in dark and light themes, hovered too; disabled, removed
and empty expressions show the ordinary colour.
- Regression evidence for the reports of 6 October, checked headlessly on Windows: real pointer
drags in `ui_drag_drop` (Project items into the Timeline between layers and at a time, and onto
the viewer; an effect onto a layer in the viewer: #85, #88, #89), `ui_render_queue` (a template
+1 -1
View File
@@ -23,7 +23,7 @@ monitors are missing, and two GPU start-up failures are open.
| HDR display | HDR monitoring on EDR / HDR displays | tone-mapped SDR view; no EDR output | no HDR output | no | no | Colour engine handles HDR maths (Rec. 2100 PQ/HLG output files) |
| Pen tablets (pressure, tilt) | yes, for Paint and Roto | Brushes panel has pressure options, but no tablet pressure is read | same | same | same | Paint strokes store pressure per vertex; input never fills it |
| Multiple monitors | panels and windows across screens | panels move into their own native windows on any screen (Move Panel to New Window; desktop builds) | same | same (#486) | — | gap 20 |
| Touch / trackpad gestures | pinch zoom in viewers | egui pointer and scroll; no pinch-to-zoom audited | — | — | — | — |
| Touch / trackpad gestures | pinch zoom in viewers | Composition and Layer viewers: two-finger scroll pans, pinch zooms around the pointer (Alt + scroll and the mouse wheel zoom too); Timeline pinch zooms time | two-finger scroll pans once it moves diagonally (a pixel-precise wheel keeps zooming); pinch arrives as Ctrl + scroll and zooms | same | same | No rotate or swipe gestures; tested in `ui_viewer_trackpad.rs` (UI logic, not on hardware) |
| Audio hardware | Core Audio / ASIO / WASAPI, device choice, latency | cpal output; preview sound late after a start offset (#504 open; #554 closed) | cpal | cpal (ALSA) | Web Audio | No input or device latency settings |
| Machine learning acceleration | Apple Neural Engine / GPU for Object Matte, Roto Brush 3 | CPU inference (pure Rust, rayon) | CPU | CPU | CPU | No GPU inference for MobileSAM or MediaPipe |
| Memory management | RAM reserve, disk cache | RAM Reserved, Reduce Cache When Low on Memory, disk cache | same | same | OPFS disk cache | good |
+14 -2
View File
@@ -12,12 +12,12 @@ Guide):
| After Effects | EffectCraft | |
|---|---|---|
| Effect plug-ins (C/C++ SDK, `.aex` / `.plugin`) | [Effect plug-ins](#effect-plug-ins): WebAssembly modules (plug-in API v1) or Rust; the `Plug-ins` folder next to the settings loads at start-up | Different ABI: After Effects SDK plug-ins can't run (G8) |
| Scripts folder: its scripts are listed in File ▸ Scripts | `Scripts/` in the settings folder (File ▸ Scripts ▸ Install Script File… copies a script there), listed in File ▸ Scripts with the bundled samples | Same |
| Scripts folder: its scripts are listed in File ▸ Scripts | `Scripts/` in the settings folder (File ▸ Scripts ▸ Install Script File… copies a script there), listed in File ▸ Scripts with the bundled samples; a subfolder (one level) is a submenu named after it | Same |
| File ▸ Scripts ▸ Run Script File… (`.jsx`, `.jsxbin`) | `.jsx` / `.js` | Gap: `.jsxbin` (an undocumented binary encoding) |
| File ▸ Scripts ▸ Recent Script Files: the 10 most recently run scripts; Cmd+Option+Shift+D (Ctrl+Alt+Shift+D on Windows) reruns one | File ▸ Scripts ▸ Recent Script Files lists the 10 scripts run last (script files and File ▸ Scripts entries, newest first, kept in the settings); Cmd+Alt+Shift+D runs the most recent one again (`file.runScript {"recent": 0}`, a key that can be changed in Edit ▸ Keyboard Shortcuts) | Same |
| ExtendScript preprocessor directives (`#include`, `#target`, `#targetengine`) | `#include` / `//@include` / `#includepath` inline files relative to the script (once each, depth-limited, file gate applies); `#target`, `#targetengine`, `#strict` are ignored | Same |
| `Scripts/Startup` and `Scripts/Shutdown`: run in alphabetical order when the app starts and quits | Not run | Gap |
| `Scripts/ScriptUI Panels`: listed at the bottom of the Window menu, dockable like the app's own panels, `this` is the panel | `Scripts/ScriptUI Panels/` in the settings folder (File ▸ Scripts ▸ Install ScriptUI Panel… copies one there), listed at the bottom of the Window menu, docked like any panel, `this` is the panel; the panels open when the app quits open again at the next launch | Same. Where a panel was docked isn't kept: workspace layouts aren't saved between launches |
| `Scripts/ScriptUI Panels`: listed at the bottom of the Window menu, dockable like the app's own panels, `this` is the panel | `Scripts/ScriptUI Panels/` in the settings folder (File ▸ Scripts ▸ Install ScriptUI Panel… copies one there), listed at the bottom of the Window menu (a subfolder, one level, is a submenu named after it), docked like any panel, `this` is the panel; the panels open when the app quits open again at the next launch | Same. Where a panel was docked isn't kept: workspace layouts aren't saved between launches |
| Keyboard shortcuts for scripts (the Keyboard Shortcuts editor lists the scripts of the Scripts folder) | Shortcuts bind to commands and menu entries; the File ▸ Scripts and Window menu entries of scripts aren't offered | Gap |
| Running a script from the command line (`afterfx -r script.jsx`) | `effectcraft-cli script`, the control channel's and MCP's `script.run` | Same purpose, other entry points |
| `app.settings` (kept in the preferences between sessions) | Kept in `script_settings.json` in the settings folder (`script.settings.get` / `script.settings.save`) | Same |
@@ -41,6 +41,11 @@ journaled and the same as the UI's action. The `effectcraft-script` crate's docs
folder). It is listed in File ▸ Scripts at once (After Effects asks for a restart).
* `file.scripts.list` lists installed and bundled scripts and panels; `file.runScript {name}` runs
one; `file.uninstallScript {name}` removes an installed one.
* A subfolder of `Scripts/` is a submenu of File ▸ Scripts named after the folder, and its scripts
are named `Folder/File.jsx` in `file.scripts.list` and `file.runScript`. One level of folders is
listed; deeper folders are not. `file.uninstallScript` removes only scripts at the top of the
Scripts folders: delete a script in a subfolder yourself. A subfolder may be a link to another
folder (an extension's own), and uninstall never deletes a file through it.
## ScriptUI panels
@@ -52,6 +57,13 @@ other panel. Closing the tab closes the panel (its `onClose` runs). Panels open
quits open again at the next launch (`window.restoreScriptPanels`, which the desktop and web apps
run at start-up; the list is `scriptui_panels.json` in the settings folder).
To group panels, for example the several panels of one extension, make a subfolder of
`Scripts/ScriptUI Panels/` and put them in it: `Scripts/ScriptUI Panels/My Tools/Animate.jsx` is
listed as Window ▸ My Tools ▸ Animate.jsx, its tab is titled Animate, and commands name it
`My Tools/Animate.jsx` (`window.scriptPanel {name}`). One level of folders is listed. Install
ScriptUI Panel… copies a panel to the top of the folder; make the subfolder and move panels into it
yourself, and delete them from it yourself too.
Run from File ▸ Scripts instead, the same script gets no panel, so it should make a floating
palette itself. A minimal panel:
+1
View File
@@ -42,6 +42,7 @@ Commands (CLI, MCP, control channel):
- `startup.windowGraphics`: Window Graphics (`auto` / `gl`), what the desktop window draws with from the next launch. `auto` lets the platform pick (DirectX 12, Vulkan or Metal); `gl` uses OpenGL, with CPU compositing, for graphics drivers that crash with the others. A launch leaves a `launch-pending` marker in the settings folder until its window has been drawing for a few seconds (or the app quits normally before that); a launch that stops earlier (a graphics driver crashing as the window opens or as its surface is re-created) leaves the marker behind, and the next launch switches to `gl` and says so (not on macOS, which has no OpenGL backend). `WGPU_BACKEND` overrides both.
- `project.useTemplate`: New Project Loads Template
- `project.templatePath`: Template Project
- `project.compactProjectFiles`: Save Compact Project Files (default off). Off: File ▸ Save writes `.ecproj` files as indented JSON, one value per line, easy to read and diff in version control. On: compact JSON on one line, smaller and faster to save and open. Either opens the same; auto-saves, XML copies and template packages are not affected.
- `autoSave.enabled`: Automatically Save Projects
- `autoSave.intervalMinutes`: Save Every
- `autoSave.maxVersions`: Maximum Project Versions
+1 -1
View File
@@ -22,7 +22,7 @@ our own scripts; no real-world script library has been run against both apps.
| `$.evalFile`, `BridgeTalk` | Evaluate another script file; identify the host application and message other Adobe applications | `$.evalFile` evaluates a file relative to the calling file (nested at most 32 deep, under the scripts' file gate) and returns its last value; `BridgeTalk` names the host honestly (`appName` `effectcraft`, its version); messaging other applications throws "not supported" | partial | `tests_evalfile.rs` (#760); no inter-application messaging |
| ScriptUI | Dialogs, palettes, dockable panels, resource strings, `onDraw` / ScriptUIGraphics, mouse events | Same, dockable panels reopen at launch | good | `ui_scriptui`, `tests_ui*` |
| `app.scheduleTask` / `cancelTask` | One-shot and repeating tasks (polling, panel refresh) until `cancelTask` | Same: repeating tasks run every `delay` ms (at least 50 ms, at most 64 pending per script) until `cancelTask`, until they throw (reported once, then cancelled), or until their owner goes: a script's last window closing, or the project closing for scripts without windows; frontends tick them every frame, agents with `script.runTasks` | good | `tests_tasks.rs` (#773); one-shot tasks of a script file still run right after it, not after their delay |
| Scripts menu, ScriptUI Panels folder | yes | yes | same | [plugins.md](plugins.md) |
| Scripts menu, ScriptUI Panels folder | yes | yes; a subfolder (one level) of either folder is a submenu named after it | same | [plugins.md](plugins.md) |
| Startup / Shutdown folders | yes | not run | missing | plugins.md |
| `#include`, `#target` directives | yes | `#include` / `//@include` / `#includepath` inline files relative to the including script (once each, nested at most 32 deep, under the scripts' file gate; errors name the included file and line); `#target`, `#targetengine`, `#strict` are ignored | good | `tests_include.rs` (#759); installed scripts and panels have no folder of their own, so they include from the project's folder or absolute paths |
| `.jsxbin` | yes | no | out of scope | No public specification |
+1 -1
View File
@@ -120,7 +120,7 @@ compositing and effects dominate; audio is light). Hours are to full parity for
| Expressions and scripting | 7% | 93% | 65% | 20–40 | `.jsxbin`, Startup/Shutdown folders, script shortcuts (`#include` landed, #759); external editor (#364); see [scripting-parity.md](scripting-parity.md) |
| Shapes, masks and roto | 9% | 93% | 45% | 30–50 | Open mask and shape issues (#1004, #999, #1042, #932); #510, #513, #527, #509 and #303 are closed; paste SVG/AI as shape layers (26.3; SVG markup pastes since #738) |
| Text and typography | 7% | 94% | 50% | 20–35 | CJK text in comps renders wrongly (#453); paragraph alignment inverted (#316); soft text at Fit (#417, mostly fixed by #444); toolbar font options (#550); #483 and #583 are closed; variable-font filter landed (#741) |
| Effects and animation presets | 14% | 85% | 40% | 70–130 | All 306 effects exist, but rendered output is unmeasured against After Effects; 54 built-in presets (text, Transitions, Backgrounds) against After Effects' 621 ([effects-parity.md](effects-parity.md)); open effect bugs (#580 crash, #463, #369, #473, #474; #514 and #526 are closed); effect colour labels (26.5); see [effects-parity.md](effects-parity.md) |
| Effects and animation presets | 14% | 85% | 40% | 70–130 | All 306 effects exist, but rendered output is unmeasured against After Effects; 76 built-in presets (text, Transitions, Backgrounds, Behaviors) against After Effects' 621 ([effects-parity.md](effects-parity.md)); open effect bugs (#580 crash, #463, #369, #473, #474; #514 and #526 are closed); effect colour labels (26.5); see [effects-parity.md](effects-parity.md) |
| 3D (Classic and Advanced) | 6% | 82% | 35% | 35–65 | No FBX/USD/STL models, no Substance materials, mesh displacement unverified; no 3D transform gizmos (#392, #347); reflections (#261); camera aperture (#260); #489 and #315 are closed |
| Tracking and AI-assisted tools | 5% | 80% | 35% | 35–70 | Object Matte (26.2) has the effect, the viewer tool (click / box), background tracking, Freeze and the 26.5 disk cache, not yet compared with After Effects; Roto Brush (MobileSAM) and face tracking (MediaPipe) unmeasured against After Effects; camera-tracker view (#505) |
| Preview and cache | 7% | 92% | 40% | 25–50 | Playback stutters on cached frames (#595, #304); late audio (#504, #335); #329 and #554 are closed; no benchmark against After Effects |
+4
View File
@@ -5,6 +5,10 @@ cargo test --workspace # everything
cargo xtask ci # fmt, clippy -D warnings, tests, layers, assets, wasm
```
The `dev` profile keeps debug info to line tables (fast builds, readable backtraces). To debug
interactively — local variables, parameters, column breakpoints — build with the `dev-full`
profile: `cargo run --profile dev-full -p effectcraft -- --demo` (see `CONTRIBUTING.md`).
`cargo xtask ci` builds in the dev profile and runs the tests with `cargo nextest run` (plus
`cargo test --doc`) when `cargo-nextest` is installed, and with `cargo test` otherwise. It then
runs `missing_translations_report` with `--nocapture`, which prints the interface strings each
+19
View File
@@ -242,6 +242,25 @@ Window ▸ Media Browser browses a virtual tree (`src/browse.rs`):
`mediaBrowser.list` reports the places and these actions; `mediaBrowser.action {action}` runs one.
### Startup project
By default the page opens the last visit's project, and the demo project on a first visit. A
`startup` key in `effectcraft-config.json` (next to `index.html`) changes that:
```json
{"startup":"empty"}
```
| Value | On load |
|---|---|
| `"last"` (or no key) | the last visit's project; the demo project on a first visit |
| `"empty"` | the last visit's project; a blank project on a first visit, never the demo |
| `"demo"` | the demo project, whatever the last visit left; the session is not saved, so the last visit's project stays as it was |
The `?empty` and `?demo` URL flags still win. A missing, invalid or non-JSON file, or an unknown
value, is the default. The file is fetched once per page load; serve it with
`Cache-Control: no-cache` (the service worker never caches it).
### Audio
Audio preview goes through **Web Audio**: an AudioWorklet (`audio-worklet.js`; a