Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/Features/3D-Layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Create mesh clips from the Media Panel via `+ Add > Mesh` or the context menu:
### Preview Scene Gizmo

- Selected scene objects expose a viewport transform gizmo for Move, Rotate, and Scale.
- The Preview scene-handle toggle hides both the React hit handles and the native WebGPU gizmo pass, so disabling it removes the visible axis gizmo from the preview.
- Move, Rotate, and Scale visuals are drawn by the native WebGPU scene gizmo pass from the selected object's local transform basis. The React overlay only supplies hit targets and the mode toolbar.
- Move and Scale use larger Unreal-style colored local-axis handles with dark outlines and a white center hub. Hovering an axis brightens and thickens that native gizmo handle. Dragging the center hub moves freely in the preview plane for Move and applies proportional uniform scale for Scale.
- Rotate mode draws larger screen-space stable colored rings from the X, Y, and Z local rotation planes, so the ring orientation changes with object rotation and the scene view without the rings visually growing or shrinking. The invisible SVG hit targets are generated from the same projected 3D ring points, and hover/drag chooses the nearest ring instead of whichever SVG stroke is visually on top.
Expand Down
4 changes: 2 additions & 2 deletions docs/Features/Export.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ FCPXML is exposed as a selectable export container for NLE interchange.

`FrameExporter` is used for both the WebCodecs and HTMLVideo export buttons.

Canvas-backed sources such as text, solids, and Lottie are re-rendered for every export frame before capture, so the exported frame matches the current timeline time instead of reusing a stale first-frame texture.
Canvas-backed sources such as text, solids, and Lottie are re-rendered for every export frame before capture, so the exported frame matches the current timeline time instead of reusing a stale first-frame texture. Motion shape clips are built as `motion` layer sources and rendered by the WebGPU motion renderer at export frame time before compositing.

### Fast Mode

Expand Down Expand Up @@ -272,7 +272,7 @@ The PNG frame export action reads the current composited frame from the GPU.
1. Prepare clips and runtimes for the selected export mode.
2. Seek all clips to each export time.
3. Build layers for that frame.
4. Render through the GPU engine.
4. Render procedural motion shape textures and nested compositions, then composite through the GPU engine.
5. Capture a `VideoFrame` from the export canvas when possible, otherwise fall back to pixel readback.
6. Encode and mux the file.

Expand Down
10 changes: 10 additions & 0 deletions docs/Features/GPU-Engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ The engine currently supports:
- Images and canvases are copied into `rgba8unorm` GPU textures.
- Cached image views are reused when possible.

### Motion Shapes

- `motion-shape` clips render through `src/engine/motion/MotionRenderer.ts`.
- Rectangle and ellipse primitives are drawn with analytic WGSL SDFs into transparent `rgba8unorm` textures.
- Grid-replicated motion shapes use a per-shape instance buffer and instanced draws in the same shader path, capped at 100 instances for the current MVP.
- The resulting texture view is composited through the normal `CompositorPipeline`, so masks, effects, blend mode, nested comps, preview targets, and export share the same downstream path.

### Masks

- Mask textures are uploaded per layer.
Expand Down Expand Up @@ -155,6 +162,7 @@ Runtime flags are exposed on `window.__ENGINE_FLAGS__`.
- `useLiveSlotTrigger` swaps slot-grid clicks from editor-open behavior to direct live triggering.
- `useWarmSlotDecks` prepares reusable slot-owned live decks for faster layer adoption.
- `use3DLayers` and `useGaussianSplat` are enabled in this branch.
- `useMotionDesignSystem` exists for the motion-design rollout; current rectangle/ellipse render plumbing is additive for `motion-shape` clips.

---

Expand Down Expand Up @@ -192,6 +200,8 @@ Key implementation files:
- `src/engine/render/RenderDispatcher.ts`
- `src/engine/render/RenderLoop.ts`
- `src/engine/render/htmlVideoPreviewFallback.ts`
- `src/engine/motion/MotionRenderer.ts`
- `src/engine/motion/shaders/motionShapes.wgsl`
- `src/engine/pipeline/OutputPipeline.ts`
- `src/engine/managers/ExportCanvasManager.ts`
- `src/engine/texture/ScrubbingCache.ts`
Expand Down
26 changes: 25 additions & 1 deletion docs/Features/Keyframes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[<- Back to Index](./README.md)

