diff --git a/src/render/filter.rs b/src/render/filter.rs
index a846c7d5353be70b09d875b35d6f59dc5438bc1d..6a76ccc3adf5301abfd9720f61c864edec6c0abd 100644
--- a/src/render/filter.rs
+++ b/src/render/filter.rs
@@ -47,14 +47,12 @@ pub(crate) enum Filter {
 		output: Cow<'static, str>
 	},
 
-	/// Fast forward. Will output before, ff, and after parts separately.
+	/// Fast forward.
 	FastForward {
 		input: Cow<'static, str>,
 		ffinput: Cow<'static, str>,
-		start: Time,
-		duration: Time,
 		multiplier: usize,
-		output: [Cow<'static, str>; 3]
+		output: Cow<'static, str>
 	}
 }
 
@@ -199,90 +197,27 @@ impl Filter {
 			Self::FastForward {
 				input,
 				ffinput,
-				start,
-				duration,
 				multiplier,
 				output
 			} => {
-				let end = *start + *duration;
-
-				// ok so let's start by duplicating the audio and video 3 times
-				let vin = next_tmp_3(filter_idx);
-				let ain = next_tmp_3(filter_idx);
+				let vff = next_tmp(filter_idx);
 				writeln!(
 					complex,
-					"{}split=3{}{}{};",
-					channel('v', input),
-					vin[0],
-					vin[1],
-					vin[2]
+					"{}setpts=PTS/{multiplier}{vff};",
+					channel('v', input)
 				);
 				writeln!(
 					complex,
-					"{}asplit=3{}{}{};",
+					"{}atempo={multiplier}{};",
 					channel('a', input),
-					ain[0],
-					ain[1],
-					ain[2]
-				);
-
-				// next we cut those audio/videos into before, ff, after
-				let vcut = next_tmp_3(filter_idx);
-				let acut = next_tmp_3(filter_idx);
-				writeln!(complex, "{}trim=duration={start}{};", vin[0], vcut[0]);
-				writeln!(complex, "{}atrim=duration={start}{};", ain[0], acut[0]);
-				writeln!(
-					complex,
-					"{}trim=start={start}:duration={duration},setpts=PTS-STARTPTS{};",
-					vin[1], vcut[1]
-				);
-				writeln!(
-					complex,
-					"{}atrim=start={start}:duration={duration},asetpts=PTS-STARTPTS{};",
-					ain[1], acut[1]
-				);
-				writeln!(
-					complex,
-					"{}trim=start={end},setpts=PTS-STARTPTS{};",
-					vin[2], vcut[2]
-				);
-				writeln!(
-					complex,
-					"{}atrim=start={end},asetpts=PTS-STARTPTS{};",
-					ain[2], acut[2]
+					channel('a', output)
 				);
-
-				// now we speed up the ff part
-				let vff = next_tmp(filter_idx);
-				let aff = next_tmp(filter_idx);
-				writeln!(complex, "{}setpts=PTS/{multiplier}{vff};", vcut[1]);
-				writeln!(complex, "{}atempo={multiplier}{aff};", acut[1]);
-
-				// and we overlay the vff part
-				let voverlay = next_tmp(filter_idx);
 				writeln!(
 					complex,
-					"{vff}{}overlay=x=main_w/2-overlay_w/2:y=main_h/2-overlay_h/2{voverlay};",
-					channel('v', ffinput)
+					"{vff}{}overlay=x=main_w/2-overlay_w/2:y=main_h/2-overlay_h/2{};",
+					channel('v', ffinput),
+					channel('v', output)
 				);
-
-				// and finally we place everything into the output
-				for (i, o) in [
-					(&vcut[0], &output[0]),
-					(&voverlay, &output[1]),
-					(&vcut[2], &output[2])
-				] {
-					write!(complex, "{i}null{};", channel('v', o));
-				}
-				writeln!(complex);
-				for (i, o) in [
-					(&acut[0], &output[0]),
-					(&aff, &output[1]),
-					(&acut[2], &output[2])
-				] {
-					write!(complex, "{i}anull{};", channel('a', o));
-				}
-				writeln!(complex);
 			}
 		}
 
