Advanced 3D comps render their 3D runs as one scene of world-space triangles: model and
primitive layers (meshes, skinning, animation clips by layer time), extruded text/shape
layers, and textured cards for every other layer with their Material Options. Shading is
physically based in linear light: Cook-Torrance GGX + Lambert for parallel/spot/point lights
(AE falloff and cone), ambient lights, image-based light from an Environment light's layer
(equirectangular; cosine irradiance map + roughness mip chain + split-sum BRDF fit), and
PCF shadow maps (spot/parallel/point cube faces). 2x2 SSAA, depth of field as a gather blur
by circle of confusion, back to the working encoding.
The software rasteriser (depth buffer, top-left rule, perspective-correct, opaque visibility
then sorted transparent blending) is the reference and CI path; effectcraft-gpu implements
Accelerator::raster_3d with a wgpu render pipeline (advanced3d.wgsl) and matches it within
tolerance. Environment layers are hidden from both renderers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New crate effectcraft-model (L2): glTF 2.0 (.gltf/.glb, data URIs, external buffers and
images, PBR metallic-roughness with base/MR/normal/occlusion/emissive textures, node
hierarchy, skins, step/linear/cubic animations) and OBJ/MTL import, parametric primitives,
extruded/bevelled outline meshes and polygon triangulation with holes.
Project: FootageKind::Model, LayerSource::Model/Primitive, Layer::environment, Geometry and
Material Options builders, Environment light Source. Media probes and loads models
(FootageSource::model). Engine: layer.newModel, layer.new3dPrimitive/newPrimitive,
comp.renderer, layer.environment, material.set/reset/duplicateAssign/revealSource (stubs
replaced); File > Import and layer.addItem pick up models. Hand-written fixtures with
attribution sidecars.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeps the GPU's public placement() API (motion-blur sample count now
shared via mb_samples) and the footprint-limited quantisation; the CPU
fallback helpers draw_layer_cpu / draw_collapsed_cpu pass blank = false.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Auto backend keeps comps with 3D layers on the CPU compositor (3D runs are
still CPU-composited; the GPU round-trip made them slower).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Region-of-interest frames render on the CPU path; the viewer keeps main's
frame drawing (Show Channel, exposure, ROI) and reads GPU-composited frames
back. Keeping frames as native egui-wgpu textures stays implemented behind
RenderSource::gpu_display (off).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8/16 bpc no longer re-quantises the whole comp after every layer: 2D layers
report the canvas region they can change (transformed buffer bounds plus the
resampling margin; stencil modes, motion blur, perspective, styles,
adjustment and 3D runs fall back to the full canvas), and only that region
is quantised. A quantised buffer placed pixel-exactly (integer translation,
Normal, 100%) over a blank canvas needs nothing; invisible layers need
nothing; collapsed precomps quantise per nested layer. The redundant final
comp pass is skipped unless the comp is re-encoded. Solids are filled with a
pre-quantised colour and skip the source pass unless masked. Row-parallel
vectorisable kernel. Debug builds verify every pixel outside the region is
already quantised (whole workspace test suite run with 8 bpc forced: no
violations).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New L3 crate effectcraft-gpu: a wgpu compute backend for the per-frame
compositing walk (layer transforms with the CPU's nearest/bilinear/bicubic
sampling and minification pre-filter, motion-blur accumulation, track
mattes, Preserve Transparency, layer style passes and knockout, all 38
blend modes as a WGSL port of color::blend, 8/16 bpc clamp+quantise,
colour-space conversions). 3D runs, adjustment and wireframe layers run on
the CPU between GPU steps. GPU effects with the CPU semantics: Gaussian,
Fast Box and Directional Blur, Glow, Levels, Curves, Hue/Saturation, Tint,
Fill, Gradient Ramp, Fractal Noise, Drop Shadow, Brightness & Contrast,
Exposure, Invert, Transform (registry gpu badge from effects::GPU_EFFECTS).
render: Backend (Cpu/Gpu/Auto) in RenderOpts, the Accelerator trait and
Renderer::accel, GPU effect chains in the effect stack, hooks for the GPU
walk. Project setting gpu_acceleration (Mercury GPU Acceleration / Software
Only), render.backend command, file.projectSettings renderer. Session and
Render Queue exports carry the accelerator. CLI --gpu and bench --gpu.
Tests compare GPU vs CPU renders (blend modes x bit depths, transforms,
masks, mattes, motion blur, colour management, styles, collapse,
adjustment fallback, every GPU effect) and skip without an adapter.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TextDoc gains character style runs (font, size, colours, tracking, leading,
baseline shift, h/v scale, tsume, faux styles, caps, super/subscript, kerning
metrics/optical/manual, ligatures) and per-paragraph settings (justification
with last-line modes, indents, space before/after, direction, composer,
hanging punctuation), serde-compatible with single-style documents. Layout
handles mixed runs on a line (shared baseline, largest leading), every-line
composer, editing geometry (carets, hit testing, selection quads) and basic
vertical type. Renderer colours per run. Expressions get the sourceText style
API (style, getStyleAt, createStyle, setters with character ranges).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Composition viewer interaction parity with After Effects:
- Snapping (View > Snapping, the Tools bar checkbox, Cmd/Ctrl inverts while
dragging): layer edges, centres and anchor points, mask and shape-path
vertices, comp edges/centre, guides (Snap to Guides) and grid (Snap to
Grid), with highlighted target box / line / cross feedback. Pure maths in
engine::viewer (nearest target within tolerance, priority ties).
- Rulers (Cmd+R) with live pointer markers and a draggable zero point;
guides dragged out of the rulers, moved in the viewer, dragged off to
remove (view.moveGuide / view.removeGuide), lock respected; proportional
grid.
- Show Channel (RGB/Red/Green/Blue/Alpha/RGB Straight, Colorize), exposure
(+/- stops in linear light, reset), Take Snapshot (Shift+F5) with a stored
render and Show Snapshot (F5 held / button held), Fast Previews (Off,
Adaptive Resolution, Draft, Fast Draft, Wireframe), Resolution > Custom.
- Region of interest: drawn in the viewer; RenderOpts.roi renders only that
rectangle (crop of the full render, tested), Crop Comp to ROI uses it.
- Bottom bar in AE 2026 order ("136.1 %", "(Full)").
- Shape Pen (SHP-1): Pen on a shape layer / nothing selected draws Shape n
groups (Path 1, Stroke 1, Fill 1) via shape.newPath; the G slot cycles
Add / Delete / Convert Vertex and Mask Feather; mask.* vertex commands
work on shape paths; mask.insertVertex / mask.convertVertex; marquee
vertex selection, Alt-drag/click converts, double-click free transform
box (path.freeTransform on masks and shape paths).
- Motion paths (ANM-3): keyframe dots and spatial tangent handles, dragging
keys (keys.set) and tangents (keys.setSpatialTangents), parented layers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collapsed precomps draw their layers straight into the parent with
concatenated transforms and multiplied opacity (nested blend modes and
adjustment layers act on the parent); a non-Normal mode, track matte or
Preserve Transparency composites the collapsed result as a whole; masks,
effects or styles force a flattened render. Nested 3D layers use the
parent's camera and lights and join the parent's 3D run when the precomp
layer is 3D. Continuously Rasterize re-rasterises text and shape layers at
their on-screen scale. Draft quality samples nearest-neighbour, Wireframe
draws the layer bounds. layer.slip / slipBack (Alt+PageUp) / slipForward
(Alt+PageDown) and dragging the timeline source bar slip the source under
fixed in/out points, clamped to the source duration; undoable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Frame Mix and Pixel Motion for footage and precomp layers when the layer's
Frame Blending switch and the comp's Enable Frame Blending are on and the
source time falls between source frames (rate conform, time stretch, time
remap). Exact frame positions from tick arithmetic. Pixel Motion is a
hierarchical block-matching optical flow (SAD search, sub-pixel parabola
fit, vector median) with a bidirectional warp-blend (raster::flow). Blended
footage frames are cached.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8/16 bpc projects clamp and quantise layer pixels after source/masks and
every effect, and the comp after every layer (16 bpc = 0..32768); 32 bpc
keeps over-range values. Blend modes without HDR support clamp their inputs
in 32 bpc. Working space (sRGB, Rec. 709, Rec. 2020, Display P3; matrices
derived from the published primaries), footage colour profiles from stream
metadata or Interpret Footage, Linearize Working Space and Blend Colors Using
1.0 Gamma. Layer cache keys include the colour settings. Project panel depth
button: click cycles, Alt-click opens Project Settings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Implement 20 formerly disabled menu stubs: Look at Selected/All Layers
(2D viewer fit, 3D view cameras, active camera), Switch View Layout (1/2/4
views in the viewer) and Share View Options, Create Camera from 3D View,
Add Output Module (every module encodes the item's frames), Pre-render
(queue + Import & Replace Usage post-render action), Save Current Preview,
RotoBezier / Convert To Bezier Path, Group / Ungroup Shapes, Set First
Vertex, Free Transform Points, mask Motion Blur (sub-frame mask samples),
Feather Falloff (smooth/linear), Hide Locked Masks, and workspace
save / save as / edit. Tests incl. undo.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A real Settings model in the engine (`engine::prefs`): serde, versioned, dotted keys
(`general.undoLevels`, `labels.3.name`), migration of older layouts, unknown keys kept,
persisted through a `ConfigStore` trait (platform config dir on the desktop; MemoryConfig
for tests; the web app can back it with localStorage).
- After Effects 2026 pages (General … Scripting & Expressions) as a schema shared by the
dialog, `prefs.pages` and docs/preferences.md (rows not wired yet are listed there and
kept in step by a test).
- Commands: prefs.get, prefs.set {key, value | values}, prefs.reset {page?}, prefs.open
{page?}, prefs.pages.
- Wired settings: undo levels, path point size, tool tips, layers at comp start / CTI,
default spatial interpolation linear, Home screen on launch, new project template,
rendering progress in Info, adaptive resolution limit, cache when idle, fast previews,
theme + UI brightness, label names/colours everywhere (Label menu, timeline, project,
render queue, viewer), label colour for handles, grid colour/spacing/subdivisions, guide
colour, safe margins, still footage duration, sequence fps, audio output device and
output mapping, layer / footage / preview cache budgets, default 3D renderer.
- Settings dialog: AE's left page list + right content, OK / Cancel (restores) / Previous /
Next, every widget with an automation id.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paint: non-destructive Paint effect with Brush/Clone/Eraser stroke groups
(Path, Stroke Options, Transform, Duration spans, Write On End keys),
dab rasterizer (diameter/angle/roundness/hardness/spacing/flow/opacity,
pressure dynamics, modes, channels), eraser modes, clone from layer/time,
cache keys include visible strokes. paint.stroke/options/brushPreset/
setCloneSource/removeStroke/presets. Layer panel with paint tools, Paint
and Brushes panels.
Puppet: Puppet effect with meshes and Position/Advanced/Bend/Starch/Overlap
pins; alpha contour -> expansion (exact EDT) -> simplified outline ->
Delaunay with constraint recovery; ARAP (Igarashi 2005) two-step solve with
CG; anti-aliased texture-mapped rendering with overlap depth. puppet.addPin/
movePin/setPin/removePin/mesh/info. Puppet tools in the viewer with mesh
overlay and header options.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Angle dial with revolutions (0x+45.0°), point crosshair + on-viewer effect
point controls via a small viewer hook, colour eyedropper sampling the
composited frame, Curves graph editor and Levels histogram/triangles bound
to their params, layer params with a Source/Masks/Effects & Masks companion,
mask (Path) popups, keyframe navigator, drag-to-reorder effect headers.
Engine: effect.copy / effect.paste / effect.reset; effect.remove takes the
selection; Edit > Copy/Paste/Duplicate/Clear act on selected effects; all
undoable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New L2 crate effectcraft-track: per-point feature/search region tracking with
pyramid normalized cross-correlation and Lucas-Kanade (similarity + gain/bias)
sub-pixel refinement, confidence, adapt/stop/extrapolate/continue actions,
RGB/luminance/saturation channels, blur/enhance pre-processing, and
homography/affine/similarity solves.
Project: Motion Trackers > Tracker n > Track Point n property tree
(GroupKind::Tracker with type, target and options). Engine: track.* commands
(new/motion/stabilize/property/select/setType/setTarget/options/setPoint/
analyze/stop/apply/reset/delete/status), background or blocking analysis
job with progress and cancel (one undo step), apply to Transform, Stabilize
and Corner Pin. Renderer::layer_source for the tracker's frames.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Animation ▸ Animate Text (Enable Per-character 3D, every animator property,
Fill/Stroke Color RGB/Hue/Saturation/Brightness/Opacity submenus, Line Anchor,
Character Value), Add Text Selector ▸ Range/Wiggly/Expression (text.addSelector
now supports all kinds) and Text Animation Presets after Browse Presets, in the
engine menu tree. Stubs replaced: text.perCharacter3d, animatorLineAnchor,
animatorCharacterValue, addWigglySelector, addExpressionSelector.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also: render uses web_time::Instant in layer styles; renderQueue.add documents
its render-settings/output-module keys so checked execution accepts them (+test).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- apps/effectcraft-web (L6): eframe web runner booting the shared EffectcraftApp with a browser
session: in-memory file table, <input type=file> pickers and drag-and-drop for open/import,
downloads for Save and Render Queue outputs (several files as one stored .zip), and the
window.effectcraft promise API over the control channel. Demo project on start.
- wasm32 runtime fixes: web-time for Instant/SystemTime (render, engine, export, ui-egui);
Frames renders queued jobs on the UI thread (Frames::pump) instead of a thread pool;
Render Queue runs inline; relative outputs resolve under / ; control PNGs inline.
- effectcraft-export: per-job output Sink (files to memory instead of disk); host FileExporter
carries it; test that sink exports touch no file.
- xtask web [--dev] [--serve PORT] (wasm-bindgen 0.2.129, optional wasm-opt, COOP/COEP server);
effectcraft-web in WEB_CRATES. docs/web.md, headless Chrome smoke test, favicon (+attribution).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve conflicts; register playback.audio (Composition > Preview > Audio)
as a real frontend command replacing its stub; layer_at uses content_buf.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Audio effects (Backwards, Bass & Treble, Delay, Flange & Chorus,
High-Low Pass, Modulator, Parametric EQ, Reverb, Stereo Mixer, Tone)
applied per layer in the comp mixdown, so export and preview share them.
RBJ-cookbook biquads, Schroeder/Moorer reverb, modulated delay lines;
stateless per block via pre-roll. Tone makes any layer audible.
- Real-time audio preview: ui-egui AudioFeed/AudioPlayback (feeder thread
mixing ahead) behind an AudioDevice hook; the desktop app implements it
with cpal (L6). Audio clock drives playback (consumed frames minus device
latency, looped through the work area). Preview panel Include Audio /
playback.muteAudio.
- Audio panel VU meters with peak hold and clip indicators; selected
layer's Audio Levels.
- Timeline Audio > Waveform for footage with audio (L reveals Audio
Levels, LL the Waveform), drawn from a cached per-item peak summary.
- ui.playback reports audio state and meter levels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Timeline: Text group 'Animate:' and animator 'Add:' pop-ups (properties and
Range/Wiggly/Expression selectors), selectors/properties listed directly under
each animator, Path Options ▸ Path lists the layer's masks and hides the
other path options until one is chosen. Properties panel Add Animator lists
every animator property plus Enable Per-character 3D, and a Presets menu.
Animation menu: Animate Text, Add Text Selector, Text Animation Presets.
Expression Selector result replaces the selection (no Mode); selector popups
are non-keyframable like AE.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Range selector complete (units, based on, modes, shapes, smoothness, ease
high/low, randomize), Wiggly and Expression selectors combined by mode;
all AE animator properties incl. line anchor, character offset/value,
skew axis, tracking type, fill/stroke HSB+opacity, blur; More Options
(anchor grouping, fill & stroke order, inter-character blending); Path
Options (text on a mask path); per-character 3D rendered as planes in
the 3D compositor; text animation commands and original presets.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Layer Styles (Drop Shadow, Inner Shadow, Outer/Inner Glow, Bevel and Emboss, Satin,
Color/Gradient Overlay, Stroke) and Blending Options (Global Light, Fill Opacity, R/G/B,
Knockout, Blend Interior Styles as Group) as a layerStyles property group with AE names,
order and defaults. Rendered in layer space after effects (exterior passes composite with
their own blend modes below the body), cached per pass, scaled with render resolution.
Commands layer.style.* (menu Layer > Layer Styles), Global Light shared per comp, timeline
eye switches.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
EffectHost gains self_at (the layer's source + masks + first N effects at
another layer time) and layer_at (another layer at another comp time);
EffectEnv carries the effect's stack index. The renderer serves these via
Renderer::layer_input, cached in the layer cache under input keys (footage
included, keyed by source time) with a depth guard against recursion.
Time effects are TIME_DEPENDENT so processed-layer keys fold in layer
time; layers with time effects and expressions are not cached.
Also registers the Effect > Audio specs (DSP in effects::audio_fx; the
mixdown integration follows in M11.2). Audio effects are skipped by the
video effect pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolve conflicts with perf (layer cache / Arc<Buf>), export (RenderOpts.view: None
for output), timeline (separate dimensions + one-node POI visibility), mask/pen
viewer gestures (alongside camera tools and view-aware layer matrices), key
dialogs (alongside Camera/Light Settings), render queue + MCP command modules.
3D path tangent honours separated position; DOF blurs a per-frame copy so cached
layer buffers stay shared. New test: cached renders match uncached after camera,
focus, light and 3D view changes. Fix a clone-on-copy lint from main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- layer.autoOrient {mode: off|alongPath|towardsCamera|towardsPointOfInterest}
(Layer > Transform > Auto-Orient…, Cmd+Alt+O); render support for Orient
Along Path (2D and 3D) and Orient Towards Camera (with recursion guard).
- Layers parented to cameras/lights follow their full rotation.
- Draft 3D turns off lights and shadows (and DOF).
- Turning the 3D switch off discards Z values, Orientation and X/Y Rotation.
- Tests for all of the above.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Engine (all commands with undo/redo + tests in engine/src/tests_timeline.rs):
- keys.copy/paste (Edit Copy/Cut/Paste route to keys when keys are selected;
relative timing kept, paste at CTI, cross-property paste of same type),
keys.selectAll (Cmd+Alt+A), keys.nudge* (Alt+Arrow, Alt+Shift+Arrow),
keys.set / keys.setEase (graph editor drags), keys.info (dialog data),
keys.timeReverse; keys.interpolation gains continuous/auto Bezier, spatial
interpolation and roving; keys.velocity takes per-dimension values and
Continuous.
- Roving keys (keyframe::retime_roving): constant speed along the motion path.
- Layer > Time: layer.timeStretch (percent|duration, hold in/current/out),
layer.timeReverse (Cmd+Alt+R), layer.enableTimeRemap (Cmd+Alt+T),
layer.freezeFrame, layer.freezeOnLastFrame; render uses Time Remap as the
source time for precomp/footage layers.
- prop.separateDimensions (X/Y/Z Position; prop.set on Position writes them).
- layer.setParent compensates so the layer stays in place (compensate:false
to opt out).
- prop.pickWhip writes AE-style reference expressions (thisComp.layer(..),
effect(..)(..), mask(..).maskFeather, temp=…;[temp,temp], …[0]).
- prop.setExpression: syntax errors keep the text but disable the expression.
- expr: arrays of properties returned by an expression are resolved.
UI: Keyframe Velocity, Keyframe Interpolation and Time Stretch dialogs;
Graph Editor (value + speed graphs, draggable keys and Bezier/influence
handles, auto-zoom, fit, show selected, separate dimensions, hold/linear/
auto Bezier/Easy Ease buttons); key + layer context menus. Clears the
workspace clippy -D warnings failures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- render: LayerCache (content-keyed processed-layer cache: source -> masks ->
effects, keyed by a hash of the layer's non-transform props evaluated at the
frame time, solid item, switches, scale; time folded in for clock-driven
effects / wiggle paths). Static and transform-only layers render once;
edits only re-render the edited layer. Session + UI frame jobs share it.
- effects: TIME_DEPENDENT list + test that every clock-reading effect is listed.
- raster: affine composite fast path (per-row span clipping, unchecked interior
bilinear, integer-translation copies, inlined Normal blend), checked against
composite_warp_reference; box/Gaussian blur without transposes, two
ping-pong buffers; accumulate_warp so motion blur sums into one buffer
instead of one full-frame image per sub-sample.
- ui: viewer frame is urgent and jumps the prefetch queue; stale-revision jobs
are dropped; paused prefetch waits for the viewer frame; "Frame Render Time"
reports viewer frames only; parallel f32 -> Color32 conversion.
- cli: `frame --bench N` (per-layer/per-effect timings, wall + CPU time) and
`frame --bench-play N` (consecutive frames with/without the layer cache).
- tests: cache transparency (scrub, edits of values/keys/effects/masks/
solids/text/timing), hit/miss accounting, time-dependent effects, demo
cached == uncached, fast warp/blur/accumulate == reference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>