# How to Manage Gaps in Multi-Track Timelines with Ripple Edit Operations in Clypra

> Efficiently manage multi-track timeline gaps in Clypra using ripple edit operations. Learn how Clypra's gap engine automatically shifts clips for seamless editing.

- Repository: [Abdulkabir Musa/Clypra](https://github.com/AIEraDev/Clypra)
- Tags: how-to-guide
- Published: 2026-07-16

---

**Clypra treats gaps as first-class timeline entities that automatically shift surrounding clips when ripple edit mode is enabled, using pure functions in [`gapEngine.ts`](https://github.com/AIEraDev/Clypra/blob/main/gapEngine.ts) to calculate affected clips while respecting protected gaps.**

Clypra is an open-source video editing framework that reimagines timeline management by treating gaps as manipulable entities rather than passive empty space. When you need to manage gaps in multi-track timelines with ripple edit operations in Clypra, the engine provides precise control over temporal shifting while preventing collisions with protected sections. This architecture separates gap logic from state management, making ripple behavior predictable and testable across complex multi-track projects.

## Understanding Gaps as First-Class Timeline Entities

Unlike traditional video editors that treat empty space as background, Clypra's architecture defines gaps as discrete objects with metadata. In [`src/lib/timeline/gapEngine.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/lib/timeline/gapEngine.ts), gaps carry properties like `protected: true` or type `"protected"`, allowing the ripple engine to distinguish between collapsible space and fixed boundaries. This design enables sophisticated timeline operations where gaps can be inserted, resized, or removed programmatically while maintaining sync relationships with other tracks.

## Core Ripple Edit Operations in Clypra

The gap engine exposes four primary operations for managing timeline gaps. Each returns `affectedClipIds`—an array of clips that must shift to accommodate the temporal change.

### Inserting Gaps with Ripple Effects

The `insertGapWithRipple` function validates duration and start time before creating a `Gap` object. According to the implementation in [`src/lib/timeline/gapEngine.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/lib/timeline/gapEngine.ts), it identifies all clips on the same track whose `startTime` is greater than or equal to the insertion point and less than the next protected gap.

```typescript
import { insertGapWithRipple } from '@/lib/timeline/gapEngine';
import { useTimelineStore } from '@/store/timelineStore';

const { clips, gaps, trackId } = useTimelineStore.getState();

const result = insertGapWithRipple(
  trackId,          // track where the gap will live
  12.0,            // start time (seconds)
  3.0,             // duration (seconds)
  clips,
  gaps,
);

// Shift the returned clips right by the gap duration
if (result.success) {
  result.affectedClipIds.forEach((id) => {
    // UI layer applies the temporal shift
  });
}

```

### Removing Gaps and Shifting Clips Left

When deleting space, `removeGapWithRipple` locates the nearest protected gap before the removed gap to establish a boundary. It selects only those clips that start after the gap and will not cross the protected-gap boundary when shifted left.

```typescript
import { removeGapWithRipple } from '@/lib/timeline/gapEngine';
import { useTimelineStore } from '@/store/timelineStore';

const { clips, gaps } = useTimelineStore.getState();
const gapToDelete = gaps.find(g => g.id === 'gap-123');

if (gapToDelete) {
  const { affectedClipIds } = removeGapWithRipple(gapToDelete, clips, gaps);
  // Shift affected clips left by gapToDelete.duration
}

```

### Resizing Gaps While Respecting Protected Boundaries

The `resizeGap` function computes `deltaTime` (new duration minus old duration). If the gap grows, shifting stops at the next protected gap; if it shrinks, following clips shift left safely without boundary violations.

```typescript
import { resizeGap } from '@/lib/timeline/gapEngine';

const resized = resizeGap(gapToResize, 5.0, clips, gaps);
if (resized.success) {
  // Apply new duration and shift affected clips
}

```

### Packing Tracks to Collapse All Unprotected Gaps

The `packTrack` function removes all unprotected gaps and returns every clip on the track as `affectedClipIds`, allowing the UI to collapse them tightly against each other. This operation replaces gaps entirely rather than rippling them, making it ideal for final timeline compaction.

## Enabling and Controlling Ripple Edit Mode

Ripple edit mode is toggled via the global timeline store at [`src/store/timelineStore.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/store/timelineStore.ts). The boolean `rippleEditEnabled` gates the ripple-aware trimming logic (`rippleTrimClip`) and is exposed through the shortcut store ([`src/store/shortcutStore.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/store/shortcutStore.ts)).

When **disabled**, trimming affects only the selected clip. When **enabled**, trim operations propagate time differences to the `affectedClipIds` returned by the gap engine, creating the classic "ripple" effect that maintains gaps between clips.

```typescript
import { useTimelineStore } from '@/store/timelineStore';

const toggleRipple = () => {
  useTimelineStore.setState(s => ({
    rippleEditEnabled: !s.rippleEditEnabled,
  }));
};

```

## Protected Gaps and Timeline Safety

Protected gaps serve as immovable anchors in the timeline. All gap-engine functions check for `protected: true` before calculating shift distances. This ensures that ripple operations cannot overwrite locked sections, such as synchronized audio tracks or locked reference markers, even when performing aggressive ripple edits across multiple tracks.

## Architecture: Separating Logic from State

Clypra's architecture enforces a clean separation between pure gap logic and state management. The [`gapEngine.ts`](https://github.com/AIEraDev/Clypra/blob/main/gapEngine.ts) file contains pure functions with no side effects, while [`timelineStore.ts`](https://github.com/AIEraDev/Clypra/blob/main/timelineStore.ts) (using Zustand) manages the mutable timeline state. This design makes ripple behavior testable, reusable across multiple tracks, and extensible for features like snap-to-gap functionality.

## Summary

- **Gaps as entities**: Clypra treats gaps as first-class objects with metadata, not empty space.
- **Four core operations**: Insert, remove, resize, and pack gaps using `insertGapWithRipple`, `removeGapWithRipple`, `resizeGap`, and `packTrack` in [`src/lib/timeline/gapEngine.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/lib/timeline/gapEngine.ts).
- **Protected boundaries**: Gaps marked with `protected: true` prevent ripple operations from shifting clips beyond safe boundaries.
- **State separation**: Pure functions in [`gapEngine.ts`](https://github.com/AIEraDev/Clypra/blob/main/gapEngine.ts) calculate effects while [`timelineStore.ts`](https://github.com/AIEraDev/Clypra/blob/main/timelineStore.ts) manages the `rippleEditEnabled` flag and applies shifts.
- **Multi-track support**: The engine returns `affectedClipIds` for the UI to animate, supporting simultaneous operations across multiple tracks.

## Frequently Asked Questions

### What is the difference between ripple edit mode and standard trimming in Clypra?

Standard trimming modifies only the selected clip's duration or position. When ripple edit mode is enabled via `rippleEditEnabled` in [`timelineStore.ts`](https://github.com/AIEraDev/Clypra/blob/main/timelineStore.ts), trimming operations propagate time changes to all subsequent clips on the track, automatically closing gaps or shifting content to maintain synchronization according to the gap engine's calculations.

### How does Clypra prevent ripple edits from affecting locked timeline sections?

Clypra implements **protected gaps**—gaps flagged with `protected: true` or type `"protected"`. Functions like `insertGapWithRipple` and `removeGapWithRipple` search for the nearest protected gap before calculating shift distances, ensuring clips never cross these boundaries during ripple operations.

### Can I apply ripple edits to multiple tracks simultaneously?

While [`gapEngine.ts`](https://github.com/AIEraDev/Clypra/blob/main/gapEngine.ts) functions operate on individual tracks, the returned `affectedClipIds` can be processed across multiple tracks by the UI layer. The [`timelineStore.ts`](https://github.com/AIEraDev/Clypra/blob/main/timelineStore.ts) manages the global ripple state, allowing simultaneous applications of gap operations to selected tracks or the entire timeline.

### Where is the ripple edit toggle stored and how is it accessed?

The toggle resides in [`src/store/timelineStore.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/store/timelineStore.ts) as the boolean `rippleEditEnabled`. The shortcut store ([`src/store/shortcutStore.ts`](https://github.com/AIEraDev/Clypra/blob/main/src/store/shortcutStore.ts)) registers the "Toggle Ripple Edit" action under ID `"toggle-ripple-edit"`, which updates this flag and typically triggers UI feedback via hooks like [`useKeyboardShortcuts.ts`](https://github.com/AIEraDev/Clypra/blob/main/useKeyboardShortcuts.ts).