{
  "slug": "line-boil",
  "title": "Line boil",
  "tagline": "Line boil — during a hold, text/outline contours wriggle slightly every 3 frames like a hand-redrawn frame, keeping a still image \"alive\" and breathing.",
  "description": "Line boil — during a hold, text/outline contours wriggle slightly every 3 frames like a hand-redrawn frame, keeping a still image \"alive\" and breathing.",
  "category": "effects",
  "tags": [
    "shotcraft",
    "remotion",
    "effects",
    "typography"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 4.72,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/line-boil/poster-2fec82f78186.jpg",
    "video": "https://gimgs.net/motion/media/line-boil/video-738f9e6f96fb.mp4"
  },
  "variants": [
    {
      "key": "line-boil",
      "label": "line-boil · Line boil",
      "description": "Line boil — during a hold, text/outline contours wriggle slightly every 3 frames like a hand-redrawn frame, keeping a still image \"alive\" and breathing.",
      "use": "",
      "video": "https://gimgs.net/motion/media/line-boil/video-738f9e6f96fb.mp4",
      "poster": "https://gimgs.net/motion/media/line-boil/poster-2fec82f78186.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/line-boil.html",
  "json_url": "https://gimgs.net/motion/line-boil.json",
  "template_url": "https://gimgs.net/motion/line-boil/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/line-boil.md",
  "updated_at": "2026-09-24T07:40:04.432Z",
  "prompt": "## When to use\n\nFor long holds on title cards/outlined elements (the go-to upgrade for a black-scene title card); a textural technique that piggybacks on another shot.\n\n## Intention\n\nThe library's precedent demands \"real stillness\" once things settle, but a long title card freezing completely reads as \"the film froze\". This card offers a third state: **living stillness** — the outline gets \"redrawn\" every 3 frames, position and content completely unchanged, only the line edges breathing very slightly, a century-old convention of hand-drawn animation hold frames. The difference from a rejected drift technique: perceptibility — drift is an overall shift imperceptible to the eye, boiling is contour deformation with stepped jumps that are visible. In a paper-and-ink aesthetic, this is the direct source of \"handmade feel\".\n\n## Duration & energy\n\n- Duration: Parasitic — the boil segment lasts as long as its host's hold; it has no duration of its own\n- Energy: Low (ambient-noise level)\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n库内 R 系判例要求落定后\"真静止\"，但长字卡（3s+ 的黑场字卡、片尾）\n完全冻结会读作\"片子卡了\"。本卡给第三种状态：**活着的静止**——轮廓\n每 3 帧被\"重描\"一次，位置内容全然不动，只有线条边缘在极轻微地呼吸，\n手绘动画 hold 帧的百年惯例。与 noise-drift（被否决）的区别正是可感性：\n漂移是整体 1–3px 位移（肉眼读不出），沸腾是轮廓形变+阶梯跳变（每次\n换 seed 都是一次可见的\"重描\"）。纸墨审美里这是\"手工感\"的直接来源。\n\n## 动效核心\n- SVG filter：feTurbulence(fractalNoise, baseFrequency 0.015,\n  numOctaves 2) + feDisplacementMap **scale=8**（原案 3–6 不可感，\n  已加码）作用于文字/描边层\n- **seed = Math.floor(f/3) 阶梯换**——每 3 帧跳一次新形变，\n  10fps 的手翻书质感；逐帧换 seed 读作电噪不是手绘\n- **摘罩命门**（feTurbulence 判例）：沸腾段之外 filter 与 SVG defs\n  根本不渲染（条件挂载），不是 opacity 到 0——否则永不真静止\n- 结构靠对比：demo 用 静止 35f → 沸腾 70f → 摘罩静止 35f 三段\n  自证可感；实战沸腾段直接铺满宿主 hold\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| displacement scale | 8（大字标题）；小字/细线 5–6 | >12 读作故障扭曲；<4 不可感 |\n| seed 节拍 | floor(f/3)，即每 3 帧重描 | /2 太贼、/4 以上读作卡顿跳帧 |\n| baseFrequency | 0.015（形变尺度约字高级） | 0.05+ 变成毛边噪点，不是\"重描\" |\n| 适用对象 | 大标题、描边框、手绘圈注 | 实心小元素（图标/头像）沸腾读作模糊 |\n| 与 R 系关系 | 沸腾段视作合法 hold；宿主镜头收尾若需绝对静止则提前 ≥15f 摘罩 | 摘罩瞬间轮廓归位本身就是一次收束 |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- 全片同时沸腾的元素 ≤1 组——标题在沸腾、卡片框也在沸腾，观众\n  找不到该看哪（P4）\n- 与 speed-lines/集中线的\"沸腾\"（impact-feedback C 式每 2 帧换形态）\n  同族不同职：那是冲击帧的高能沸腾，这是 hold 帧的底噪沸腾，能量\n  档位差两级，别互换参数\n- SVG filter 对大面积层有渲染开销；1920 全幅文字层可接受，整页\n  截图套 boil 渲染时间翻倍且语义不对（页面不是手绘物）\n- paper-title-card/黑场字卡（shot-transitions D 式）是本卡首选宿主，\n  组合时字卡的打字机/压印入场完成后再起沸腾\n\n## 参考实现\ndemos/effects/line-boil/\n（LineBoil.tsx）\n\n## Reference implementation (Remotion)\n\n- demos/effects/line-boil/LineBoil.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/line-boil/LineBoil.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/effects/line-boil/LineBoil.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/effects/line-boil/LineBoil.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/line-boil/LineBoil.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": 142,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/line-boil/LineBoil.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/effects/line-boil/LineBoil.tsx =====\n// 线条沸腾（line-boil）——手绘动画 line boil 质感：静止线稿在\"沸腾段\"\n// 边缘逐帧微颤，像手绘逐帧描线的抖动。SVG filter feTurbulence(baseFrequency\n// 0.015, numOctaves 2, seed = Math.floor(f/3) 每 3 帧阶梯换) + feDisplacementMap\n// scale=8（原案 3–6 已按可感性加码）作用于大标题 \"ALIVE\" 与描边卡整层。\n// 结构靠\"对比\"可感：先静止（干净版）→ 沸腾 → 摘罩回静止；判例：feTurbulence\n// 收尾必须整个 filter 移除（沸腾段外根本不渲染 filter 与 SVG def），\n// 105f 起逐帧完全相同，真静止 ≥35f。\n// 关键帧：0–35 完全静止(boil off) → 35–105 沸腾(boil on, seed 每 3 帧一换)\n// → 105 摘罩 → 105–140 真静止(boil off)。\nimport React, { useId } from 'react';\nimport { useCurrentFrame, interpolate } from 'remotion';\nimport { G, TitleBlock } from '../../_fixtures/Fixtures';\n\nconst BOIL_START = 35;\nconst BOIL_END = 105;\nconst BOIL_SCALE = 8; // 原案 3–6 已按可感性加码；QA 看不出再加到 12\n\nconst CornerTag: React.FC<{ text: string; opacity: number }> = ({ text, opacity }) => (\n  <div\n    style={{\n      position: 'absolute',\n      right: 72,\n      bottom: 56,\n      padding: '10px 22px',\n      border: `3px solid ${G.ink}`,\n      borderRadius: 999,\n      color: G.ink,\n      background: G.bg,\n      fontFamily: 'Helvetica, Arial, sans-serif',\n      fontWeight: 700,\n      fontSize: 30,\n      letterSpacing: 2,\n      opacity,\n    }}\n  >\n    {text}\n  </div>\n);\n\nexport const LineBoil: React.FC = () => {\n  const f = useCurrentFrame();\n  // 滤镜 ID 按实例生成，多实例同场不串引（useId 的 «:» 在 url() 里非法，需清洗）\n  const boilId = `boil-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;\n  const boiling = f >= BOIL_START && f < BOIL_END;\n  // seed 每 3 帧阶梯换 → 8~10Hz 的手绘颤动感；帧确定，无随机源\n  const seed = Math.floor(f / 3);\n\n  // 角标：boil on 只在沸腾段淡入淡出；boil off 与之互补。\n  // 全部过渡在 105f 前完成 → 105–140 逐帧完全相同\n  const onOp = interpolate(f, [35, 40, 100, 105], [0, 1, 1, 0], {\n    extrapolateLeft: 'clamp',\n    extrapolateRight: 'clamp',\n  });\n  const offOp = 1 - onOp;\n\n  return (\n    <div style={{ width: 1920, height: 1080, background: G.bg, position: 'relative', overflow: 'hidden' }}>\n      {/* 沸腾段才渲染 filter 定义——摘罩即整个 SVG def 消失，收尾天然真静止 */}\n      {boiling && (\n        <svg width={0} height={0} style={{ position: 'absolute' }}>\n          <defs>\n            <filter id={boilId} x=\"-15%\" y=\"-15%\" width=\"130%\" height=\"130%\">\n              <feTurbulence\n                type=\"fractalNoise\"\n                baseFrequency={0.015}\n                numOctaves={2}\n                seed={seed}\n                result=\"noise\"\n              />\n              <feDisplacementMap\n                in=\"SourceGraphic\"\n                in2=\"noise\"\n                scale={BOIL_SCALE}\n                xChannelSelector=\"R\"\n                yChannelSelector=\"G\"\n              />\n            </filter>\n          </defs>\n        </svg>\n      )}\n\n      {/* 手法名标签：不入滤镜层，作为静止参照物 */}\n      <div style={{ position: 'absolute', left: 120, top: 96 }}>\n        <TitleBlock text=\"LINE BOIL\" size={54} />\n      </div>\n\n      {/* 被沸腾的整层：大标题 + 描边卡 */}\n      <div\n        style={{\n          position: 'absolute',\n          inset: 0,\n          display: 'flex',\n          flexDirection: 'column',\n          alignItems: 'center',\n          justifyContent: 'center',\n          gap: 56,\n          filter: boiling ? `url(#${boilId})` : undefined,\n        }}\n      >\n        <div\n          style={{\n            fontFamily: 'Helvetica, Arial, sans-serif',\n            fontWeight: 800,\n            fontSize: 170,\n            color: G.ink,\n            letterSpacing: 4,\n            lineHeight: 1,\n          }}\n        >\n          ALIVE\n        </div>\n        {/* 描边卡：透明底 3px ink 描边 520×300 圆角框 + 几条灰线 */}\n        <div\n          style={{\n            width: 520,\n            height: 300,\n            border: `3px solid ${G.ink}`,\n            borderRadius: 20,\n            boxSizing: 'border-box',\n            padding: '36px 40px',\n            display: 'flex',\n            flexDirection: 'column',\n            gap: 26,\n          }}\n        >\n          <div style={{ height: 16, width: '62%', background: G.mid, borderRadius: 8 }} />\n          <div style={{ height: 12, width: '88%', background: G.bar, borderRadius: 6 }} />\n          <div style={{ height: 12, width: '74%', background: G.bar, borderRadius: 6 }} />\n          <div style={{ height: 12, width: '81%', background: G.bar, borderRadius: 6 }} />\n          <div style={{ marginTop: 'auto', display: 'flex', gap: 12, alignItems: 'center' }}>\n            <div style={{ width: 30, height: 30, borderRadius: 15, border: `3px solid ${G.ink}`, boxSizing: 'border-box' }} />\n            <div style={{ height: 12, width: 120, background: G.mid, borderRadius: 6 }} />\n          </div>\n        </div>\n      </div>\n\n      {/* 状态角标：沸腾段 boil on，静止段 boil off */}\n      <CornerTag text=\"boil on\" opacity={onOp} />\n      <CornerTag text=\"boil off\" opacity={offOp} />\n    </div>\n  );\n};\n\n\n"
}