The keyframe system animates clip properties over time using per-clip keyframe maps, curve editors, and Bezier handles. It supports transform properties, speed, numeric effect parameters, mask properties, and vector-animation state/input properties.
The keyframe system animates clip properties over time using per-clip keyframe maps, curve editors, and Bezier handles. It supports transform properties, speed, numeric effect parameters, mask properties, vector-animation state/input properties, and numeric motion-shape properties.

---

Expand Down Expand Up @@ -82,6 +82,29 @@ lottieInput.{stateMachine}.{input}

`lottieState.*` keyframes are discrete named states. They render as blue diamonds and stepped curves because a state change should hold until the next state keyframe, not ease between values. Boolean and numeric `lottieInput.*` properties use the normal stopwatch/keyframe workflow.

### Motion Shape Properties

Motion shape clips use flat property paths from the property registry:

```text
shape.size.w
shape.size.h
shape.cornerRadius
appearance.{appearanceId}.opacity
appearance.{appearanceId}.color.r
appearance.{appearanceId}.color.g
appearance.{appearanceId}.color.b
appearance.{appearanceId}.color.a
appearance.{appearanceId}.stroke.width
replicator.count.x
replicator.count.y
replicator.spacing.x
replicator.spacing.y
replicator.offset.opacity
```

Numeric motion properties are interpolated before `MotionRenderer` draws the shape texture, so preview, nested compositions, and export evaluate the same frame state. Enum-like fields such as primitive and stroke alignment are currently static controls.

### Visibility Rules

- 2D clips hide `rotation.x`, `rotation.y`, `position.z`, and `scale.z` in the timeline UI.
Expand Down Expand Up @@ -110,6 +133,7 @@ The diamond button writes a keyframe at the playhead. If a keyframe already exis
- `scale.all` does not overwrite `scale.x`, `scale.y`, or `scale.z`; render, export, and scene-gizmo paths multiply it into the final visible scale only at evaluation time.
- Camera stopwatch buttons are per camera value. FOV and mm both write `camera.fov`; Near, Far, Resolution X, and Resolution Y write their own camera properties.
- Mask panel stopwatch buttons are available for Feather, Feather Quality, Position X/Y, and the whole Mask Path.
- Motion shape stopwatch buttons are available for size, corner radius, fill opacity, and stroke width in the Motion tab.

### Recording Mode

Expand Down
31 changes: 31 additions & 0 deletions docs/Features/Motion-Design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
[Back to Docs](./README.md)

# Motion Design

Status: shape MVP in progress. The data model, property registry, shape editing tab, GPU rectangle/ellipse renderer, persistence, nested composition path, and export layer path are wired.

The motion design system follows `docs/plans/motion-design-system-plan.md`. It is native MasterSelects timeline content, not an embedded external editor.

## Current Scope

- `src/types/motionDesign.ts` defines versioned motion layer data for shape, null, adjustment, and group layers.
- `TimelineSourceType`, `TimelineClip`, `SerializableClip`, and project clip persistence accept `motion-shape`, `motion-null`, and `motion-adjustment`.
- Motion definitions are plain JSON and survive timeline/project serialization.
- `src/services/properties/PropertyRegistry.ts` describes transform, effect, color, mask, vector-animation, and motion properties without owning Zustand state.
- `src/stores/timeline/motionClipSlice.ts` can create rectangle/ellipse shape clips, null clips, adjustment clips, update motion definitions, and convert solid clips to motion rectangle clips.
- `src/components/panels/properties/MotionShapeTab.tsx` exposes primitive, size, corner radius, fill, and stroke controls for motion shape clips.
- Video track-header context menus can create Motion Rectangle and Motion Ellipse clips at the playhead.
- Solid clip context menus can convert the selected solid to a motion shape while preserving its clip id and timing.
- The Motion tab exposes a first Grid Replicator section with enable, count, spacing, and opacity fade controls.
- `src/engine/motion/MotionRenderer.ts` renders rectangle and ellipse primitives into transparent `rgba8unorm` textures using analytic WGSL SDFs.
- The renderer supports grid-replicated rectangle/ellipse shapes through a per-shape instance buffer and instanced draws, capped at 100 instances for the current MVP.
- `LayerBuilderService`, `NestedCompRenderer`, `RenderDispatcher`, and `ExportLayerBuilder` pass motion shape layers through the same compositor path as image/text/video textures.
- Numeric motion properties are evaluated through the keyframe store via the property registry before rendering.

## Not Yet Implemented

- Replicators have a grid MVP for shape clips, but no random/noise modifiers, radial/linear layouts, falloff, or direct media replicators are wired yet.
- Texture fills, gradients, appearance blend modes, polygon/star rendering, viewport motion paths, and graph mode are not implemented yet.
- Adjustment layers remain blocked on the render graph work.

