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 speedscalar < 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:
Finds the audio clip (
AMFile) that has a speed changeCalculates the stretch factor needed to reach the target speed
Calls
rescale_project()to scale all timing values proportionally
What gets scaled¶
rescale_project() walks the entire project and scales:
Clip
startanddurationon every trackTransition durations
Timeline markers (time and endTime)
Nested clips inside
StitchedMedia(includingmediaStart/mediaDuration)Nested clips inside
Groupinternal tracksScalars 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.