diff --git a/src/render/mod.rs b/src/render/mod.rs
index ec0677cc63e2154f466d382d82508ae9fc3affb0..1c963e022d1ef57a25cbcf97bd753bd5c356cdb2 100644
--- a/src/render/mod.rs
+++ b/src/render/mod.rs
@@ -34,6 +34,9 @@ const TRANSITION_LEN: Time = Time {
 	micros: 200_000
 };
 const FF_MULTIPLIER: usize = 8;
+// logo sizes at full hd, will be scaled to source resolution
+const FF_LOGO_SIZE: usize = 128;
+const LOGO_SIZE: usize = 96;
 
 fn cmd() -> Command {
 	let mut cmd = Command::new("busybox");
@@ -227,7 +230,7 @@ impl<'a> Renderer<'a> {
 			include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/logo.svg"))
 		)?;
 		let logo_png = self.target.join("logo.png");
-		svg2png(&logo_svg, &logo_png, 150 * source_res.width() / 1920)?;
+		svg2png(&logo_svg, &logo_png, LOGO_SIZE * source_res.width() / 1920)?;
 
 		// copy fastforward then render to png
 		let fastforward_svg = self.target.join("fastforward.svg");
@@ -242,7 +245,7 @@ impl<'a> Renderer<'a> {
 		svg2png(
 			&fastforward_svg,
 			&fastforward_png,
-			128 * source_res.width() / 1920
+			FF_LOGO_SIZE * source_res.width() / 1920
 		)?;
 
 		Ok(())
@@ -270,20 +273,85 @@ impl<'a> Renderer<'a> {
 		let ff = ffmpeg.add_input(FfmpegInput::new(self.target.join("fastforward.png")));
 
 		let mut part1: Cow<'static, str> = intro.into();
-		let mut part2: Cow<'static, str> = rec.into();
 		let mut part3: Cow<'static, str> = outro.into();
 
-		// trim the recording
-		let rectrim = "rectrim";
+		// the recording is fun because of all the fast forwarding
+		let mut part2 = VecDeque::new();
+		let mut part2_start_of_the_end = None;
+		let mut part2_end_of_the_start = None;
+
+		// ok so ff is fun. we will add the ff'ed section as well as the part between
+		// the previous ff'ed section and our new section, unless we are the first
+		project.source.fast.sort();
+		for (i, (ff_st, ff_end)) in project.source.fast.iter().rev().enumerate() {
+			if let Some(prev_end) = part2_end_of_the_start {
+				let recffbetween = format!("recff{i}between");
+				ffmpeg.add_filter(Filter::Trim {
+					input: rec.clone().into(),
+					start: Some(*ff_end),
+					duration: Some(prev_end),
+					output: recffbetween.clone().into()
+				});
+				part2.push_front(recffbetween.into());
+			} else {
+				part2_start_of_the_end = Some(*ff_end);
+			}
+			part2_end_of_the_start = Some(*ff_st);
+
+			let recffpart = format!("recff{i}part");
+			ffmpeg.add_filter(Filter::Trim {
+				input: rec.clone().into(),
+				start: Some(*ff_st),
+				duration: Some(*ff_end - *ff_st),
+				output: recffpart.clone().into()
+			});
+
+			let recff = format!("recff{i}");
+			ffmpeg.add_filter(Filter::FastForward {
+				input: recffpart.into(),
+				ffinput: ff.clone().into(),
+				multiplier: FF_MULTIPLIER,
+				output: recff.clone().into()
+			});
+			part2.push_front(recff.into());
+		}
+
+		// if the recording was not ff'ed, perform a normal trim
 		let start = project.source.start.unwrap();
-		let duration = project.source.end.unwrap() - start;
-		ffmpeg.add_filter(Filter::Trim {
-			input: part2,
-			start: Some(start),
-			duration: Some(duration),
-			output: rectrim.into()
-		});
-		part2 = rectrim.into();
+		let end = project.source.end.unwrap();
+		let part2_last_part_duration;
+		if part2.is_empty() {
+			let rectrim = "rectrim";
+			part2_last_part_duration = end - start;
+			ffmpeg.add_filter(Filter::Trim {
+				input: rec.into(),
+				start: Some(start),
+				duration: Some(part2_last_part_duration),
+				output: rectrim.into()
+			});
+			part2.push_back(rectrim.into());
+		}
+		// otherwise add the first and last parts separately
+		else {
+			let rectrimst = "rectrimst";
+			ffmpeg.add_filter(Filter::Trim {
+				input: rec.clone().into(),
+				start: Some(start),
+				duration: Some(part2_end_of_the_start.unwrap() - start),
+				output: rectrimst.into()
+			});
+			part2.push_front(rectrimst.into());
+
+			let rectrimend = "rectrimend";
+			part2_last_part_duration = end - part2_start_of_the_end.unwrap();
+			ffmpeg.add_filter(Filter::Trim {
+				input: rec.into(),
+				start: Some(part2_start_of_the_end.unwrap()),
+				duration: Some(part2_last_part_duration),
+				output: rectrimend.into()
+			});
+			part2.push_back(rectrimend.into());
+		}
 
 		// fade out the intro
 		let introfade = "introfade";
@@ -299,7 +367,7 @@ impl<'a> Renderer<'a> {
 		// fade in the recording
 		let recfadein = "recfadein";
 		ffmpeg.add_filter(Filter::Fade {
-			input: part2,
+			input: part2.pop_front().unwrap(),
 			direction: "in",
 			start: Time {
 				seconds: 0,
@@ -308,18 +376,18 @@ impl<'a> Renderer<'a> {
 			duration: TRANSITION_LEN,
 			output: recfadein.into()
 		});
-		part2 = recfadein.into();
+		part2.push_front(recfadein.into());
 
 		// fade out the recording
 		let recfadeout = "recfadeout";
 		ffmpeg.add_filter(Filter::Fade {
-			input: part2,
+			input: part2.pop_back().unwrap(),
 			direction: "out",
-			start: duration - TRANSITION_LEN,
+			start: part2_last_part_duration - TRANSITION_LEN,
 			duration: TRANSITION_LEN,
 			output: recfadeout.into()
 		});
-		part2 = recfadeout.into();
+		part2.push_back(recfadeout.into());
 
 		// fade in the outro
 		let outrofade = "outrofade";
@@ -335,32 +403,8 @@ impl<'a> Renderer<'a> {
 		});
 		part3 = outrofade.into();
 
-		// prepare list for concat
-		let mut parts = VecDeque::new();
-		parts.push_back(part2);
-
-		// fast-forward the requested parts in reverse order
-		project.source.fast.sort();
-		for (i, (ff_st, ff_end)) in project.source.fast.iter().rev().enumerate() {
-			let recff = [
-				format!("recff{i}b").into(),
-				format!("recff{i}ff").into(),
-				format!("recff{i}a").into()
-			];
-			ffmpeg.add_filter(Filter::FastForward {
-				input: parts.pop_front().unwrap(),
-				ffinput: ff.clone().into(),
-				start: *ff_st - start,
-				duration: *ff_end - *ff_st,
-				multiplier: FF_MULTIPLIER,
-				output: recff.clone()
-			});
-			parts.push_front(recff[2].clone());
-			parts.push_front(recff[1].clone());
-			parts.push_front(recff[0].clone());
-		}
-
 		// concatenate everything
+		let mut parts = part2;
 		parts.push_front(part1);
 		parts.push_back(part3);
 		let concat = "concat";