Speed Changes with Visual Re-sync

You have a Camtasia project where the voiceover was sped up — say 1.07× — and you want to slow it back to normal (1.0×) while keeping every visual element in sync. This guide shows how set_audio_speed() handles that in one call.

How Camtasia stores speed

Every clip has a scalar field that controls playback speed. The scalar is the ratio of timeline duration to source duration:

scalar = timeline_duration / source_duration
  • scalar = 1 → normal speed

  • scalar < 1 → faster playback (less timeline per source frame)

  • scalar > 1 → slower playback

For example, audio sped up to 1.07× has a scalar of roughly 1/1.07 0.935. Camtasia stores this as a string fraction like "51/101" for exact rational arithmetic.

Clips with a speed change also have metadata.clipSpeedAttribute.value set to true.

The solution

set_audio_speed() does three things:

  1. Finds the audio clip (AMFile) that has a speed change

  2. Calculates the stretch factor needed to reach the target speed

  3. Calls rescale_project() to scale all timing values proportionally

What gets scaled

rescale_project() walks the entire project and scales:

  • Clip start and duration on every track

  • Transition durations

  • Timeline markers (time and endTime)

  • Nested clips inside StitchedMedia (including mediaStart / mediaDuration)

  • Nested clips inside Group internal tracks

  • Scalars on clips that already have speed changes (adjusted so source alignment is preserved)

Complete example

import json
from camtasia.operations import set_audio_speed

with open('project.tscproj') as f:
    data = json.load(f)

factor = set_audio_speed(data, target_speed=1.0)
print(f"Stretched by {float(factor):.4f}x")

with open('project.tscproj', 'w') as f:
    json.dump(data, f, indent=2)

If the audio was at 1.07× (scalar ≈ 51/101), this stretches the entire timeline by ~1.07× so everything plays at normal speed. The audio clip’s scalar is reset to 1 and its clipSpeedAttribute is cleared.

The math

Given a current scalar s and a target speed t:

target_scalar = 1 / t
factor = target_scalar / s

For s = 51/101 (1.07× speed) and t = 1.0:

target_scalar = 1/1 = 1
factor = 1 / (51/101) = 101/51 ≈ 1.9804

Wait — that’s the factor for the scalar, not the timeline. The timeline stretch factor is 101/51 because the audio needs to occupy more timeline ticks to play at normal speed, and everything else stretches to match.

Using rescale_project directly

If you need a custom stretch factor (not tied to audio speed), use rescale_project() directly:

from fractions import Fraction
from camtasia.operations import rescale_project

# Stretch everything by 10% (slow down)
rescale_project(data, Fraction(11, 10))

# Compress by 20% (speed up)
rescale_project(data, Fraction(4, 5))

The factor is multiplicative: values > 1 stretch (slow down), < 1 compress (speed up).

Note

This workflow was validated against a real Camtasia 2026 project — a voiceover recorded at 1.07× was corrected to 1.0× and the project played back correctly on the first try.