Internals

Webcam animations & camera regions

Camera regions mark segments of the timeline where the webcam changes state: fullscreen, hidden, or a custom PiP layout. Each boundary can have its own animated transition, rendered by the compositor at export.

Region Types

Three camera states

Outside any defined region the webcam renders using the global PiP layout from the editor properties panel. Regions override that global state for their duration.

Fullscreen

The webcam fills the whole canvas for the region's duration. For talking-head segments where you replace the screen recording with the speaker.

Hidden

The webcam disappears for the region's duration, even if PiP is on globally. For segments where the webcam would be distracting.

Custom

A PiP overlay with layout and style that override the global defaults. Each custom region gets its own position, size, aspect ratio, corner radius, border, shadow, and flip.

CameraRegionData structure

CameraRegionData {
  id:                     UUID
  startSeconds:           Double
  endSeconds:             Double
  type:                   .fullscreen | .hidden | .custom

  // Custom layout overrides (nil = use global defaults)
  customLayout:           CameraLayout?       // position, size
  customCameraAspect:     CameraAspect?       // original, 1:1, 4:3, 16:9, 9:16
  customCornerRadius:     CGFloat?
  customShadow:           CGFloat?
  customBorderWidth:      CGFloat?
  customBorderColor:      CodableColor?
  customMirrored:         Bool?

  // Transitions
  entryTransition:        CameraTransitionType?
  entryTransitionDuration:Double?             // seconds
  exitTransition:         CameraTransitionType?
  exitTransitionDuration: Double?
}
Transitions

Fade, scale & slide

Every region boundary can have its own transition type and duration. Entry and exit are set separately, so a region can fade in and slide out.

Fade

Opacity goes from 0 to 1 on entry and 1 to 0 on exit. Simple and works at any speed.

Scale

Scales from nothing to full size on entry, shrinks back on exit. For fullscreen-to-PiP transitions, the region morphs between both rects instead of just scaling from center.

Slide

Slides in from below the canvas on entry, slides back down on exit. The distance is the full camera height so it starts and ends off-screen.

None

Hard cut with no animation. For fast-paced content or when the edit around it already provides context.

Progress computation

// elapsed = time since region start
// remaining = time until region end

if elapsed < entryDuration:
  progress = smoothstep(elapsed / entryDuration)  // 0 → 1
elif remaining < exitDuration:
  progress = smoothstep(remaining / exitDuration) // 1 → 0
else:
  progress = 1.0                                  // hold

smoothstep(t) = t³ × (6t² − 15t + 10)

Effect applied per transition type

Fade:
  context.setAlpha(progress)

Scale (PiP):
  scale at camera center:
  context.scaleBy(x: progress, y: progress)

Scale (Fullscreen ↔ PiP):
  rect = lerp(fullscreenRect, pipRect, progress)
  cornerRadius = lerp(0, pipCornerRadius, progress)

Slide:
  slideY = cameraRect.height × (1 − progress)
  context.translateBy(x: 0, y: −slideY)
Transition mechanics

How transitions are composed

All transition logic runs in CameraVideoCompositor, a custom AVVideoCompositing called once per output frame. No pre-processing pass; everything is computed and drawn inline.

Independent Entry & Exit Durations

Entry and exit transitions can use different types and different durations on the same region. For example, a fast fade-in followed by a slow slide-out.

Smoothstep Easing on All Transitions

All progress values go through smoothstep (t³(6t²−15t+10)) for ease-in-out with zero velocity at both ends.

Hold Phase Between Transitions

The region is fully active between the end of its entry transition and the start of its exit transition. During the hold phase the progress value is 1.0 and no interpolation is applied.

Per-region Styling During Transitions

Corner radius, border width, and other properties animate along with position and opacity. For scale transitions, corner radius interpolates proportionally with the scale factor.

Compositing pipeline

Frame rendering order

Each exported frame goes through a fixed five-stage pipeline in CameraVideoCompositor.renderFrame(). Camera regions and their transitions only affect stage 5 — the webcam layer — leaving background, video, and cursor unmodified.

Per-frame Region Lookup

CameraVideoCompositor evaluates the region list on every frame. It binary-searches the active region for the current composition timestamp and computes the transition progress before any drawing begins.

Rendering Order

Each frame is composited in a fixed order: background first, then the screen video (with zoom applied), then the cursor overlay, and finally the webcam. Camera regions only affect the webcam layer.

Fullscreen → PiP Scale Morph

At a fullscreen/PiP boundary with a scale transition, the compositor interpolates the camera rect from fullscreen bounds to PiP rect. Position and size change together.

Aspect Ratio Preservation

The webcam is always aspect-filled into its destination rect. During transitions that rect is the interpolated intermediate, so aspect fill stays correct throughout.

Per-frame pipeline

renderFrame(at compositionTime):
  1. drawBackground()            // solid color / gradient / image
  2. drawScreenVideo()           // source video cropped by zoom rect
  3. drawCursorOverlay()         // cursor + click highlights
  4. lookupActiveRegion(at time) // binary search through regions
  5. drawWebcam():
       if region == .hidden:     skip (with exit transition if leaving)
       if region == .fullscreen: fill canvas (with entry/exit transitions)
       if region == .custom:     render PiP at custom layout
       if no region:             render PiP at global layout
Timeline editor

Camera track in the editor

Camera regions live on a dedicated track in the timeline. The preview updates live, so you can scrub through transitions before exporting.

Dedicated Camera Track

Sits below the video and audio tracks.

Add Regions by Double-clicking

Double-click empty space to insert a fullscreen region. It extends to the next region or the end of the video.

Drag to Move and Resize

Drag to reposition, drag the edges to resize. Regions can't overlap their neighbors.

Edit Popover

Right-click (or click edit) to open a popover where you change the region type, set transitions and durations, and override styling for custom regions.