Skip to content

Vertical clip from a podcast ​

A 9:16 cut of a landscape podcast: one clip per sentence with a crop on whoever speaks, a name tag on each speaker change, short karaoke captions.

What the manifest gives it ​

The podcast transcript carries a speaker per word, which chose each clip's crop. The crop width 0.3164 is a 9:16 window on a 16:9 source.

What the file shows ​

  • The composition is 1080×1920; fit="cover" and a crop per clip do the reframing.
  • The crop is a literal per clip, so the editor can nudge each one; a per-speaker face box in the manifest would let the model place them blind.
  • maxChars={28} keeps generated cues to two short lines on a phone.

The file ​

tsx
import { z } from "zod";

import { Captions, Clip, Composition, Title, Track } from "@cutcraft/kit";

export const schema = z.object({
  accent: z.string().describe("Name-tag and karaoke colour"),
});

export const defaults = { accent: "#5ad1ff" };

/**
 * A vertical clip from a landscape podcast. The composition is 9:16; each
 * sentence is its own clip with a crop that frames whoever speaks, chosen
 * from the transcript's speaker field. A name tag appears when the speaker
 * changes. Captions are generated, short, and coloured word by word.
 */
export default function Video({ accent }: z.infer<typeof schema>) {
  return (
    <Composition fps={30} width={1080} height={1920} background={{ mode: "color", color: "#000000" }}>
      <Track id="main" name="Dialogue" role="dialogue">
        {/* A 9:16 window on the 16:9 source: 0.3164 of the width is 1080 of 1920 scaled to the frame. */}
        <Clip
          id="s0"
          src="asset:podcast"
          in="word:0"
          out="word:4.end"
          fit="cover"
          transform={{ crop: { x: 0.08, y: 0, width: 0.3164, height: 1 } }}
        />
        <Clip
          id="s1"
          src="asset:podcast"
          in="word:5"
          out="word:8.end"
          fit="cover"
          transform={{ crop: { x: 0.6, y: 0, width: 0.3164, height: 1 } }}
        />
        <Clip
          id="s2"
          src="asset:podcast"
          in="word:9"
          out="word:15.end"
          fit="cover"
          transform={{ crop: { x: 0.08, y: 0, width: 0.3164, height: 1 } }}
        />
        <Clip
          id="s5"
          src="asset:podcast"
          in="word:35"
          out="word:49.end"
          fit="cover"
          transform={{ crop: { x: 0.6, y: 0, width: 0.3164, height: 1 } }}
        />
        <Clip
          id="s6"
          src="asset:podcast"
          in="word:50"
          out="word:52.end"
          fit="cover"
          transform={{ crop: { x: 0.08, y: 0, width: 0.3164, height: 1 } }}
        />
        <Clip
          id="s7"
          src="asset:podcast"
          in="word:53"
          out="word:63.end"
          fit="cover"
          transform={{ crop: { x: 0.6, y: 0, width: 0.3164, height: 1 } }}
        />
        <Clip
          id="s8"
          src="asset:podcast"
          in="word:64"
          out="word:66.end"
          fit="cover"
          transform={{ crop: { x: 0.08, y: 0, width: 0.3164, height: 1 } }}
        />
        <Clip
          id="s9"
          src="asset:podcast"
          in="word:67"
          out="word:79.end"
          fit="cover"
          transform={{ crop: { x: 0.6, y: 0, width: 0.3164, height: 1 } }}
        />
        <Clip
          id="s10"
          src="asset:podcast"
          in="word:80"
          out="word:84.end"
          fit="cover"
          transform={{ crop: { x: 0.08, y: 0, width: 0.3164, height: 1 } }}
        />
      </Track>

      <Track id="tags" name="Name tags">
        <Title
          id="tag-s0"
          anchor="s0"
          offset={0.2}
          duration={1.8}
          size={48}
          weight={700}
          color={accent}
          align="left"
          valign="bottom"
          transform={{ position: { x: 60, y: -360 } }}
          animation={{ preset: "slide-left", in: 0.3, out: 0.2 }}
        >
          Ana
        </Title>
        <Title
          id="tag-s1"
          anchor="s1"
          offset={0.2}
          duration={1.8}
          size={48}
          weight={700}
          color={accent}
          align="left"
          valign="bottom"
          transform={{ position: { x: 60, y: -360 } }}
          animation={{ preset: "slide-left", in: 0.3, out: 0.2 }}
        >
          Ben
        </Title>
        <Title
          id="tag-s2"
          anchor="s2"
          offset={0.2}
          duration={1.8}
          size={48}
          weight={700}
          color={accent}
          align="left"
          valign="bottom"
          transform={{ position: { x: 60, y: -360 } }}
          animation={{ preset: "slide-left", in: 0.3, out: 0.2 }}
        >
          Ana
        </Title>
        <Title
          id="tag-s5"
          anchor="s5"
          offset={0.2}
          duration={1.8}
          size={48}
          weight={700}
          color={accent}
          align="left"
          valign="bottom"
          transform={{ position: { x: 60, y: -360 } }}
          animation={{ preset: "slide-left", in: 0.3, out: 0.2 }}
        >
          Ben
        </Title>
        <Title
          id="tag-s6"
          anchor="s6"
          offset={0.2}
          duration={1.8}
          size={48}
          weight={700}
          color={accent}
          align="left"
          valign="bottom"
          transform={{ position: { x: 60, y: -360 } }}
          animation={{ preset: "slide-left", in: 0.3, out: 0.2 }}
        >
          Ana
        </Title>
        <Title
          id="tag-s7"
          anchor="s7"
          offset={0.2}
          duration={1.8}
          size={48}
          weight={700}
          color={accent}
          align="left"
          valign="bottom"
          transform={{ position: { x: 60, y: -360 } }}
          animation={{ preset: "slide-left", in: 0.3, out: 0.2 }}
        >
          Ben
        </Title>
        <Title
          id="tag-s8"
          anchor="s8"
          offset={0.2}
          duration={1.8}
          size={48}
          weight={700}
          color={accent}
          align="left"
          valign="bottom"
          transform={{ position: { x: 60, y: -360 } }}
          animation={{ preset: "slide-left", in: 0.3, out: 0.2 }}
        >
          Ana
        </Title>
        <Title
          id="tag-s9"
          anchor="s9"
          offset={0.2}
          duration={1.8}
          size={48}
          weight={700}
          color={accent}
          align="left"
          valign="bottom"
          transform={{ position: { x: 60, y: -360 } }}
          animation={{ preset: "slide-left", in: 0.3, out: 0.2 }}
        >
          Ben
        </Title>
        <Title
          id="tag-s10"
          anchor="s10"
          offset={0.2}
          duration={1.8}
          size={48}
          weight={700}
          color={accent}
          align="left"
          valign="bottom"
          transform={{ position: { x: 60, y: -360 } }}
          animation={{ preset: "slide-left", in: 0.3, out: 0.2 }}
        >
          Ana
        </Title>
      </Track>

      <Captions
        id="captions"
        src="asset:podcast"
        style={{ position: "bottom", size: 56, highlight: accent }}
        animation="karaoke"
        maxChars={28}
      />
    </Composition>
  );
}

What the editor can do ​

  • Drag a clip's crop: one transform.crop literal changes.
  • Edit a cue: all cues become literal, then the edit applies.

What to check ​

  • No lint warnings expected.
  • captureSheet([0.3, 2.2, 6, 14]): Ana, Ben, a name tag, a caption with the current word highlighted.

MIT licensed.