smooth-video-warp
v0.1.1
Published
Smoothly compress long stretches of video into short, watchable time warps.
Readme
smooth-video-warp
Smoothly compress long stretches of video into short, watchable time warps.
smooth-video-warp takes a video clip and compresses it to a requested duration while:
- starting at normal playback speed
- accelerating smoothly
- reaching maximum speed at the midpoint
- decelerating symmetrically
- finishing at normal playback speed
Rather than abruptly switching from 1× to some arbitrary fast-forward speed, smooth-video-warp calculates the piecewise constant jerk required to fit the complete source video into the requested target duration.
The result is a smooth, symmetric time warp suitable for software demonstrations, AI agent workflows, builds, renders and other recordings where elapsed work should remain visible without making the viewer wait for it.
1× ─────╮
╰───────────────╮
╰── peak ──╮
╰───────────────╮
╰───── 1×The video is not cut.
Time is compressed.
Example
Given a 37 minute 15 second recording:
37m 15s = 2235 secondscompress it to 18 seconds:
smooth-video-warp agent-working.mp4 --duration 18 --output agent-working.warped.mp4smooth-video-warp probes the source duration and calculates the warp required to satisfy the requested target duration exactly. It prints a report:
Source duration: 37m 15.000s
Target duration: 18.000s
Average speed: 124.1667×
Peak speed: 247.3333×
Jerk: 12.164609 s⁻²and, because --output was given, writes the warped video to agent-working.warped.mp4.
Playback begins at:
1.00×accelerates continuously to:
247.33×at the midpoint then follows the exact reverse profile back to:
1.00×at the end.
The caller does not choose the peak playback speed or jerk.
They fall out of the source duration, requested target duration and warp model.
Installation
Requirements
Runtime requirements:
- Node.js
- FFmpeg
- ffprobe
ffmpeg and ffprobe must be available on the system PATH.
Check with:
node --version
ffmpeg -version
ffprobe -versionnpm
npm install -g smooth-video-warpThis installs the:
smooth-video-warpbinary.
Usage
smooth-video-warp <input> --duration <seconds> [--output <path>] [--samples <n>] [--fps <rate>]--duration specifies the desired duration of the warped output in seconds, and is the only required option. The entire input file is warped.
smooth-video-warp only prints the report unless --output <path> is given. Without --output, no file is written — the command reports the source duration, target duration, average speed, peak speed and jerk, and exits. With --output <path>, the warped video is rendered and written to exactly that path.
# report only — nothing is written
smooth-video-warp agent-working.mp4 --duration 18
# render the warped video to the given path
smooth-video-warp agent-working.mp4 --duration 18 --output agent-working.warped.mp4--samples and --fps only affect rendering and are refused if --output is not given:
--samples <n>— how many source frames are averaged together per output frame (default64).--fps <rate>— the output frame rate, as a decimal (30) or exact rational (30000/1001). Defaults to the source's own frame rate.
smooth-video-warp deliberately does not provide timeline editing. If only part of a longer recording should be compressed, split that section into its own video first.
For example:
presentation.mp4
│
├── normal section
│
├── agent-working.mp4 ← warp this
│
└── normal sectionThen:
smooth-video-warp agent-working.mp4 --duration 12 --output agent-working.warped.mp4The resulting warped clip can be placed back into the presentation using the editor of your choice.
The warp model
smooth-video-warp uses a symmetric piecewise constant-jerk motion profile.
The easiest way to understand the maths is to treat:
source video time as position
and:
output video time as time
Let:
D = source duration
T = target duration
t = current output time
x(t) = source timestamp displayed at output time t
v(t) = instantaneous playback speed
a(t) = acceleration of playback speed
j(t) = jerkThen:
$$ v(t)=x'(t) $$
$$ a(t)=x''(t) $$
and:
$$ j(t)=x'''(t) $$
A playback speed of:
$$ v(t)=7.32 $$
therefore means:
Speed ×7.32and:
$$ x(t)=6324.043 $$
means the source timestamp is:
Time 01h 45m 24.043sWhy jerk?
An abrupt fast-forward might do this:
1× → 100×Playback speed changes instantaneously.
A simple speed ramp improves this, but can still introduce sudden changes in acceleration.
smooth-video-warp instead controls jerk: the rate at which acceleration changes.
Its playback speed therefore behaves more like an elevator or train:
normal speed
↓
acceleration begins gently
↓
acceleration increases
↓
acceleration decreases
↓
maximum speed
↓
deceleration increases
↓
deceleration decreases
↓
normal speedThe source timestamp, playback speed and acceleration remain continuous throughout the warp.
Jerk itself is piecewise constant and changes sign at defined points in the profile.
Jerk profile
Define:
$$ q=\frac{T}{4} $$
The complete warp consists of three constant-jerk regions:
$$ j(t)= \begin{cases} +J & 0 \leq t < q \ -J & q \leq t < 3q \ +J & 3q \leq t \leq T \end{cases} $$
Conceptually:
jerk
+J ──────────┐ ┌──────────
│ │
0 │ │
│ │
-J └─────────────────┘
0 T/4 T/2 3T/4 TThis produces a triangular acceleration profile.
During the first quarter:
acceleration: 0 → maximum
speed: 1× → increasingDuring the second quarter:
acceleration: maximum → 0
speed: increasing → maximumAt the midpoint:
acceleration: 0
speed: maximumThe second half is the exact mirror image.
At the end:
acceleration: 0
speed: 1×Calculating the required jerk
The central constraint is:
$$ x(T)=D $$
The complete source duration must fit exactly into the requested output duration.
For this jerk profile:
$$ D=T+\frac{JT^3}{32} $$
Solving for (J):
$$ \boxed{ J=\frac{32(D-T)}{T^3} } $$
This is why the user does not need to specify a jerk value.
Given:
source duration
target durationthere is a specific jerk magnitude required by this profile.
For compression:
$$ D>T $$
and therefore:
$$ J>0 $$
If:
$$ D=T $$
then:
$$ J=0 $$
and the video simply remains at normal speed.
smooth-video-warp is a compression tool and does not use this model for slow motion where:
$$ T>D $$
A --duration longer than the source duration is refused.
Peak playback speed
The maximum playback speed occurs exactly halfway through the output:
$$ t=\frac{T}{2} $$
For the constant-jerk profile:
$$ v_{\text{peak}}
1+\frac{JT^2}{16} $$
Substituting the calculated jerk gives:
$$ \boxed{ v_{\text{peak}}
2\frac{D}{T}-1 } $$
Define the average compression ratio:
$$ R=\frac{D}{T} $$
Then:
$$ \boxed{ v_{\text{peak}}=2R-1 } $$
So a clip requiring an average playback speed of:
124.1667×has a peak speed of:
247.3333×The peak is not an independently configurable parameter.
It is a consequence of the requested compression.
Quarter-point speed
At:
$$ t=\frac{T}{4} $$
the playback speed is:
$$ v(T/4)
1+\frac{JT^2}{32} $$
Substituting (J):
$$ \boxed{ v(T/4)=\frac{D}{T} } $$
So the playback speed one quarter of the way through the output is exactly the average compression ratio.
By symmetry the same is true at:
$$ t=\frac{3T}{4} $$
For the 37m 15s → 18s example:
t = 0% speed = 1.0000×
t = 25% speed = 124.1667×
t = 50% speed = 247.3333×
t = 75% speed = 124.1667×
t = 100% speed = 1.0000×Source timestamp mapping
The warp needs to answer one fundamental question for every output frame:
At this output time, what point in the original video should be displayed?
That is:
$$ x(t) $$
First quarter
For:
$$ 0 \leq t \leq q $$
where:
$$ q=T/4 $$
the source timestamp is:
$$ \boxed{ x(t)
t+\frac{Jt^3}{6} } $$
Playback speed is:
$$ \boxed{ v(t)
1+\frac{Jt^2}{2} } $$
and acceleration is:
$$ a(t)=Jt $$
Middle half
For:
$$ q < t < 3q $$
define:
$$ u=t-q $$
At the first quarter:
$$ x_q=q+\frac{Jq^3}{6} $$
and:
$$ v_q=1+\frac{Jq^2}{2} $$
The source timestamp is then:
$$ \boxed{ x(t)
x_q + v_qu + \frac{Jq}{2}u^2
\frac{J}{6}u^3 } $$
Playback speed is:
$$ \boxed{ v(t)
v_q + Jqu
\frac{J}{2}u^2 } $$
Acceleration is:
$$ a(t)=Jq-Ju $$
Acceleration reaches zero exactly at:
$$ t=T/2 $$
which is where playback speed reaches its maximum.
Final quarter
The warp is symmetric.
For:
$$ 3q \leq t \leq T $$
the source timestamp can therefore be calculated from the first quarter:
$$ \boxed{ x(t)=D-x(T-t) } $$
and:
$$ \boxed{ v(t)=v(T-t) } $$
This guarantees:
$$ x(0)=0 $$
$$ x(T)=D $$
$$ v(0)=1 $$
$$ v(T)=1 $$
and:
$$ x(t)+x(T-t)=D $$
Example: 37m 15s → 18s
Given:
$$ D=2235 $$
and:
$$ T=18 $$
the average compression ratio is:
$$ R=\frac{2235}{18}=124.1666667 $$
The required jerk is:
$$ J= \frac{32(2235-18)}{18^3} $$
giving:
$$ \boxed{ J=12.1646090535\ s^{-2} } $$
Peak playback speed is:
$$ 2(124.1666667)-1 $$
giving:
$$ \boxed{ 247.3333333\times } $$
Some points on the resulting warp are:
| Output time | Source timestamp | Speed | | ----------: | ---------------: | ------: | | 0.000s | 00m 00.000s | ×1.00 | | 1.000s | 00m 03.027s | ×7.08 | | 2.000s | 00m 18.219s | ×25.33 | | 4.500s | 03m 09.250s | ×124.17 | | 9.000s | 18m 37.500s | ×247.33 | | 13.500s | 34m 05.750s | ×124.17 | | 16.000s | 36m 56.781s | ×25.33 | | 17.000s | 37m 11.973s | ×7.08 | | 18.000s | 37m 15.000s | ×1.00 |
The source video is consumed slowly near the beginning and end then extremely rapidly around the middle.
That is intentional.
Frame mapping
The mathematical warp is continuous.
Video is not.
A video consists of discrete frames.
For an output frame rate (F), output frame (n) corresponds approximately to:
$$ t_n=\frac{n}{F} $$
The desired source timestamp for that frame is:
$$ s_n=x(t_n) $$
and the instantaneous playback multiplier is:
$$ r_n=v(t_n) $$
Conceptually:
output frame 0
↓
output time 0.000s
↓
source time 0.000s
↓
source frame near 0.000s
output frame 1
↓
output time 0.033s
↓
source time x(0.033)
↓
source frame near that timestamp
...
output frame n
↓
output time n / fps
↓
source time x(n / fps)At low playback speeds, most source frames survive.
At very high playback speeds, large numbers of source frames are skipped.
For example, around:
247×roughly one source frame out of every 247 can appear in an output with the same frame rate.
This is expected.
The purpose of the warp is to compress elapsed source time rather than preserve every source frame.
Each output frame is a blend of several nearby source frames rather than a single selected one — --samples controls how many.
Rendering pipeline
Conceptually:
input.mp4
│
▼
ffprobe
│
├── duration
├── frame information
└── media information
│
▼
warp calculation
│
├── required jerk
├── average speed
├── peak speed
└── source-time mapping
│
▼
FFmpeg
│
├── decode source
├── select and blend source frames
├── rebuild output timestamps
└── encode output
│
▼
--output pathAudio
The initial scope is video warping.
Input audio is not copied unchanged into the warped output because it would immediately become unsynchronised with the video.
Narration, music or other presentation audio can be added when the warped clip is incorporated into the final edit.
Input assumptions
smooth-video-warp is aimed primarily at screen recordings and similar demonstration footage.
Constant-frame-rate input is the primary target.
Variable-frame-rate media may require additional handling and is not part of the current compatibility guarantee.
The tool requires a valid video stream whose duration can be determined by ffprobe.
Validation
The following must hold before a warp is attempted:
source duration > 0
target duration > 0
target duration <= source durationIf:
target duration == source durationthe calculated jerk is zero and playback remains at 1×.
If:
target duration > source durationthe request represents slow motion rather than compression and is refused.
The tool also fails clearly if:
- the input file does not exist
- the input cannot be probed
- no usable video stream exists
- FFmpeg is unavailable
- ffprobe is unavailable
- the requested output cannot be encoded
Design principles
Target duration is the user intent
The primary input is:
Make this clip this long.
The user should not need to guess an appropriate peak playback speed.
They should not need to tune acceleration.
They should not need to tune jerk.
The tool calculates those values.
source duration
+
target duration
↓
required jerk
↓
warp curve
↓
peak speed
↓
frame schedulePreserve the process
A hard cut answers:
What happened after the wait?
A warp also shows:
Something continued happening during the wait.
This is useful when the passage of work itself provides context or credibility.
Smooth entry and exit
The transition from normal playback into the warp should feel intentional rather than like a playback glitch.
Likewise, the viewer should arrive back at normal speed smoothly before important content resumes.
Remain small
smooth-video-warp does one thing:
Smoothly compress an entire video clip to a requested duration.
It is not intended to become a general-purpose video editor.
The scope does not include:
- timeline editing
- arbitrary trimming
- sequencing multiple clips
- presentation assembly
- titles
- transitions between clips
- music
- narration
- general audio editing
- slow motion
- arbitrary speed-keyframe editing
- manual animation curves
Those concerns belong in other tools.
Why?
Long-running processes are common in recorded technical demonstrations:
- AI agents reasoning
- AI agents writing code
- builds
- tests
- package installation
- compilation
- rendering
- data processing
- deployment
- long-running commands
Showing these in real time is boring.
Removing them completely with a hard cut can make a demonstration feel disjointed or hide the fact that work actually occurred.
Constant fast-forward is better, but jumping instantly between normal speed and extreme playback speed is visually abrupt.
smooth-video-warp provides another option:
1×
↓
smooth acceleration
↓
very fast
↓
smooth deceleration
↓
1×The viewer sees that time passed.
They see the process continue.
They just don't have to wait for it.
