{
  "slug": "grain-dissolve",
  "title": "Grain dissolve to wordmark",
  "tagline": "A full line of text bursts into boiling grain noise with a selection box appearing, then the noise cloud rapidly condenses into a larger glowing short wordmark, drift decaying to zero as it freezes.",
  "description": "A full line of text bursts into boiling grain noise with a selection box appearing, then the noise cloud rapidly condenses into a larger glowing short wordmar",
  "category": "outro",
  "tags": [
    "shotcraft",
    "remotion",
    "outro"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 2.5,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/grain-dissolve/poster-480e605762cb.jpg",
    "video": "https://gimgs.net/motion/media/grain-dissolve/video-c5f3fea630f6.mp4"
  },
  "variants": [
    {
      "key": "grain-dissolve",
      "label": "grain-dissolve · Grain dissolve to wordmark",
      "description": "A full line of text bursts into boiling grain noise with a diagonal-hatched selection box appearing, then the noise cloud rapidly condenses into a larger glowing short wordmark, drift decaying to zero as it freezes.",
      "use": "",
      "video": "https://gimgs.net/motion/media/grain-dissolve/video-c5f3fea630f6.mp4",
      "poster": "https://gimgs.net/motion/media/grain-dissolve/poster-480e605762cb.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/grain-dissolve.html",
  "json_url": "https://gimgs.net/motion/grain-dissolve.json",
  "template_url": "https://gimgs.net/motion/grain-dissolve/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/grain-dissolve.md",
  "updated_at": "2026-09-24T07:40:00.208Z",
  "prompt": "## When to use\n\nAn \"XX. Now Live\" style closing launch announcement; the energy-condensing beat where a long sentence compresses into a short brand mark.\n\n## Intention\n\nShoot \"one sentence condensing into one word\" as a physical event: the sentence first destabilizes into boiling grain (information disintegrating), a selection box implies \"being selected and extracted\", then all the grain's energy collapses toward the center into a bigger, brighter short wordmark — what the audience reads is \"all of this boils down to this\".\n\n## Duration & energy\n\n- Duration: About 2.0s (60f@30fps; granulate 0.26–0.56s · condense 1.2–1.42s · settle and freeze)\n- Energy: Medium-high (a brief, one-time energy pulse, a natural outro beat)\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n把\"一句话浓缩成一个词\"拍成物理事件：句子先失稳沸腾成颗粒（信息解体），选区框暗示\"正在选中提取\"，然后所有颗粒能量向中心坍缩成更大更亮的短字标——观众读到的是\"这一切归结为它\"。\n\n## 动效核心\n- 滤镜链是唯一引擎：`feTurbulence`（fractalNoise，baseFrequency 0.9→1.3）+ `feDisplacementMap`（scale 0→52）+ `feGaussianBlur`（0→1.1px），三者由同一条 `burst` 曲线（t=0.13–0.28，outCubic）驱动\n- 噪点\"沸腾\"靠 `seed = floor(t*46)` 逐帧换种子——每帧一幅新噪声图，颗粒永不静止\n- t=0.60–0.71 交叉淡化：整行字（33px）淡出、短字标（54px 粗体）淡入，两者共用同一滤镜——切换发生在最沸腾时，观众看不到换字\n- t=0.68–0.90 `lock` 曲线把 displacement scale 和 blur 拉回 0，颗粒\"凝固\"成清晰字形\n- 白辉光三段式：砂化期 0.3 弱光 → 凝聚冲高 0.7 → t=0.88 后回落 0.45，`drop-shadow` 半径 4–24px 同步呼吸\n- 斜纹选区框（45° 线阵 + 四角像素棋盘手柄）随 burst 浮现、t=0.55–0.64 撤掉——在凝聚前退场，不抢主体\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| displacement scale | 峰值 52 | <30 只是\"轻微毛边\"；>80 颗粒散到认不出轮廓，\"隐约可辨\"是砂化质感的底线 |\n| baseFrequency | 0.9 → +0.4 | 越高颗粒越细；低于 0.5 变成大块扭曲不是\"砂\" |\n| seed 换帧率 | floor(t*46)，2s 内 46 次 | 降低会看出噪声图重复；保持 ≥20 次/秒的换图率 |\n| 砂化窗口 | 0.13–0.28（outCubic） | 前 0.13 的干净字停留是\"失稳前的常态\"，砍掉则没有对比 |\n| 凝聚交叉 | 0.60–0.71（inOutCubic） | 必须落在最沸腾段内完成换字；提前会穿帮看到两行字叠影 |\n| 辉光包络 | burst·0.3 + cond·0.7 − settle·0.45 | 凝聚冲高是卡点位，压 BGM 重音；回落别到 0，留柔光\"余温\" |\n\n## 已知坑\n- 换字标文案（ACME → 项目短标）时两行 `<text>` 都要改：整行句式 `{ XX. Now Live }` 与短标 XX 必须指同一对象，否则叙事断裂\n- 短标变长（>6 字符）需同步缩 54px 字号或加宽选区框 bw，否则凝聚后超出框位视觉重心偏移\n- `feDisplacementMap` 在 Safari 上性能较差，这张卡是 2s 短拍可接受；拉长时长需实测帧率\n- 选区框撤场（0.55–0.64）必须早于凝聚完成（0.71）——框留到凝固后会读作\"还没选完\"，语义反了\n\n## 参考实现\ndemos/outro/grain-dissolve/\n（GrainDissolve.tsx）\n\n## Reference implementation (Remotion)\n\n- demos/outro/grain-dissolve/GrainDissolve.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/outro/grain-dissolve/GrainDissolve.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/outro/grain-dissolve/GrainDissolve.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/outro/grain-dissolve/GrainDissolve.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/outro/grain-dissolve/GrainDissolve.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": 75,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/outro/grain-dissolve/GrainDissolve.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/outro/grain-dissolve/GrainDissolve.tsx =====\n// grain-dissolve — Grain Dissolve → Condense 文字砂化凝聚（motion-lab 定稿转原生 Remotion）\n// 干净的整行字 \"{ ACME. Now Live }\" 先爆裂成沸腾颗粒噪点（轮廓隐约可辨、白色辉光），\n// 同时出现带 45° 斜纹填充和像素方块角柄的选区框；噪点沸腾约半程后选区框消失，\n// 噪点云急速凝聚成更大号的颗粒短字标（占位词 \"ACME\"），位移量衰减归零、辉光冲高回落，\n// 凝固为清晰发光短字标。四角 HUD 括角/圆点与左右中线短划全程常驻。\n// 滤镜链：feTurbulence seed 逐帧 + displacement scale 双向动画，终字同走滤镜再解除。\n// 设计坐标 480×270（DesignStage 等比放大），SVG viewBox 640×360 铺满全幅。\nimport React, { useId } from 'react';\nimport { E, DesignStage, seg, useT } from '../../_fixtures/Motion';\n\nexport const GRAIN_DISSOLVE_DURATION = 60; // 2000ms @30fps\n\n// 选区框几何（viewBox 坐标）\nconst BX = 128;\nconst BY = 148;\nconst BW = 384;\nconst BH = 62;\n\n// 45° 斜纹：x 从 bx-bh 起每 34 一根，右下→左上\nconst HATCH_XS: number[] = [];\nfor (let x = BX - BH; x < BX + BW; x += 34) HATCH_XS.push(x);\n\n// 四角像素棋盘手柄（两块 5×5 错位方块）\nconst Handle: React.FC<{ x: number; y: number }> = ({ x, y }) => (\n  <g transform={`translate(${x - 5},${y - 5})`} fill=\"#cfd2d8\">\n    <rect width={5} height={5} />\n    <rect x={5} y={5} width={5} height={5} />\n  </g>\n);\n\n// HUD 括角 + 圆点（sx/sy 控制朝向）\nconst Corner: React.FC<{ x: number; y: number; sx: number; sy: number }> = ({ x, y, sx, sy }) => (\n  <>\n    <path\n      d={`M${x + 14 * sx} ${y}H${x}V${y + 14 * sy}`}\n      fill=\"none\"\n      stroke=\"#3a3a40\"\n      strokeWidth={1.5}\n    />\n    <circle cx={x + 34 * sx} cy={y + 28 * sy} r={1.6} fill=\"#8b8d94\" />\n  </>\n);\n\nexport const GrainDissolve: React.FC = () => {\n  const t = useT();\n  // 滤镜/clipPath ID 按实例生成，同一 Composition 放多个实例时互不串引\n  // （useId 的 «:» 在 CSS url() 里非法，清洗成纯字母数字）\n  const uid = useId().replace(/[^a-zA-Z0-9]/g, '');\n  const fid = `gd-${uid}`;\n  const cid = `gd-${uid}-clip`;\n  const burst = seg(t, 0.13, 0.28, E.outCubic); // 干净字 → 砂化\n  const cond = seg(t, 0.60, 0.71, E.inOutCubic); // 整行噪点云 → 短字标噪点云\n  const lock = seg(t, 0.68, 0.90, E.outCubic); // 位移衰减凝固\n  const settle = seg(t, 0.88, 1, E.outCubic); // 辉光回落\n  // 白色辉光：砂化期轻微，凝聚时冲高，凝固后回落到柔光\n  const glow = burst * 0.3 + cond * 0.7 - settle * 0.45;\n  return (\n    <DesignStage bg=\"#0a0a0c\">\n      <svg\n        viewBox=\"0 0 640 360\"\n        style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', background: '#0a0a0c' }}\n      >\n        <defs>\n          <filter id={fid} x=\"-40%\" y=\"-150%\" width=\"180%\" height=\"400%\">\n            <feTurbulence\n              type=\"fractalNoise\"\n              baseFrequency={0.9 + burst * 0.4}\n              numOctaves={2}\n              seed={Math.floor(t * 46)}\n              result=\"n\"\n            />\n            <feDisplacementMap\n              in=\"SourceGraphic\"\n              in2=\"n\"\n              scale={burst * 52 * (1 - lock)}\n              xChannelSelector=\"R\"\n              yChannelSelector=\"G\"\n              result=\"d\"\n            />\n            <feGaussianBlur in=\"d\" stdDeviation={burst * 1.1 * (1 - lock)} />\n          </filter>\n        </defs>\n        {/* 四角 HUD 括角 + 圆点 + 左右中线短划（全程常驻） */}\n        <g>\n          <Corner x={88} y={96} sx={1} sy={1} />\n          <Corner x={552} y={96} sx={-1} sy={1} />\n          <Corner x={88} y={264} sx={1} sy={-1} />\n          <Corner x={552} y={264} sx={-1} sy={-1} />\n          <line x1={52} y1={180} x2={76} y2={180} stroke=\"#4a4a50\" strokeWidth={1.5} strokeDasharray=\"4 3\" />\n          <line x1={564} y1={180} x2={588} y2={180} stroke=\"#4a4a50\" strokeWidth={1.5} strokeDasharray=\"4 3\" />\n        </g>\n        {/* 选区框：砂化时浮现，凝聚前撤掉 */}\n        <g opacity={burst * (1 - seg(t, 0.55, 0.64))}>\n          <clipPath id={cid}>\n            <rect x={BX} y={BY} width={BW} height={BH} />\n          </clipPath>\n          <g clipPath={`url(#${cid})`}>\n            {HATCH_XS.map((x) => (\n              <line key={x} x1={x} y1={BY + BH} x2={x + BH} y2={BY} stroke=\"#2c2c31\" strokeWidth={1} />\n            ))}\n          </g>\n          <rect x={BX} y={BY} width={BW} height={BH} fill=\"none\" stroke=\"#55565c\" strokeWidth={1} />\n          <Handle x={BX} y={BY} />\n          <Handle x={BX + BW} y={BY} />\n          <Handle x={BX} y={BY + BH} />\n          <Handle x={BX + BW} y={BY + BH} />\n        </g>\n        {/* 文字组：整行字与终字标同走滤镜链 + 白色辉光 */}\n        <g\n          style={{\n            filter: `url(#${fid}) drop-shadow(0 0 ${4 + glow * 20}px rgba(255,255,255,${Math.max(0, glow) * 0.9}))`,\n          }}\n        >\n          <text\n            x={320}\n            y={191}\n            textAnchor=\"middle\"\n            opacity={1 - cond}\n            style={{\n              fill: '#eceef2',\n              font: \"500 33px Inter,'Helvetica Neue',system-ui,sans-serif\",\n              letterSpacing: '2.5px',\n            }}\n          >\n            {'{ ACME. Now Live }'}\n          </text>\n          <text\n            x={320}\n            y={198}\n            textAnchor=\"middle\"\n            opacity={cond}\n            style={{\n              fill: '#fff',\n              font: \"800 54px Inter,'Helvetica Neue',system-ui,sans-serif\",\n              letterSpacing: '4px',\n            }}\n          >\n            ACME\n          </text>\n        </g>\n      </svg>\n    </DesignStage>\n  );\n};\n\n\n"
}