Skip to main content

Jump Cutting

Sometimes you want to implement a "jump cut" to skip parts of a video (for example to cut out the "uhm"s).
You have the following considerations to make:

The browser might have already preloaded a small portion of the future of the video (around 1-2 seconds). In this case, it would be useful to reuse the same video tag.

A jump far into the future will not be loaded yet. In this case, the video tag should not be re-used, rather a second one should be premounted so it can preload.

If the jump is tiny (<0.45sec), Remotion might not seek due to the acceptableTimeshiftInSeconds prop. You might have to decrease it temporarily.

note

These considerations are only for smooth previews in the browser.
During rendering, videos will be frame-perfect even if you do not follow these considerations.

Re-using the same video tag

For case

1
where you make a small jump, it makes sense to re-use the same video tag.
The following snippet shows how to do this.

In case of

3
we are temporarily disabling the acceptableTimeshiftInSeconds prop for force a seek even if it is a tiny jump.

tsx
import React, {useMemo} from 'react';
import {CalculateMetadataFunction, OffthreadVideo, staticFile, useCurrentFrame} from 'remotion';
 
const fps = 30;
 
type Section = {
trimBefore: number;
trimAfter: number;
};
 
export const SAMPLE_SECTIONS: Section[] = [
{trimBefore: 0, trimAfter: 5 * fps},
{
trimBefore: 7 * fps,
trimAfter: 10 * fps,
},
{
trimBefore: 13 * fps,
trimAfter: 18 * fps,
},
];
 
type Props = {
sections: Section[];
};
 
export const calculateMetadata: CalculateMetadataFunction<Props> = ({props}) => {
const durationInFrames = props.sections.reduce((acc, section) => {
return acc + section.trimAfter - section.trimBefore;
}, 0);
 
return {
fps,
durationInFrames,
};
};
 
export const JumpCuts: React.FC<Props> = ({sections}) => {
const frame = useCurrentFrame();
 
const cut = useMemo(() => {
let summedUpDurations = 0;
for (const section of sections) {
summedUpDurations += section.trimAfter - section.trimBefore;
if (summedUpDurations > frame) {
const trimBefore = section.trimAfter - summedUpDurations;
const offset = section.trimBefore - frame - trimBefore;
 
return {
trimBefore,
firstFrameOfSection: offset === 0,
};
}
}
 
return null;
}, [frame, sections]);
 
if (cut === null) {
return null;
}
 
return (
<OffthreadVideo
pauseWhenBuffering
trimBefore={cut.trimBefore}
// Remotion will automatically add a time fragment to the end of the video URL
// based on `trimBefore` and `trimAfter`. Opt out of this by adding one yourself.
// https://www.remotion.dev/docs/media-fragments
src={`${staticFile('time.mp4')}#t=0,`}
// Force Remotion to seek when it jumps even just a tiny bit
acceptableTimeShiftInSeconds={cut.firstFrameOfSection ? 0.000001 : undefined}
/>
);
};

Pre-mounting a second video tag

In case

2
where you make a large jump, it makes sense to pre-mount a second video tag.
See Playing videos in sequence for how to do this.

See also