{
  "slug": "rhythm-interrupt-moves",
  "title": "Rhythm interrupt moves",
  "tagline": "Two rhythm-breaking techniques: jump-cut-punch-in, a three-step jump-cut push-in, and strobe-black-frames, strobing black frames.",
  "description": "Two rhythm-breaking techniques: jump-cut-punch-in, a three-step jump-cut push-in, and strobe-black-frames, strobing black frames.",
  "category": "rhythm",
  "tags": [
    "shotcraft",
    "remotion",
    "rhythm"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 4.54,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/rhythm-interrupt-moves/poster-555ebee9990c.jpg",
    "video": "https://gimgs.net/motion/media/rhythm-interrupt-moves/video-64118a579674.mp4"
  },
  "variants": [
    {
      "key": "jump-cut-punch-in",
      "label": "jump-cut-punch-in",
      "description": "transform-origin is pinned to the target's center, scale jumps through three steps with zero tweening, each jump getting a 2f deepening pulse as a tick.",
      "use": "Closing in on a core metric step by step; a documentary-style stare",
      "video": "https://gimgs.net/motion/media/rhythm-interrupt-moves/video-64118a579674.mp4",
      "poster": "https://gimgs.net/motion/media/rhythm-interrupt-moves/poster-555ebee9990c.jpg"
    },
    {
      "key": "strobe-black-frames",
      "label": "strobe-black-frames",
      "description": "Full-screen black frames flash according to a hardcoded frame-number table (2f each, intervals tightening from 8f down to 3f), and the last flash lifts straight into a hard-cut scale-up landing.",
      "use": "The countdown build-up before the film's highest climax",
      "video": "https://gimgs.net/motion/media/rhythm-interrupt-moves/video-da6562586324.mp4",
      "poster": "https://gimgs.net/motion/media/rhythm-interrupt-moves/poster-strobe-black-frames-6671053b43bd.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/rhythm-interrupt-moves.html",
  "json_url": "https://gimgs.net/motion/rhythm-interrupt-moves.json",
  "template_url": "https://gimgs.net/motion/rhythm-interrupt-moves/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/rhythm-interrupt-moves.md",
  "updated_at": "2026-09-24T07:40:17.496Z",
  "prompt": "## When to use\n\nUsing \"breaking continuity\" itself as a rhythm device: a jolting push-in (B), a suffocating approach (C); complements beat-cut-moves (cut-point layout) and montage-rhythm (section breathing)\n\n## Intention\n\nThe library's other rhythm cards all handle \"how to cut, how densely\"; these two handle **how to break**: the viewer expects continuity, and you break it on purpose — the way you break it is the message. B breaks in space — the same composition jumps three times with no in-between (1x→1.6x→2.6x), a Godard-style jump-cut jolt, with more of a \"look here, closer, that's it\" command than a smooth push-in; C breaks in existence — the image and pure black alternate in a strobe with tightening intervals, a suffocating countdown before a climax. Choose by use case: forced focus on a metric uses B, building pressure before a climax uses C.\n\n## Duration & energy\n\n- Duration: B ~4.5s / C ~4.5s\n- Energy: B Medium / C High\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n库内节奏卡管的都是\"怎么切、切多密\"；这两式管**怎么断**——观众预期\n连续，你偏打断，打断方式即表达：B 是空间断——同构图三次无补间跳大\n（1x→1.6x→2.6x），戈达尔跳切的顿挫，比连续推近更有\"看这里、再近点、\n就是它\"的指令感；C 是存在断——画面与纯黑交替频闪且间隔收紧，\n高潮前的窒息倒数。选型：强制聚焦指标用 B，高潮前蓄压用 C。\n\n## 两式选型\n| 式 | 做法 | 适用 |\n|----|------|------|\n| B jump-cut-punch-in | transform-origin 钉目标中心，三档 scale 阶梯跳变（零补间），每跳 2f 加深脉冲当 tick | 逐级逼近核心指标；纪录片式盯住 |\n| C strobe-black-frames | 全屏黑帧按写死帧号表闪现（每次 2f，间隔 8f→3f 收敛），末闪掀开即硬切放大落定 | 全片最高潮前的倒数蓄压 |\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| B 跳挡 | 1.0→1.6→2.6，各 hold ≥35f | 挡差 <1.5× 读作画面抖了一下 |\n| B tick | 跳变帧起 2f brightness 0.92 全画面脉冲 | 无 tick 的跳切读作丢帧 |\n| C 帧号表 | [40,48,55,61,66,70,73,76,79] 各 2f 纯黑 | 间隔必须收敛；等距频闪只是闪没有\"逼近\" |\n| C 落锤 | 末闪掀开一帧到位 scale 1.35 + 2f 加深脉冲 | 掀开后还在原构图，频闪就白憋了 |\n| 收尾 | B 末挡 ≥45f / C 硬切后 ≥50f 真静止 | 打断系事后 hold 从重给 |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- 两式与 speed-ramp-freeze 都动\"时间轴\"，同一镜头只能有一个时间\n  操纵者\n- C 式**光敏警示**：频闪画面需注意光敏性癫痫观众提示；且必须配乐\n  渐强同步（无声频闪读作信号故障），全片 ≤1 次\n- B 式三挡构图必须同 origin（钉死目标中心）——每挡重新构图就是\n  三个镜头，不是跳切\n- 声音强依赖：B 每跳一声 tick、C 每闪一声打点（sound-design §4.5）\n\n## 参考实现\ndemos/rhythm/rhythm-interrupt-moves/\n（JumpCutPunchIn.tsx / StrobeBlackFrames.tsx）\n\n## Reference implementation (Remotion)\n\n- demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx\n- demos/rhythm/rhythm-interrupt-moves/StrobeBlackFrames.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/rhythm-interrupt-moves/StrobeBlackFrames.tsx\n\nThe component imports shared fixtures from `demos/_fixtures` and helpers from `assets/lib` in the repo; SKILL.md there documents the render workflow.\n",
  "flow": [
    {
      "step": "Read the recipe",
      "note": "Prompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points."
    },
    {
      "step": "Get the code",
      "tool": "git",
      "cmd": "git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install"
    },
    {
      "step": "Register + render",
      "tool": "remotion",
      "cmd": "npx remotion render <CompositionId> out.mp4   # component: demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx, demos/rhythm/rhythm-interrupt-moves/StrobeBlackFrames.tsx"
    },
    {
      "step": "Or re-implement",
      "note": "Port the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing."
    }
  ],
  "files": [
    {
      "path": "demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx"
    },
    {
      "path": "demos/rhythm/rhythm-interrupt-moves/StrobeBlackFrames.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/rhythm-interrupt-moves/StrobeBlackFrames.tsx"
    }
  ],
  "contract": "React/Remotion composition (TSX) from the open-source video-shotcraft library (Apache-2.0). It is not a browser-playable template: clone the repo, register the component in a Remotion project and render with `npx remotion render`. The recipe (prompt) describes timing, easing and parameters so you can also re-implement the shot in any engine.",
  "render": {
    "engine": "remotion",
    "frames": 136,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx",
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/rhythm/rhythm-interrupt-moves/StrobeBlackFrames.tsx"
    ],
    "note": "Clone https://github.com/Vincentwei1021/video-shotcraft (npm install). The component lives under demos/{category}/{name}/ and imports shared fixtures from demos/_fixtures and helpers from assets/lib. Copy it (with those imports) into a Remotion project, register a <Composition> with the template's width/height/fps/duration in Root.tsx, then `npx remotion render <CompositionId> out.mp4`. SKILL.md in the repo documents the full Claude Code / Codex workflow. The prompt below is the shot recipe (timing, easing, parameter table, pitfalls) — enough to re-implement the shot in HTML/WAAPI or another engine if you prefer."
  },
  "source": "// ===== demos/rhythm/rhythm-interrupt-moves/JumpCutPunchIn.tsx =====\n// 三级跳切推近（jump-cut-punch-in）——节奏剪辑｜戈达尔跳切/纪录片 punch-in。\n// FakeDashboard A 固定构图，transform-origin 钉在第 2 行中间卡中心\n// (1070px, 817px)。三次无补间硬切逐级放大：刻意零 interpolate，\n// 切换帧一帧到位。每次跳切帧叠 2f 整画面 brightness 0.92 加深脉冲当 tick。\n// 关键帧：0–34 scale 1.0 hold → 帧 35 直跳 1.6（35–36 加深脉冲）→ hold →\n// 帧 70 直跳 2.6（70–71 加深脉冲）→ 72–134 全静止（63f ≥45f）。\nimport React from 'react';\nimport { useCurrentFrame } from 'remotion';\nimport { G, FakeDashboard, TitleBlock } from '../../_fixtures/Fixtures';\n\n// 目标卡片（3×2 网格第 2 行中间卡）几何：\n// 侧栏 220 + 内边距 36；卡宽 (1920-220-72-56)/3 = 524.67，卡高 (1080-72-72-28)/2 = 454\nconst CARD_L = 220 + 36 + 524.67 + 28; // ≈808.67\nconst CARD_T = 72 + 36 + 454 + 28; // 590\nconst CARD_W = 524.67;\nconst CARD_H = 454;\nconst ORIGIN_X = CARD_L + CARD_W / 2; // ≈1071\nconst ORIGIN_Y = CARD_T + CARD_H / 2; // 817\n\n// 三级硬切：零补间，帧 35 / 帧 70 一帧到位\nconst scaleAt = (f: number): number => (f < 35 ? 1.0 : f < 70 ? 1.6 : 2.6);\n\n// 跳切帧 2f 加深脉冲（整画面 brightness 0.92）\nconst pulseAt = (f: number): number =>\n  (f >= 35 && f <= 36) || (f >= 70 && f <= 71) ? 0.92 : 1;\n\nexport const JumpCutPunchIn: React.FC = () => {\n  const frame = useCurrentFrame();\n  const s = scaleAt(frame);\n  const b = pulseAt(frame);\n\n  return (\n    <div\n      style={{\n        width: 1920,\n        height: 1080,\n        background: G.bg,\n        position: 'relative',\n        overflow: 'hidden',\n        filter: `brightness(${b})`,\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          inset: 0,\n          transform: `scale(${s})`,\n          transformOrigin: `${ORIGIN_X}px ${ORIGIN_Y}px`,\n        }}\n      >\n        <FakeDashboard variant=\"A\" />\n        {/* 目标卡片标记：随构图一起缩放，提示 punch-in 落点 */}\n        <div\n          style={{\n            position: 'absolute',\n            left: CARD_L - 10,\n            top: CARD_T - 10,\n            width: CARD_W + 20,\n            height: CARD_H + 20,\n            border: `3px dashed ${G.mid}`,\n            borderRadius: 20,\n            boxSizing: 'border-box',\n          }}\n        />\n      </div>\n      {/* 标签不随缩放，固定左上角 */}\n      <div style={{ position: 'absolute', left: 260, top: 20 }}>\n        <TitleBlock text=\"JUMP CUT PUNCH-IN\" size={54} />\n      </div>\n    </div>\n  );\n};\n\n\n// ===== demos/rhythm/rhythm-interrupt-moves/StrobeBlackFrames.tsx =====\n// 频闪黑帧（strobe-black-frames）——节奏剪辑｜VJ strobe / 剪映闪黑。\n// FakeDashboard A 全程缓慢推近蓄张力（scale 1.0→1.05，Easing.in(quad)，\n// 越到后段越快）。40–80f 频闪窗：全屏纯黑 #0c0c0c 按写死帧号表闪现，\n// 每次持续 2f，间隔从 8f 收敛到 3f，窒息感逐渐逼近。最后一闪盖住 79–80，\n// 帧 81 掀开时构图已硬切到 scale 1.35 对准第 2 行中间卡（零补间一帧到位），\n// 叠 2f brightness 0.88 加深脉冲当落锤。\n// 关键帧：0–78 推近 1.0→~1.05 → 黑闪 [40,48,55,61,66,70,73,76,79]（各 2f）→\n// 79–80 全黑 → 帧 81 硬切 scale 1.35（81–82 加深脉冲）→ 83–134 全静止（52f ≥50f）。\n// 光敏警示：实战建议配乐渐强使用。\nimport React from 'react';\nimport { useCurrentFrame, interpolate, Easing } from 'remotion';\nimport { G, FakeDashboard, TitleBlock } from '../../_fixtures/Fixtures';\n\n// punch-in 落点：3×2 网格第 2 行中间卡中心（同 JumpCutPunchIn 几何）\n// 侧栏 220 + 内边距 36；卡宽 (1920-220-72-56)/3 ≈ 524.67，卡高 (1080-72-72-28)/2 = 454\nconst CARD_L = 220 + 36 + 524.67 + 28; // ≈808.67\nconst CARD_T = 72 + 36 + 454 + 28; // 590\nconst CARD_W = 524.67;\nconst CARD_H = 454;\nconst ORIGIN_X = CARD_L + CARD_W / 2; // ≈1071\nconst ORIGIN_Y = CARD_T + CARD_H / 2; // 817\n\n// 黑闪帧号表（写死）：间隔 8→7→6→5→4→3→3→3，每次持续 2f（f0 与 f0+1）\nconst FLASHES = [40, 48, 55, 61, 66, 70, 73, 76, 79];\n\nconst isBlack = (f: number): boolean =>\n  FLASHES.some((f0) => f >= f0 && f <= f0 + 1);\n\n// 帧 81 前缓慢推近（Easing.in 蓄力），81 起零补间硬切 1.35 定住\nconst scaleAt = (f: number): number =>\n  f < 81\n    ? interpolate(f, [0, 80], [1.0, 1.05], {\n        extrapolateLeft: 'clamp',\n        extrapolateRight: 'clamp',\n        easing: Easing.in(Easing.quad),\n      })\n    : 1.35;\n\n// 落锤：81–82 两帧整画面加深脉冲\nconst pulseAt = (f: number): number => (f >= 81 && f <= 82 ? 0.88 : 1);\n\nexport const StrobeBlackFrames: React.FC = () => {\n  const frame = useCurrentFrame();\n  const s = scaleAt(frame);\n  const b = pulseAt(frame);\n  const black = isBlack(frame);\n\n  return (\n    <div\n      style={{\n        width: 1920,\n        height: 1080,\n        background: G.bg,\n        position: 'relative',\n        overflow: 'hidden',\n        filter: `brightness(${b})`,\n      }}\n    >\n      <div\n        style={{\n          position: 'absolute',\n          inset: 0,\n          transform: `scale(${s})`,\n          transformOrigin: `${ORIGIN_X}px ${ORIGIN_Y}px`,\n        }}\n      >\n        <FakeDashboard variant=\"A\" />\n        {/* 落点卡片标记：随构图一起缩放，提示硬切对准的核心卡 */}\n        <div\n          style={{\n            position: 'absolute',\n            left: CARD_L - 10,\n            top: CARD_T - 10,\n            width: CARD_W + 20,\n            height: CARD_H + 20,\n            border: `3px dashed ${G.mid}`,\n            borderRadius: 20,\n            boxSizing: 'border-box',\n          }}\n        />\n      </div>\n      {/* 标签不随缩放，固定顶栏位置 */}\n      <div style={{ position: 'absolute', left: 260, top: 20 }}>\n        <TitleBlock text=\"STROBE BLACK FRAMES\" size={54} />\n      </div>\n      {/* 全屏黑闪层：盖住一切（含标签），每次 2f */}\n      {black && (\n        <div\n          style={{\n            position: 'absolute',\n            inset: 0,\n            background: '#0c0c0c',\n            zIndex: 10,\n          }}\n        />\n      )}\n    </div>\n  );\n};\n\n\n"
}