The next implementation slice should add pinned motion property lanes or media texture fills while keeping adjustment layers deferred until the render graph work is ready.
1 change: 1 addition & 0 deletions docs/Features/Project-Persistence.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ interface ProjectFile {
- Text clip properties
- Solid clip color
- Vector animation settings (loop, end behavior, fit, animation selection, background)
- Motion design definitions for shape/null/adjustment clips
- Transcript and analysis data per clip
- Scene description data

Expand Down
1 change: 1 addition & 0 deletions docs/Features/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ The docs in this folder were re-audited against the current codebase and now tra
| [Professional Color Correction Plan](./Color-Correction-Professional-Plan.md) | Tactical roadmap for wheels, curves, LUTs, secondaries, float precision, scopes, compare, and presets |
| [Masks](./Masks.md) | Overlay mask editing, whole-path keyframes, feathering, and stored modes |
| [Text Clips](./Text-Clips.md) | Canvas-backed text rendering, typography controls, and timeline text items |
| [Motion Design](./Motion-Design.md) | Motion layer schema, property registry, rectangle/ellipse shape editing, GPU renderer, and persistence/export plumbing |
| [3D Layers](./3D-Layers.md) | Shared-scene path, native Gaussian splats, cameras, and splat effectors |
| [Vector Animation](./Vector-Animation.md) | Lottie import, runtime playback, bounce modes, state-machine keyframes, and export behavior |
| [Audio](./Audio.md) | Playback sync, EQ, waveform extraction, audio clip behavior, and export |
Expand Down
17 changes: 15 additions & 2 deletions docs/Features/Timeline.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

[<- Back to Index](./README.md)

The Timeline is the core editing interface for multi-track editing. It now covers video, audio, image, Lottie, text, solid, mesh, composition, camera, and splat-effector clips, with keyframe lanes, transitions, multicam grouping, pick-whip parenting, and slot-grid playback.
The Timeline is the core editing interface for multi-track editing. It now covers video, audio, image, Lottie, text, solid, motion shape, mesh, composition, camera, and splat-effector clips, with keyframe lanes, transitions, multicam grouping, pick-whip parenting, and slot-grid playback.

---

## Track Types

### Video Tracks
- Hold video, image, Lottie, text, solid, mesh, composition, camera, and splat-effector clips.
- Hold video, image, Lottie, text, solid, motion shape, mesh, composition, camera, and splat-effector clips.
- Higher tracks render on top of lower tracks.
- Expanded tracks can show keyframe property rows and curve editors.
- Default layout starts with `Video 2` above `Video 1`.
Expand Down Expand Up @@ -63,6 +63,15 @@ getTrackChildren() // Query child tracks
### Solid
- Flat color clips used for mattes and backgrounds.

### Motion Shape
- Rectangle and ellipse shape clips are timeline clips with JSON motion definitions.
- Video track-header context menus can add Motion Rectangle or Motion Ellipse clips at the current playhead position.
- Solid clip context menus expose Convert Solid to Motion Shape.
- The Motion tab exposes primitive, size, radius, fill, and stroke controls.
- Motion shape replicator controls can enable a grid, edit X/Y counts, edit X/Y spacing, and keyframe the per-instance fade.
- Solid clips can be converted in the store to motion rectangle clips while preserving timeline identity, timing, transform, effects, and keyframes.
- Motion shape rendering uses WebGPU SDF textures, then the normal compositor stack.

### Mesh
- Primitive 3D meshes such as cube, sphere, plane, cylinder, torus, and cone.
- Rendered as 3D clips with full transform and keyframe support.
Expand Down Expand Up @@ -97,6 +106,7 @@ getTrackChildren() // Query child tracks
### Copy and Paste
- Copying clips includes linked audio automatically when the video clip is selected.
- Copy/paste preserves Lottie clip type and vector animation settings.
- Copy/paste preserves motion shape definitions.
- Copying keyframes stores them relative to the earliest copied keyframe.
- Pasting keyframes targets the selected clip when exactly one clip is selected; otherwise it falls back to the original clip from the clipboard data.

Expand Down Expand Up @@ -132,6 +142,7 @@ getTrackChildren() // Query child tracks
- Camera clips and native-render gaussian splats keep the camera-style property model visible.
- Numeric effect parameters appear as `effect.{effectId}.{paramName}` lanes.
- Lottie state changes appear as `lottieState.{stateMachine}` lanes; state-machine inputs appear as `lottieInput.{stateMachine}.{input}` lanes.
- Motion shape numeric lanes use registry paths such as `shape.size.w` and `appearance.{id}.stroke.width`.
- Audio EQ lanes sort `volume` and the band parameters first.

### Curve Editor
Expand Down Expand Up @@ -180,6 +191,7 @@ Soloing multiple tracks is supported. Non-solo tracks dim visually when any solo

### Track Header Context Menu

- Video tracks can create Motion Rectangle, Motion Ellipse, or Math Scene clips at the playhead.
- `Duplicate Track` currently creates a new empty track of the same type.
- `Delete` is blocked for the last remaining track of that type.
- Deleting a populated track shows the affected clip count in the menu label/tooltip.
Expand Down Expand Up @@ -228,6 +240,7 @@ The timeline store in `src/stores/timeline/index.ts` combines 20 slices plus 2 u
- `clipSlice`
- `textClipSlice`
- `solidClipSlice`
- `motionClipSlice`
- `meshClipSlice`
- `cameraClipSlice`
- `splatEffectorClipSlice`
Expand Down
33 changes: 31 additions & 2 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -9170,6 +9170,7 @@ input[type="checkbox"] {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
}

/* Column headers */
Expand Down Expand Up @@ -9412,6 +9413,13 @@ input[type="checkbox"] {
user-select: none;
flex: 1;
min-height: 0;
overflow: auto;
contain: layout paint;
}

.media-classic-virtual-spacer {
flex: 0 0 auto;
pointer-events: none;
}

/* View mode toggle buttons */
Expand Down Expand Up @@ -9674,11 +9682,17 @@ input[type="checkbox"] {
}

.media-board-wrapper.board-interacting .media-board-node {
will-change: left, top, transform;
will-change: transform;
}

.media-board-wrapper.board-interacting .media-board-group {
will-change: left, top, width, height, transform;
will-change: transform;
}

.media-board-wrapper.board-interacting .media-board-node,
.media-board-wrapper.board-interacting .media-board-group,
.media-board-wrapper.board-interacting .media-board-insert-gap {
transition: none;
}

.media-board-wrapper.board-interacting .media-board-node-thumb img {
Expand All @@ -9696,6 +9710,7 @@ input[type="checkbox"] {

.media-board-group {
position: absolute;
contain: layout paint style;
border: 1px solid rgba(255, 255, 255, 0.09);
border-radius: 6px;
background: rgba(255, 255, 255, 0.035);
Expand Down Expand Up @@ -9788,6 +9803,7 @@ input[type="checkbox"] {

.media-board-insert-gap {
position: absolute;
contain: layout paint style;
border: 1px dashed rgba(45, 140, 235, 0.68);
border-radius: 6px;
background: rgba(45, 140, 235, 0.12);
Expand All @@ -9800,6 +9816,7 @@ input[type="checkbox"] {
.media-board-node {
position: absolute;
display: block;
contain: layout paint style;
overflow: hidden;
border: 1px solid var(--border-color);
border-top: 3px solid var(--border-color);
Expand Down Expand Up @@ -9835,6 +9852,18 @@ input[type="checkbox"] {
opacity: 0.55;
}

.media-board-node.lod-compact {
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.18);
}

.media-board-node.lod-compact .media-board-node-timeline-drag {
display: none;
}

.media-board-node.lod-thumbnail-paused .media-board-node-placeholder {
opacity: 0.54;
}

.media-board-node-thumb {
position: relative;
width: 100%;
Expand Down
21 changes: 21 additions & 0 deletions src/changelog-data.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
[
{
"date": "2026-05-06",
"type": "improve",
"title": "Effective Playback FPS Diagnostics",
"description": "The stats overlay now reports effective FPS from the weakest active playback link, alongside render, preview, and decoder inputs so bottlenecks are visible during playback and scrubbing.",
"section": "Playback / Diagnostics"
},
{
"date": "2026-05-06",
"type": "improve",
"title": "HTML Video Playback and Scrub Feedback",
"description": "HTML-video playback and no-proxy scrubbing now use faster render cadence, tighter drag fallback drift, improved seeked-frame caching, and empty-frame hold behavior to reduce black flashes while keeping WebCodecs disabled.",
"section": "Playback / Scrubbing"
},
{
"date": "2026-05-06",
"type": "improve",
"title": "Scripted Scrub Diagnostics",
"description": "The AI playback tools can reset and return per-run diagnostics for scripted scrub runs, making long mixed-speed scrub tests easier to compare.",
"section": "Developer Tools"
},
{
"date": "2026-05-02",
"type": "improve",
Expand Down
Loading
Loading