The Timeline is the core editing interface for multi-track editing. It now covers video, audio, image, Lottie, Rive, text, solid, motion shape, mesh, composition, camera, and splat-effector clips, with keyframe lanes, transitions, multicam grouping, pick-whip parenting, and slot-grid playback.
Right-button scrubbing scrolls the viewport at either edge and follows its current scroll offset. Moving clips to another track preserves their original start times even with snapping disabled; occupied destinations do not silently shift or trim the selection. Reversed, trimmed clip thumbnails use the visible source interval in both software and worker rendering paths.
Holding or releasing Shift during an edge trim changes only snapping. Selected clips and linked audio/video retain the same trim scope in the live preview and committed edit.
Section resize observations update only the element that changed, so an audio or video child resize does not re-sample a transient parent split height during track-property edits.
Switching open composition tabs in a repository project restores only the target timeline from the installed content projection. Connected media and other panels retain their state; navigation does not reopen project journals or rebind the full media library. Each composition keeps its own playhead, zoom, and horizontal scroll position. Revision checkout still restores the complete project.
Track rows mount within the vertical viewport with overscan, while stable interaction callbacks and selective header subscriptions avoid rebuilding unrelated rows during scrubbing. Property selection and video warmup queries reuse indexed timeline data. Large native HUDs remain ordinary editable clips and nested compositions.
Scrolling prepares waveform columns only for clips intersecting the canvas viewport and its overscan, and worker eligibility checks the same canvas bounds. Columns are cached by their immutable source data, so new UI clip projections and resizing within the same channel layout can reuse them; the cache retains a bounded set of trim/display variants per source. Trims, zoom, channel layout, source analysis and waveform edits invalidate those columns. Dashed nested-composition outlines generate paths only across the canvas window, keeping long camera compositions responsive at high timeline zoom without adding borders at viewport edges.
The main-thread waveform painter also reuses unchanged waveform rasters during selection, hover, and other UI redraws. Its runtime-only LRU is capped at 32 MB and 512 entries; eviction immediately releases backing pixels. Canvases stay below 4096 pixels per dimension, with the existing Linux/Mesa software canvas policy and direct painting fallback for oversized or unsupported surfaces.
Cached waveforms appear immediately; missing source analysis loads progressively. A canvas that has already painted on the main thread retains that backend as more waveforms arrive, preserving the visible image instead of replacing it for a worker startup. Canvases eligible for worker rendering on mount still use the worker.
Dense keyframe rows share an immutable per-clip segment index: drawing rotation-path badges no longer filters and sorts the full solve for every diamond. Selection, outgoing rotation modes and the last keyframe's incoming easing target keep their existing behavior. Individual keyframe elements still have a mount/paint cost when all are visible.
Generated graphics without audio do not allocate silent composition mixdowns or media decoders. Generated-canvas, motion and mask resources have explicit cleanup and bounded reuse. These changes reduce idle work and memory pressure; decoding and compositing many simultaneously visible layers still have a real cost.
Restore batching, nested-keyframe publication, tracking-metadata cloning, and track interaction helpers have separate owners. The restore buffer keeps 128-clip batches and supports updates both before and after a batch is published. Terrain connectors resolve their animated stroke through the shared layer binding path for both top-level and nested compositions.
- Hold video, image, Lottie, Rive, 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 2aboveVideo 1.
- Hold audio-only clips and audio-linked companions for video clips.
- Waveforms support compact, detailed, and spectral timeline display modes.
- Volume keyframe rows and inline curve axes display gain in dB: unity is
0.0 dB, half gain is-6.0 dB, and silence is-∞ dB. Stored keyframes and curve geometry still use linear gain; opacity and scale retain percentage labels. - Audio Focus mode expands audio lanes and compacts video lanes without switching to a separate editor window.
- Linked audio follows video movement unless moved independently with
Altdrag. - Default layout includes one audio track named
Audio.
addTrack() // Create a video or audio track
removeTrack() // Delete a track and its clips
renameTrack() // Rename on double-click
setTrackHeight() // Resize a single track
scaleTracksOfType() // Resize all tracks of the same type together
setTrackLocked() // Lock or unlock timeline editing for a track
setTrackParent() // Parent tracks with cycle detection
getTrackChildren() // Query child tracks- Track height clamps to 20-600 px.
- Curve editors clamp to 80-600 px.
- Expanded track height depends on the selected clip, visible property rows, and open curve editors.
- Drag a track's bottom border to resize that track. The handle straddles the border in both the header and the lane, and works with mouse, pen, and touch; its grab area widens from 8 px to 14 px on coarse pointers.
- Shift + wheel over a track header resizes that one track; Alt + wheel scales every track of the same type together.
- On touch, a one-finger vertical drag anywhere else in the track-header column scales the whole section. That gesture explicitly ignores pointers that start on a resize handle, so the two never fire at once — the handle is a deliberate target and owns its pointer.
- Clip titles and passive status badges render as a lightweight DOM chrome
above
TimelineClipCanvas, clamped to the visible clip span while scrolling/zooming. The canvas still owns clip bodies, thumbnails, waveforms, spectrograms, and passive analysis/progress visuals. - When no thumbnail bitmap is available, clip chrome centers a height-scaled white outline pictogram in the currently visible clip span. Hidden track colors fall back to neutral gray, so every visible clip remains identifiable.
- Invariant — never perturb the clip previsualization: the clip body preview
(waveform, spectrogram, MIDI note bars, thumbnail) is the primary way a clip is
read at a glance and must stay unobstructed. Any clip whose body already
canvas-draws its own preview MUST NOT also render a type-icon pictogram overlay
on top of it — the overlay is redundant clutter that hurts legibility.
Currently this suppresses the icon for
midiandaudioclips (seeshowIconintimelineClipCanvasChromeOverlays.ts). This rule applies to any clip type with a body preview. Do not add badges, icons, or chrome that sit over the previsualization area. - A single resolver,
getTimelineTrackColor()(src/components/timeline/trackColor.ts), is the source of truth for a track's color. Precedence: a user-picked label color wins; otherwise a per-type default applies; otherwise the generic#303030. - MIDI identity: MIDI tracks with no custom label color resolve to
MIDI_TRACK_COLOR(#3a4050), which must stay in sync with the--midi-colortoken insrc/styles/tokens.css. The token also drives the MIDI track-header tint (TimelineHeaderisMidiDefaultTint+.track-header.midi.midi-default-tintinTimelineTracks.css). Keep header and clip in sync from these two anchors. - Pitfall (issue #259 / #228 fallout): clip bodies are painted by the
canvas renderer (
TimelineClipCanvas) using the color fromgetTimelineTrackColor(). Per-type clip colors are resolved throughgetTimelineTrackColor()rather than.timeline-clip.*CSS. Any per-type clip color (like MIDI's) must live ingetTimelineTrackColor().
- Imported from the media panel or by dropping files on the timeline.
- External drag previews are shown only for valid destinations. Video ghosts are always blue and audio ghosts are always green. A normal video-lane drop previews only the video, without an extra linked-audio ghost lane. Dropping a video that contains audio directly on an audio lane instead previews and places the linked audio on that exact lane plus the video on the lowest free video lane (normally
Video 1); silent video, images, generated visuals, and other incompatible sources show no clip ghost on audio lanes. - Thumbnails and proxies are supported.
- During video thumbnail generation, both the worker and software canvas keep decoded source previews visible while newer frames decode, then update them without blanking the thumbnail strip.
- View > Thumbnails immediately hides or restores timeline thumbnails, including cached frames and nested-composition segment previews. Clip titles, type pictograms, composition boundaries, and audio waveforms remain visible. Hidden thumbnails do not schedule timeline thumbnail warmups. The worker and main-thread software canvas both respect this setting.
- In Video Focus, holding Ctrl/Strg while dragging inside a visual clip marks a clip-scoped video bake region. Double-click marks the full visible clip. Clip-scoped regions currently use the transient preview cache path and can be unbaked or removed from the clip overlay.
- Holding Ctrl/Strg while dragging on the ruler marks a composition-scoped video bake region. Baking a ruler region renders the visible composition into a compressed WebCodecs proxy and substitutes that single proxy layer during preview playback, so the generic RAM cache indicator is not the durable bake source.
- Can exist alone or as linked audio for video clips.
- Fades are authored through
audio-volumekeyframes. - In Audio Focus with detailed or spectral display, holding Ctrl/Strg while dragging inside the waveform creates an inline audio region selection. Plain left-drag keeps the normal clip move behavior.
- Double-clicking a detailed audio clip in Audio Focus highlights the full visible clip as an editable audio region.
- Left-dragging an already selected audio region moves that highlighted region along the clip without changing its duration; left-dragging outside the selected region keeps moving the whole clip.
- Dragging the selected audio region's left or right edge resizes the highlighted region.
- Matching audio edit operations move/resize in place with the highlighted region instead of being duplicated.
- The selected region exposes a horizontal gain line for direct level edits and side handles for fading that gain change in and out.
- Existing audio-region edit markers are displayed from the top of the clip downward when ranges overlap, and
Audio Region Markersin the View menu hides/shows those markers without changing edit processing. - Right-clicking the selected region opens direct Split/Cut/Copy/Paste actions first, then secondary non-destructive audio edit-stack operations in compact submenus such as silence, insert silence, delete silence, reverse, invert polarity, channel swap, mono sum, and Region FX presets. Split isolates the selected region as its own middle clip and selects that new clip. Cut removes the selected region from the track and leaves the original audio as left/right clip parts.
- Region FX presets are stored as region edit-stack operations, not ordinary whole-clip effects, so they stay attached to the selected source range when that region is moved or resized. Their exact waveform cache renders in the background while live playback routes the effect only when the playhead is inside the region.
- Simple detailed-waveform updates for those non-destructive region edits derive from the source waveform pyramid when possible, and region gain/silence preview is applied directly to the visible waveform columns while dragging. The lane keeps high-resolution detail without waiting for a full processed-audio render, and derivable processed-waveform refreshes run as background cache updates without showing the red waveform-generation border or progress bar. Scrub and playback preview also follow simple region gain/silence changes instead of always using the raw source level.
- Audio edit-stack chips appear on edited audio clips for quick state, while the selected clip Properties panel exposes an
Audio Editstab for inspection, bypass, removal, clear, bake, unbake, and bake history. - Bake renders active region edits into a new WAV media source in the Media Panel
Baked Audiofolder, stores the project-local file underRaw/Baked Audio/, and resets the clip edit stack. Unbake restores the latest reversible bake to the original source media and the pre-bake region edit stack when that source media is still present in the project. - Waveform pyramid resolution: the displayed waveform comes from a pyramid that is content-addressed by the source (one pyramid per media file, shared by all its clips). The reference is stored per clip (
audioState.sourceAnalysisRefs.waveformPyramidId) and is only back-filled onto the clip that triggered analysis (clipWaveformAnalysisActions.ts). A clip created/rebuilt before the source finished analysing can therefore end up with an empty ref and fall back to a single-channel (mono) render while its siblings render full stereo.TimelineClipCanvasguards against this by back-filling a missing ref at render time from the media file or any sibling clip of the same source (enrichClipsWithSourceWaveformRef). Per-clip refs are a cache, not the source of truth — never assume a clip without one has no waveform.
- Created through the timeline text slice.
- Supports typography, stroke, shadow, and path text.
- Drag either timeline edge to extend text, still images, solids, motion layers, cameras, lights, MIDI, storyboards, generated scenes, Gaussian avatars, splat effectors, or static models and Gaussian splats beyond their initial duration.
- The extended duration stays in place after release and subsequent edits, aligned to the composition frame grid. The initial duration is only a starting length for these sources.
- Lottie and Rive clips can extend when looping is enabled. Recorded video/audio, non-looping vector animations, and model or splat frame sequences retain their source-duration limits.
- Lottie is imported from
.lottiepackages or Lottie JSON files from the Media Panel. - Rive is imported from
.rivfiles and rendered through the Rive WASM canvas runtime. - Both providers use the same canvas-backed render path as text and solids, so preview, nested comps, and export stay aligned.
- Exposes per-clip loop, end behavior, playback mode, fit, render resolution, animation selection, and background controls in the Properties panel.
- State machines can be selected in the provider tab, with state changes stored as blue stepped keyframes when state names are available.
- Boolean and numeric state-machine inputs appear as normal stopwatch-keyframed properties.
- Rive Data Binding exposes view models, instances, static string/enum values, and keyframed numeric/boolean/color values.
- When loop is enabled, the clip can be extended beyond its source duration on the right trim edge without freezing on the first pass.
- Flat color clips used for mattes and backgrounds.
- 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.
- Primitive 3D meshes such as cube, sphere, plane, cylinder, torus, and cone.
- Rendered as 3D clips with full transform and keyframe support.
- Nested compositions can be dropped from the media panel.
- Double-click enters the nested comp for editing.
- Camera clips and splat-effector clips are first-class clip types in the store and copy/paste flow.
- Camera clips use a compact
3Dplus video-camera badge in both DOM and canvas timeline rendering. - Camera/native-gaussian clips expose camera-oriented property labels in the keyframe UI.
- New and reset camera clips start at Z = 1 so their eye is outside the scene origin.
- Pending download clips are represented in the timeline while the download is in progress.
Timeline snapping starts disabled unless a previous choice was saved. Hold Shift to snap temporarily; Alt bypasses snapping. The magnet button saves your on/off preference.
| Action | Current Behavior |
|---|---|
| Move | Drag a clip or a multi-selection with a mouse, or hold briefly and drag with touch. |
| Trim | Drag clip edges with a mouse or touch. Coarse pointers receive enlarged edge hit targets. |
| Cut tool | C toggles Blade mode through the timeline tool palette; click clips to split them. |
| Split at playhead | Shift+C in MasterSelects, preset-specific alternatives elsewhere. |
| Split all at playhead | Available in the Cut flyout; runs through the shared timeline edit operation kernel. |
| Blade all tracks | Available in the Cut flyout as a mode; splits every unlocked visible clip crossing the click time. |
| Trim start/end to playhead | Available in the Cut flyout; trims selected clips through the operation kernel and keeps linked audio aligned. |
| Ripple delete | Available in the Cut flyout and clip context menu; deletes selected clips and closes the affected track gap through the operation kernel. |
| Delete gap | Available in the Cut flyout, clip context menu, and empty timeline right-click menu; closes an empty gap through the operation kernel. |
| Delete all gaps in this layer | Available from the empty timeline right-click menu; closes gaps on the clicked layer from the clicked empty space onward as one undoable operation. |
| Delete all gaps | Available from the empty timeline right-click menu; closes all gaps on unlocked visible tracks as one undoable operation. |
| Fit comp to window | Available from the zoom controls and empty timeline right-click menu. |
| Right-drag empty space or clips | Scrubs the playhead without opening the timeline context menu; context menus open only for a single right-click. |
| Edge playhead drag | Left-drag or touch-drag the ruler or playhead head against either visible lane edge to auto-scroll proportionally; persistent snapping plus the Shift temporary-enable and Alt bypass modifiers remain active. Snapping to a clip end lands on its last visible composition frame instead of the exclusive time immediately after it. |
| Sync via Audio | Clip context menu action for selections with at least two audible clips; aligns selected audio/video pairs by waveform correlation and writes one manual linked group. |
| Lift range | Available in the Cut flyout after drawing a Range Selection; removes the range and leaves a gap. |
| Extract range | Available in the Cut flyout after drawing a Range Selection; removes the range and ripples following clips left. |
| Copy | Ctrl+C copies selected keyframes first, otherwise selected clips. Clip right-click exposes the same clip-copy command. |
| Paste | Ctrl+V pastes keyframes if the clipboard has them, otherwise pastes clips. Empty-space right-click exposes Paste at the clicked time and layer. |
| Delete | Delete / Backspace removes selected keyframes first, then clips. |
| Reverse | Available from the clip context menu and via clip state. |
| Freeze | Properties › Speed Change › Freeze, or clip context menu › Freeze frame at playhead. Holds the currently resolved source frame; linked audio is silent. |
| Create Subcomposition | Clip context menu action that moves the selected timeline clips into a new composition and inserts that composition back into the current timeline. |
| Blend mode | + / - cycles blend modes on selected clips. |
- Linked clip partners use the same live drag geometry as the directly dragged clip, so linked audio/video and manual linked-group peers stay visually in sync while moving.
- Dragging a multi-selection across video or audio tracks moves every selected clip in that track family by the same track-index delta, preserving relative layer spacing in the live preview and committed edit. Cross-family partners such as linked audio stay on their corresponding audio tracks.
- Two-finger pinch zooms the timeline around the moving gesture midpoint and preserves the time under that midpoint while the fingers move.
- A stationary touch opens the same context menu as a right-click; movement, a second touch, or pointer cancellation aborts the long-press gesture.
- Audio drops never commit an overlap on the target audio layer: placement advances to a free interval and can open a new compatible layer when the existing layers cannot accept the clip.
- Creating a composition from an existing composition wraps it as a nested composition while inheriting the source composition's resolution, frame rate, and duration.
- Copying clips includes linked audio automatically when the video clip is selected.
- Right-clicking a clip provides Copy; right-clicking empty timeline space provides Paste when the timeline clipboard contains clips.
- Copy/paste preserves vector animation clip type and vector animation settings.
- Copy/paste preserves motion shape definitions.
- Copy/paste preserves composition clips as visual nested-composition sources; linked composition-audio wrappers remain audio-only and are never prepared as visual waveform layers.
- 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.
- When timeline clips or keyframes are selected, timeline clipboard shortcuts take precedence over Media Panel clipboard shortcuts.
- The ruler/header strip contains a compact grouped timeline tool palette.
- UX: the palette is always visible, with every tool button in place at full size like a normal toolbar (issue #256). The hold-to-open grouped-tool flyout is available.
- Root groups are Selection, Cut, Trim, Placement, and Navigation/Marking.
- Clicking a root button activates the last enabled child tool for that group.
- UX press-drag-release (issue #256): the whole root button is one target — there is no chevron/edge hit-zone. A short click just activates the group's current tool. Pressing and holding past
HOLD_TO_OPEN_MS(200ms, pointer still down) opens the unclipped portal flyout; the pointer is captured so the user can slide off the small button onto the items and release over the desired tool to select it in one continuous gesture. Releasing back on the root button activates the current tool (a hold-and-release without choosing never swallows the click); releasing over empty space cancels. Right-click and the keyboard (ArrowDown) open a sticky, click-to-pick flyout instead (no held pointer to drag with). - Enabled tools are Select, Track Select Forward/Backward/Forward All Tracks, Range Selection, Blade, Blade All Tracks, Split at Playhead, Split All at Playhead, Trim Start/End to Playhead, Ripple Trim Start/End to Playhead, Ripple Delete, Delete Gap, Edge Trim, Ripple Trim, Rolling Edit, Slip, Slide, Rate Stretch, Position/Overwrite, Hand/Pan, Zoom, Marker, In Point, Out Point, and Pen/Keyframe.
- Track Select Forward, Track Select Backward, and Track Select Forward All Tracks are enabled selection subtools; their clip clicks route through the shared pointer dispatcher and
select-clips-from-timeoperation. - Range Selection is an enabled selection mode. Dragging on timeline space stores a timeline range with the affected unlocked visible track IDs and leaves a persistent range overlay for later lift/extract/copy commands.
- Shared tool previews render through
TimelineToolOverlayLayerfor section-scrolled overlays such as Track Select highlights, Blade All Tracks cut lines, and blocked tool messages. - Active pointer tools set a matching icon cursor so the selected tool remains visible at the mouse pointer. Blade, Range, track-selection, trim modes, Hand, Zoom, Marker, In/Out, and Pen/Keyframe expose custom SVG cursor glyphs with normal CSS fallbacks.
- Hand/Pan drags the timeline surface horizontally without moving clips. Zoom clicks around the pointer and uses
AltorShiftfor zooming out. - Tool selection is treated as non-mutating and remains available during export; mutating commands are blocked while export is active.
Ctoggles Blade mode in the default preset.Escapeexits cut mode.Shift+Cperforms a direct split at the playhead without entering cut mode.- Blade hover/click is handled by the timeline tool pointer dispatcher, which writes shared preview state and commits through
applyTimelineEditOperation. - Split-at-playhead, AI single-clip split, AI bulk split-at-times/evenly, AI move, AI trim, and AI reorder all route through
applyTimelineEditOperation. - Lift Range and Extract Range also route through
applyTimelineEditOperation; they split clip boundaries at the selected range and clear the range overlay after commit. - Mutating operation-kernel commits and global undo/redo are blocked while export is active.
- Timeline edit operations expose replay descriptors for Guided Action playback, so Blade, Track Select, Trim, Slip/Slide, Placement, Lift/Extract, and related operation-kernel edits can be represented as semantic timeline replay targets without DOM clicks as the execution source.
- Deleting a gap or all gaps moves the playhead with the remaining material, including during playback. Positions inside a removed gap land at its beginning; positions before it stay unchanged. Existing clip overlaps are preserved.
- For gap deletion on several tracks with different gaps, the playhead follows the selected clip's affected track, or the first affected track when none is selected. A single explicitly targeted track takes precedence. Linked audio/video gaps are counted once.
- Trim-to-playhead and ripple-trim-to-playhead commands run through the shared operation kernel and preserve linked audio/video timing.
- Edge Trim, Ripple Trim, Rolling Edit, and Rate Stretch reuse the existing trim handles, but commit through
applyTimelineEditOperationinstead of direct clip mutations. - Edge-trimming a linked video/audio clip resizes the linked partner live in the canvas preview, including the source-extension ghost; holding
Altkeeps the partner independent. - Slip and Slide are available as registered operation-kernel modes. Dragging a clip body with either tool previews the slip/slide and commits through
applyTimelineEditOperation;Altslips independently from linked audio/video. - Trim mode activation gives Blade, Hand, Zoom, and Range clicks priority over trim/fade handles.
- Position/Overwrite is an enabled Placement mode. Dropping media in this mode uses the shared
place-timeline-rangeoperation to clear the target range before the new clip is created. - The placement operation supports insert-style space creation and overwrite-style range clearing, including split/trim/delete behavior and linked video/audio split preservation.
- Normal Select-mode drops still use gap-aware placement. Position/Overwrite intentionally keeps the requested drop time and lets the kernel trim/delete the affected target range.
- Insert, Overwrite, Replace, Fit to Fill, Append, Place on Top, and Ripple Overwrite are enabled when the Media Panel or Source Monitor exposes a current source item. The commands resolve the source, prepare the affected range through the placement operation, then create the new clip on the target track.
- Replace, Fit to Fill, and Ripple Overwrite prefer the selected timeline range or selected compatible target clip. Insert and Overwrite use the playhead. Append uses the end of the compatible target track, and Place on Top uses a free upper video track or creates one.
- New visual media clips keep the default 100% scale; users resize or fit them manually in the Properties panel when needed.
- Source Monitor In/Out marks constrain the placement source duration and source in point. Clearing the marks returns placement to the full source duration.
- Source Monitor exposes a full-width source timeline with ruler, playhead, draggable In/Out handles, audio-file playback, and direct Insert, Overwrite, Replace, Fit, Append, and Top buttons, so source edit commands are available outside the timeline tool flyout.
- Clicking a track header targets that video or audio track for source edits and clip paste. Clicking the same highlighted track again clears that target.
- Hovering or focusing Placement commands in the timeline flyout or Source Monitor publishes a non-mutating placement preview. The shared overlay renders ghost clips on affected tracks and shows the source In/Out bounds used for the command.
- Pen/Keyframe is an enabled Navigation/Marking mode with a dedicated cursor.
- Clicking a visible keyframe property lane adds or updates a keyframe at the clicked time.
- The inserted keyframe value is sampled from the existing property lane by linear interpolation, so adding in-between keys preserves the current visible curve value before further edits.
- Click selects a clip. Clicking it again keeps it selected by default. Enable Settings > General > Timeline > Deselect selected clips on repeat click to restore click-to-deselect; the preference persists in this browser.
Ctrl+Clickadds or removes a clip from the selection.Shift+Clicktoggles only the clicked clip, which is different from the normal linked-selection behavior.- Normal click on a linked video clip selects both the video and linked audio clip.
- Marquee selection also activates linked audio partners for selected video clips.
- Click empty space to clear selection.
- Refreshing the page restores the active composition, selected clips, and the focused clip in Properties for that browser tab. Clips absent from the saved project are ignored; an empty selection stays empty.
- Marquee selection works from empty timeline space.
- Keyframe selection uses the same shift-toggle pattern.
- Select keyframes by clicking the diamond.
Deleteremoves selected keyframes before it removes clips.- See Keyframes for curve and property details.
- Expanded track headers show a flat list of property rows, not nested folders.
- Property row labels are single-line with ellipsis so long effect, mask, and generated-source labels do not collide with keyframe controls or values.
- The current clip's keyframes decide which rows are visible.
- The UI hides
rotation.x,rotation.y,position.z, andscale.zfor 2D clips. - Camera clips and native-render gaussian splats keep the camera-style property model visible.
- Numeric effect parameters appear as
effect.{effectId}.{paramName}lanes. - Vector animation state changes appear as
lottieState.{stateMachine}lanes; state-machine inputs appear aslottieInput.{stateMachine}.{input}lanes. Rive Data Binding values appear asriveData.{property}lanes. - Motion shape numeric lanes use registry paths such as
shape.size.wandappearance.{id}.stroke.width. - Audio EQ lanes sort
volumeand the band parameters first.
- Double-click a property row to open the curve editor.
- Only one curve editor can be open at a time.
- The curve editor shows Bezier curves, auto-scales the value axis, and supports
Shift+wheelresizing. - Selected keyframes expose in/out handles; right-click on a handle resets it to the default 1/3-distance handle.
- Composition clips can be nested to a depth of 8.
- Composition changes propagate into nested render data immediately, including layer additions/removals and source-type conversions while the child timeline remains active.
- Pinned parent and child previews share nested frame timing in both navigation directions; parent playback drives the child preview through the visual composition wrapper rather than its linked audio companion.
- Selected clips can be converted into a new nested composition from the clip context menu.
- Composition switches trigger clip entrance/exit animations in the timeline UI.
- Vector animation clips inside nested comps render through the same canvas path used in the primary timeline and export flow.
- Transitions operate between adjacent clips on the same track.
- The first-pass suite includes Crossfade, Dip to Black, Dip to White, Wipe Left, and Wipe Right.
- Dragging a transition from the Transitions panel targets a clip junction. Hover preview shows the transition body, real source-handle coverage, and red hold-frame fallback coverage when the requested duration extends past available source material.
- The default placement is virtual
center: the transition body is centered on the cut without moving either clip. Preview and export sample the incoming left handle before the clip start and the outgoing right handle after the clip end; when either side lacks real source material, the nearest first/last frame is held and shown as red fallback coverage. - Existing transition bodies render on the timeline, can be selected, moved left/right by dragging the body, resized by dragging either edge, and expose duration plus handle/hold details in the transition-scoped Properties tab. Move snaps to the centered cut position and to available source-handle edges; resize snaps to the same source edges. Move and resize previews show the same source-handle and red hold-frame feedback. Transition durations are not capped by clip length; missing material is represented with hold-frame fallback.
- Preview and export share the same transition planner and layer assembly. Wipe transitions use compositor transition metadata rather than clip effects.
- Double-clicking a transition body opens its linked transition composition. Mapped-v3 has exactly one full-duration outgoing source clip and one incoming source clip; panels and generated layers may remain additional editable layers. Legacy segmented compositions prompt for an explicit upgrade and are otherwise opened unchanged. See Transition Compositions for mapped timing, template versions, parity, and the backup lifecycle.
- Sync via Audio is available for selected audio/video pairs. Hour-long recorder stems and large camera files are read streamed (see Audio), and the AI chat can run the same sync and multicam build through
syncClipsViaAudio,getAudioSyncStatusandsetMulticamMode. - Linked group movement preserves offsets so sync timing stays intact.
- Multicam cut mode (the Multicam toggle in the Multi Preview) treats every video track with plain camera clips (speed 1, not reversed) as one camera angle, top track = key 1. The first activation rebuilds the program: at every moment only the topmost camera with material keeps a clip, the other camera tracks are cut out, and all clips of the composition, including the synchronized audio, join one linked group. The camera sources are stored on the composition, so a cut-out range can be refilled from the original material later.
- With cut mode on, keys
1-4switch cameras. During playback a key cuts at the playhead; the new camera runs until the next existing cut. While paused a key switches the whole segment under the playhead and keeps its boundaries. A camera fills only the part of the range where it has material, and pressing the key of the camera already on air does nothing. Every switch is one undo step. - Program pieces are ordinary clips that keep the camera clip's transform, effects, and masks, so trimming, grading, and export work unchanged. Turning the toggle off keeps the edit and gives the number keys back to their normal shortcuts; turning it on again continues on the same program without rebuilding it.
- Clips and tracks support parent-child relationships.
- Parent-child links are rendered as overlays with the pick-whip interaction.
- A child inherits the parent's position, rotation, and scale as one composed 2D transform. Uniform parent scale affects both the child's own scale and its offset from the parent, so linked animation stays identical across differing source aspect ratios.
- Opacity remains local to each clip and is never inherited through the pick-whip relationship.
- During playback and scrubbing, an active parented video layer holds its last owner-matched frame while its decoder seeks, preventing a temporary black layer without filling genuine timeline gaps.
Each track header exposes:
- Visibility for video tracks.
- Mute for audio tracks.
- Solo for both track types.
- Lock for both track types; locked tracks block timeline edits such as move, trim, split, delete, keyframe edits, and dropping new clips.
- Track rename on double-click.
- Track expansion to reveal keyframe lanes.
- Right-click opens a track-header context menu with
Add Video Track,Add Audio Track,Duplicate Track, andDelete.
Soloing multiple tracks is supported. Non-solo tracks dim visually when any solo state is active.
Audio and MIDI tracks share one audible solo group: soloing a MIDI track silences non-soloed audio tracks and vice versa (both during playback and export), and the non-soloed tracks of either type dim in the timeline. Video solo stays an independent, visual-only group. An active solo button on an audio/MIDI track shows a filled amber chip so the soloed state is obvious.
- Math Scene and Motion Shape presets are created from the Media panel add/context menu and then dragged to video tracks.
Duplicate Trackcurrently creates a new empty track of the same type.Deleteis blocked for the last remaining track of that type.- Deleting a populated track shows the affected clip count in the menu label/tooltip.
The toolbar and wheel gestures drive playback and navigation:
- Space toggles play/pause.
J,K,Lshuttle reverse, pause, and forward.IandOset in/out points.Xclears in/out.- Stop returns the playhead to the In point when one is set; otherwise it returns to timeline start.
Madds a marker at the playhead.- Right-clicking a marker opens marker transport and MIDI actions.
- Markers can be turned into
Stop Markers that automatically pause playback when crossed. - Marker MIDI bindings support
Jump To Marker,Play From Marker, andJump To Marker And Stop. - Left/Right arrows step frame by frame.
- The ruler is a stack of independent ruler lanes (Time / Timecode / Frames / Bars+Beats) toggled from the Rulers checklist next to the View dropdown; each lane keeps a fixed format and only its tick density adapts to zoom (no frame↔time crossfade). Bars+Beats is projected through a per-composition TempoMap. Clicking a lane marks it active (highlighted). See Timeline Rulers.
- The timeline body lane grid (behind clips, distinct from the ruler) still uses the active composition frame rate at deep zoom: the base time grid crossfades out while frame-accurate grid lines fade in with zoom.
- Unselected clips keep persistent white edge brackets: each vertical edge has short top and bottom caps that fade inward, so adjacent clips form a visible
][cut marker without obscuring thumbnails or waveforms. Selected clips retain the complete white outline. Alt+ScrollorCtrl+Scrollzooms the timeline around the mouse pointer by default; Preferences -> General -> Timeline can switch the zoom anchor to the playhead. While zooming in near either lane edge, a magnetic 48 px edge zone preserves the corresponding visible time boundary so the nearby beginning or end is not pushed offscreen. Timeline zoom remains contained behind the dedicated navigator instead of exposing a second native panel scrollbar. Faster wheel gestures use larger zoom steps.Shift+Scrollpans horizontally.- Vertical scroll snaps to track boundaries.
Ctrl+Shift+ScrollorCmd+Shift+Scrolltoggles slot-grid view.- The toolbar also exposes a dedicated slot-grid toggle button that flips between timeline bars and the 12x4 grid icon.
- The Navigation/Marking tool flyout exposes Marker, In Point, and Out Point commands for the current playhead position.
- During playback, the playhead position is applied as a direct compositor transform on every animation frame. Its triangular head and theme-defined line shadow remain visible instead of being clipped to the two-pixel line box.
- The running clock updates the time display and playhead without rebuilding the timeline's editing controls, track headers, waveforms, or clip rows. Auto-scroll follows the clock directly; editing shortcuts and pointer actions sample the current playback position when invoked. During scrubbing, the playhead follows pointer positions directly while layer updates are combined into one update per animation frame. Marker hover updates pause during the drag so mouse movement does not rebuild the editor. Pausing, releasing the playhead, and content edits refresh the editing view.
The timeline navigator below the tracks provides the same scroll and zoom control in a dedicated bar. Releasing its scroll thumb or zoom handles never falls through to the track's click-to-jump action.
- Thumbnails, waveforms, and transcript markers can each be toggled from the toolbar.
- RAM preview caches 30 fps frames.
- Composition video bake regions render a compressed preview proxy through the export pipeline and use it as a single layer during editor preview playback. Clip-scoped video bake regions use the transient RAM preview path.
- Video bake proxy artifacts are runtime-only; project persistence keeps the region marks and resets volatile bake status after reload or timeline cache invalidation.
- Proxy caching keeps proxy frame ranges warm in the background.
- Clips whose source must be reloaded keep their normal body rendering and add the shared missing-media tint and border, so the reload state remains visible in both the main-thread canvas and projected timeline data.
- Export progress is shown directly on the timeline.
- Slot-grid view is animated through the same
slotGridProgressstate that drives the timeline/grid transition. - When
useWarmSlotDecksis enabled, slot-grid tiles can show deck warmup badges (C,Wi,Wa,H,F,D) that reflect reusable background playback state.
The timeline store in src/stores/timeline/index.ts combines modular slices plus utility modules:
trackSliceclipSlicetextClipSlicesolidClipSlicemathSceneClipSlicemotionClipSlicemeshClipSlicecameraClipSlicesplatEffectorClipSliceclipEffectSlicecolorCorrectionSlicelinkedGroupSlicedownloadClipSliceaudioEditSlicevideoBakeSlicetoolSliceeditOperationsplaybackSliceramPreviewSliceproxyCacheSliceselectionSlicekeyframeSlicemaskSlicemarkerSlicetransitionSlicenodeGraphSliceclipboardSliceaiActionFeedbackSlice
Utility modules:
positioningUtilsserializationUtils
Important guard:
timelineSessionIdis incremented when the timeline is cleared or reloaded so stale async callbacks do not write back into the wrong session.
Core timeline components live in src/components/timeline/:
Timeline.tsxorchestrates the full timeline.TimelineTrack.tsxrenders track rows and property lanes.TimelineHeader.tsxrenders track headers and property controls.TimelineClipCanvas.tsxrenders passive clip bodies, thumbnails, waveforms, spectrograms, labels, and canvas-only passive decorations.ClipInteractionShellmodules render active clip affordances such as trim/fade handles, keyframe ticks, region controls, and context-menu shells.TimelineKeyframes.tsxrenders keyframe diamonds in track lanes.CurveEditor.tsxandCurveEditorHeader.tsxhandle curve editing.TimelineControls.tsx,TimelineRuler.tsx, andTimelineNavigator.tsxhandle navigation and toolbar controls.SlotGrid.tsxandMiniTimeline.tsxhandle slot-grid mode.PickWhip.tsx,ParentChildLink.tsx, andPhysicsCable.tsxhandle parenting visuals.
The main hooks are useClipDrag, useClipTrim, useClipFade, useTimelineKeyboard, useTimelineZoom, useExternalDrop, useTransitionDrop, usePickWhipDrag, useMarqueeSelection, usePlayheadDrag, usePlayheadSnap, useMarkerDrag, usePlaybackLoop, useLayerSync, and useAutoFeatures. EditorPlaybackRuntimeHost mounts the shared playback loop at the editor-shell level so changing dock layouts does not unmount the composition clock.
Linked clip selections use a shared outline that follows the outer contour of the selected clip surfaces, including staggered edges and gaps. Individual selections retain their normal clip outline. Video and audio sections draw their respective portions independently.
setClipTimeRemap(clipId, { kind: 'freeze', sourceTime }) and
freezeClipAtPlayhead(clipId) share one atomic timeline mutation. The latter samples
the current retime contract at the playhead, so enabling Freeze retains the visible
source time. setClipTimeRemap(clipId, null) removes it. Linked video/audio partners
receive the same remap in one undo step, including when linked speed following is
disabled. A locked affected track or export lock rejects the entire action.
| Contract | Freeze behavior |
|---|---|
| Priority | Transition source map → finite transition override → transition hold → Freeze → signed speed integration. |
| Output interval | Existing start and duration are retained (raised to one frame only if shorter). Edge trims change start/duration independently of in/out; right extension is not limited by media length. Minimum duration is one composition frame. |
| Source clock | Constant source seconds, clamped to [0, naturalDuration]; if source duration is unavailable, [0, outPoint]. Nested clips use composition seconds. |
| Speed / Reverse | Ignored while frozen, retained for unfreeze. Speed-section bypass does not disable Freeze. |
| Animation | Keyframes retain clip-local time; Freeze does not pause clip effects or transform animation. |
| Prefetch | A single source time, without direction changes or jumps. Transition mappings still take precedence. |
| Audio | Explicit silence in live playback and offline/export rendering, including frozen composition audio; no pitch processing or effect tail. |
| Persistence | Clip content, saved and restored through project, composition and history paths. Future remap kinds are preserved as opaque JSON and ignored by this version; new actions reject unsupported kinds. |
The Properties source-time control edits the held frame. Timeline and composition
graph cards show a Freeze badge; the Speed time-chain node exposes
freezeSourceTime. Split pieces retain the same freeze and original source window. Nested export decoder admission is conservative across the root output interval when an ancestor is frozen; per-frame sampling still uses the exact held source time.
Unfreezing keeps the authored output duration and restores ordinary speed/mirror
sampling (which holds at source boundaries if that duration extends past them).
setClipTimeRemap(clipId, { kind: 'loop', phase?: number }) repeats the trimmed
source window. Phase is an additive offset in source seconds, defaulting to zero.
Loop and Freeze occupy the same timeRemap field and replace each other. The
Properties Speed Change section and clip context menu expose Loop; timeline
and composition cards show a Loop badge and the Speed time-chain node exposes
loopPhase. Linked partners change atomically through the same action and undo step.
| Contract | Loop behavior |
|---|---|
| Priority | Transition source map → finite override → transition hold → Loop → ordinary speed. |
| Source clock | Signed speed integration (including keyframe holds, inspector bypass and Slit Scan factor), plus phase, wraps into [inPoint, outPoint). Reverse mirrors the wrapped result afterward. There is no source-bound clamp. |
| Empty cycle | outPoint <= inPoint holds at inPoint; audio is silent. |
| Output interval | Duration is independent of the source window. Right-edge trim operations can extend through multiple cycles; left trims retain the source window and advance phase. Minimum duration is one composition frame. |
| Split | Each piece retains the source window and Loop. Phase advances by the integrated source offset; speed curves retain their out-of-window keys in each piece's local clock. |
| Prefetch / inverse | Crossing a wrap requests the full source window. Constant-speed intervals without wraps use tight bounds; unknown curves conservatively request the full window. Loop has no unique inverse. |
| Audio | Constant-speed clips repeat the trimmed, direction-correct, speed-processed cycle with phase. Preview uses the processed-buffer path and stays silent while preparing. Automated loops use exact source-time resampling; pitch preservation is currently limited to constant-speed loops. |
| Persistence | Kind and phase are clip content, retained through project/composition save/load and undo/redo. Both participate in the processed-audio cache key. |
Removing Loop with setClipTimeRemap(clipId, null) keeps the authored duration;
ordinary playback holds at source bounds after its source window is exhausted.
Loop edge drags forward the authored duration, and speed or source-window edits retain it. Nested Loop parents keep descendant video decoders admitted over the root output interval. Video frame selection wraps its backward boundary offset inside the Loop source window; ordinary backward clips clamp that offset at their in-point. These frame-selection bounds are runtime sample metadata and do not alter exact timing, audio samples, transition mappings, or persisted clip data.
Select/Edge Trim right-edge gestures stop at the next clip on the affected track (including a linked partner's track). Without a neighbor, Loop and Freeze can extend beyond media length; normal clips retain their source limit. Ripple Trim can extend Loop/Freeze and shifts following clips by the added duration in the same edit. Canvas sizing uses the clip body's shared trim geometry, so an exhausted source window cannot hide a longer Loop/Freeze preview.
While Freeze or Warp is active, the Properties Speed controls (including keyframes, reset and linked-audio speed) are disabled: Speed and Reverse are retained but ignored. Loop keeps these controls available. The clip context menu marks the active retime mode; Unfreeze clip removes Freeze, and Reverse's tooltip explains when it is ignored.
setClipTimeRemap(clipId, { kind: 'warp', points: [{ time, source }, ...] })
authors source seconds against clip-local seconds. Warp, Freeze and Loop are
mutually exclusive. The Properties Speed Change section has a Warp toggle,
a compact Time/Source points editor, insert-at-playhead and remove actions;
the clip context menu exposes Warp clip and the timeline shows Warp.
Each edit goes through the existing atomic linked-pair action and one undo step.
Dragging a numeric control uses the shared inspector gesture batch.
Paused previews and settled scrubs compare source frame indices, using the media frame rate and decoded frame timestamps when available. A nearby timestamp in an adjacent frame is not accepted. Decoder direction follows the resolved source rate; retained Speed/Reverse settings do not redirect Warp playback. Active playback keeps its existing drift thresholds.
| Contract | Warp behavior |
|---|---|
| Priority | Transition source map ? finite override ? transition hold ? Warp ? ordinary speed. |
| Points | 2?256 finite pairs; local times are nonnegative and strictly increasing. Source values can change direction, remain flat, or form steep slopes, but there are no vertical jumps. Invalid edits are rejected; malformed persisted Warp arrays fall back safely to ordinary sampling. |
| Source clock | Piecewise-linear interpolation; outside the first/last times hold the endpoint. Clamp the interpolated source to [0, naturalDuration ?? outPoint]. Clamped/flat/exterior samples have zero rate; other samples use the signed segment slope. Interior knots use the following segment. |
| Speed / Reverse | Retained but ignored while warped, including speed-section bypass. Removing Warp restores their ordinary behavior and retains the authored duration. |
| Output interval | Duration is independent of in/out, with a one-frame minimum. Right trim beyond the final point holds; left trim inserts the interpolated start, drops earlier points and rebases the remaining times. Future points beyond the clip end are retained on trim. |
| Split | Each piece retains its source window and receives the points inside its interval plus interpolated boundaries, rebased to its own local time. Interpolate raw source values before clamping to preserve clamp crossings. Redundant exterior hold points may be omitted at the 256-point cap. |
| Prefetch / inverse | Exact min/max over the requested interval includes interior points. No inverse. Nested video descendants remain conservatively admitted over the root interval. Backward video frame selection uses the existing left-frame bias bounded by the media domain. |
| Audio | Signed source-time resampling for preview, mixdown and export; holds supply silence. Pitch is not preserved, even when Preserve Pitch is enabled. Preview uses the processed-buffer cache; all points participate in its key. |
| Persistence | Points and independent duration survive opaque clip JSON, project/composition save/load and history. Linked partners receive separate copies of the same points in one mutation. |
Enabling Warp samples the current contract at the composition frame rate. Affine
and frozen mappings use two points; constant-speed boundary holds keep an exact
clamp point. Speed curves use adaptive point placement against quarter-frame and
authored-key probes, targeting a quarter source frame of error (a safety margin
below the half-frame requirement). Loop wraps become steep segments between
adjacent output-frame starts, one composition frame wide; reverse boundary
selection is retained at those frame starts. These bridges intentionally replace
the original instantaneous wrap between frames. If more than 256 points are
needed, conversion is rejected before either linked clip or history is changed.
The shared toggleClipWarpAction reads fresh keyframes and the active frame rate;
Properties and the timeline context menu use that same mutation entry point.
Adding a point samples the raw current Warp curve and preserves its shape, including out-of-domain portions whose clamped source image is held.
Export acquisition now evaluates the contract source window over the whole
clip output interval, since the mixer crops the export range after clip
rendering. PCM proxies, direct media ranges and nested mixdowns forward their
sample-aligned absolute origin (sourceBufferStart) through the export effect
stage to ClipAudioRenderService. Loops retain a complete source cycle for
constant-speed pitch processing; Warp reads include an interpolation guard
sample. Freeze allocates silent source input without decoding media. The same
origin is used for signed resampling and pitch-processed speed-curve segments.
Processed preview also reads Warp bounds, and incomplete supplied ranges remain
an explicit renderer error rather than silently missing audio.