{
  "slug": "avatar-grid-radial-build-colorize",
  "title": "Radial Grid Build & Colorize",
  "tagline": "An 8×7 card grid grows ring by ring from the center to fill up (mixed initials/icons/image placeholders), then ~15% of cards randomly turn red to flag anomalies, with a title legend anchored center.",
  "description": "An 8×7 card grid grows ring by ring from the center to fill up (mixed initials/icons/image placeholders), then ~15% of cards randomly turn red to flag anomali",
  "category": "data",
  "tags": [
    "shotcraft",
    "remotion",
    "data"
  ],
  "tier": "free",
  "license": "Apache-2.0",
  "spec": {
    "width": 1920,
    "height": 1080,
    "fps": 30,
    "duration": 6.1,
    "aspect": "16:9"
  },
  "kind": "remotion",
  "params_schema": {
    "type": "object",
    "properties": {}
  },
  "default_params": {},
  "preview": {
    "poster": "https://gimgs.net/motion/media/avatar-grid-radial-build-colorize/poster-89e17ec8f8e4.jpg",
    "video": "https://gimgs.net/motion/media/avatar-grid-radial-build-colorize/video-049bf5c95184.mp4"
  },
  "variants": [
    {
      "key": "avatar-grid-radial-build-colorize",
      "label": "avatar-grid-radial-build-colorize",
      "description": "An 8×7 card grid grows ring by ring from the center to fill up (mixed initials/icons/image placeholders), then ~15% of cards randomly turn red to flag anomalies, with a title legend anchored center.",
      "use": "",
      "video": "https://gimgs.net/motion/media/avatar-grid-radial-build-colorize/video-049bf5c95184.mp4",
      "poster": "https://gimgs.net/motion/media/avatar-grid-radial-build-colorize/poster-89e17ec8f8e4.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/avatar-grid-radial-build-colorize.html",
  "json_url": "https://gimgs.net/motion/avatar-grid-radial-build-colorize.json",
  "template_url": "https://gimgs.net/motion/avatar-grid-radial-build-colorize/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/avatar-grid-radial-build-colorize.md",
  "updated_at": "2026-09-24T07:39:43.276Z",
  "prompt": "## When to use\n\nData narrative for \"anomalies/highlights emerging from a crowd\": user base health, monitoring dashboards, batch-status overviews.\n\n## Intention\n\nA two-act structure: Act 1, \"the group forms\" — cards grow ring by ring from the center, like a colony growing under a microscope; Act 2, \"anomalies surface\" — once filled, individual cards turn red one after another, forcing the viewer's eye to scan the whole field for red dots. It turns the line \"we watch over every item for you\" into an experience.\n\n## Duration & energy\n\n- Duration: About 5.6s (168f@30fps; fill 0.5–1.7s · coloring 1.7–3.4s, staggered).\n- Energy: Medium (the build phase has rhythm; the coloring phase is a quiet \"discovery\" moment).\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n两幕结构：第一幕\"群体成形\"——卡片从中心一环环长出，像镜头下的菌落生长；第二幕\"异常浮现\"——铺满后个别卡片陆续变红，观众的眼睛被迫扫描全场找红点。它把\"我们帮你盯着所有项\"这句话拍成了体验。\n\n## 动效核心\n- 分环 stagger：每格按 `ring = round(hypot((c-3.5), (r-3)/0.85))` 算环号，延迟 `(ring*4 + rand*3)/TOTAL` ——同环内再加 3 帧随机抖动，生长波有机不机械\n- 卡片入场只做 opacity（0.018 窗口）+ scale 0.8→1（0.03，outQuad），**无位移**——\"长出来\"而不是\"飞进来\"\n- 卡片内容三种占位混合（`rand(i*9.1)` 决定）：首字母对（logo 位）、几何图标符（◆▲●✦）、渐变色块（图片位）——实际使用换项目素材（头像/logo/缩略图均可）\n- 中央 3×6 区域 `visibility:hidden` 占位不销毁——网格完整性保留，给标题和图例腾出舞台\n- 染色幕：15% 的卡片（`rand<0.15`）在 t=0.30–0.60 随机时刻各自 0.036 窗口染色——白底→浅红 #FDECEC、描边→#F6CFCF、状态点绿→红，三通道同拍 mix\n- 标题 0.02–0.10 先入（scale 0.98→1），图例（Active/Pending/Inactive 三色点）0.26 跟进\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 生长速度 | 每 4 帧一环 + 3 帧抖动，1.2s 铺满 | 快于 3 帧/环读作\"闪现\"；抖动砍掉后波前整齐得像机器 |\n| 异常比例 | 15% | <8% 观众可能漏看；>25% \"异常\"变\"普遍\"，叙事反转 |\n| 染色窗口 | 每张 0.036，散布在 0.30–0.60 | 全部同拍染色是警报不是浮现；散布区间越长\"陆续发现\"感越强 |\n| 状态色 | 绿 #37C46B → 红 #F0453A（点），底 #FDECEC | 功能色不随品牌换；底色染红别深过 #F8D8D8，卡片文字要仍可读 |\n| 网格规模 | 8×7 − 中央 18 格 = 38 张可见 | 更大网格需同步缩 gap 和字号；中央留空区随标题长度调 |\n| 内容混合比 | 首字母/图标/图片 各 1/3（rand 均分） | 全换成同类（如全头像）也成立；混合版本更像\"多类型资产\"场景 |\n\n## 已知坑\n- 中央留空用 `visibility:hidden` 而不是不渲染——删格子会让分环计算的环心偏移，生长波变形\n- 染色的三个通道（底/描边/点）必须同一条 cT 曲线驱动，分开会出现\"点红了底还白\"的中间态\n- 图片占位是 CSS 渐变色块，换真实图片时保留 `overflow:hidden` 和 border-radius 继承，注意深色图上状态点要加白描边\n- 标题/图例 z-index 在卡片之上但无底板，若换更密的网格需给中央区加半透明底垫保证可读\n\n## 参考实现\ndemos/data/avatar-grid-radial-build-colorize/\n（AvatarGridRadialBuildColorize.tsx）\n\n## Reference implementation (Remotion)\n\n- demos/data/avatar-grid-radial-build-colorize/AvatarGridRadialBuildColorize.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/data/avatar-grid-radial-build-colorize/AvatarGridRadialBuildColorize.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/data/avatar-grid-radial-build-colorize/AvatarGridRadialBuildColorize.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/data/avatar-grid-radial-build-colorize/AvatarGridRadialBuildColorize.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/data/avatar-grid-radial-build-colorize/AvatarGridRadialBuildColorize.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": 183,
    "repo": "https://github.com/Vincentwei1021/video-shotcraft",
    "files": [
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/data/avatar-grid-radial-build-colorize/AvatarGridRadialBuildColorize.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/data/avatar-grid-radial-build-colorize/AvatarGridRadialBuildColorize.tsx =====\n// avatar-grid-radial-build-colorize — Avatar Grid 分环生长随机染色（motion-lab 定稿转原生 Remotion）\n// 8×7 小卡片网格由中心向四周分环生长（每 4 帧扩一环，1.2s 铺满），卡片内容混合三种占位：\n// 首字母 / 图标符 / 色块缩略图，只做 opacity + scale 0.8→1 不位移；铺满后约 15% 的卡片\n// 在 1s 内随机时刻把底色染成浅红、状态点转红，形成\"异常项逐渐浮现\"的呼吸感。\n// 中央 3×6 区域 visibility:hidden 占位留给标题与图例，标题层始终 100% 不透明。\n// 设计坐标 480×270（DesignStage 等比放大），参数表数值以此坐标系标定。\nimport React from 'react';\nimport { DesignStage, E, lerp, rand, seg, useT } from '../../_fixtures/Motion';\n\nexport const AVATAR_GRID_RADIAL_BUILD_COLORIZE_DURATION = 168; // 5600ms @30fps\n\nconst PAPER = '#F3F3F1';\nconst INK = '#111113';\nconst MID = '#8A8A8F';\nconst CARD_WHITE = '#ffffff';\nconst FLAG_BG = '#FDECEC';\n\n// 颜色插值（effect.js 的 mix；hex2rgb 原来自 lab 页面全局，这里内联实现）\nconst hex2rgb = (hex: string): [number, number, number] => [\n  parseInt(hex.slice(1, 3), 16),\n  parseInt(hex.slice(3, 5), 16),\n  parseInt(hex.slice(5, 7), 16),\n];\nconst mix = (a: string, b: string, t: number) => {\n  const A = hex2rgb(a);\n  const B = hex2rgb(b);\n  return `rgb(${Math.round(A[0] + (B[0] - A[0]) * t)},${Math.round(A[1] + (B[1] - A[1]) * t)},${Math.round(\n    A[2] + (B[2] - A[2]) * t,\n  )})`;\n};\n\nconst COLS = 8;\nconst ROWS = 7;\nconst TOTAL = 5.6 * 30; // 总帧数（delay 换算用）\n\n// 网格几何：等价 effect.js 的 inset:16px + gap:10px CSS grid，但改为绝对定位手排。\n// CSS grid 在 480×270 设计坐标下会把轨道取整到整像素（放大 4 倍后节距 140/144 交替），\n// 与原片的全分辨率均匀分布不符；小数设计坐标 + 绝对定位可保住亚像素节距。\nconst GAP = 10;\nconst CELL_W = (480 - 32 - (COLS - 1) * GAP) / COLS; // 47.25\nconst CELL_H = (270 - 32 - (ROWS - 1) * GAP) / ROWS; // ≈25.43\n\n// 卡片内容三种占位：首字母(logo 位) / 图标符 / 色块缩略图(图片位)，实际使用换项目素材\nconst INI = ['VS', 'KJ', 'EM', 'AL', 'TR', 'MN', 'BQ', 'DW', 'RC', 'SF', 'PL', 'GH'];\nconst ICONS = ['◆', '▲', '●', '■', '✦', '◐', '❖', '▣'];\n\n// 每格的静态参数（种子与 effect.js 逐一对应，跨帧确定）\nconst CELLS = Array.from({ length: ROWS * COLS }, (_, i) => {\n  const r = Math.floor(i / COLS);\n  const c = i % COLS;\n  const hidden = r >= 2 && r <= 4 && c >= 1 && c <= 6; // 中央留空给标题 + 图例（占位不破坏网格）\n  const kind = Math.floor(rand(i * 9.1) * 3); // 0=首字母 1=图标 2=图片缩略\n  const ini = INI[Math.floor(rand(i * 3.7) * INI.length)];\n  const icon = ICONS[Math.floor(rand(i * 5.3) * ICONS.length)];\n  const h = Math.floor(rand(i * 7.7) * 360); // 缩略图渐变色相\n  const ring = Math.round(Math.hypot((c - 3.5) / 1.0, (r - 3) / 0.85)); // 到中心的\"环号\"\n  const delay = (ring * 4 + rand(i + 40) * 3) / TOTAL; // 每 4 帧扩一环 + 帧级抖动\n  const flagged = rand(i + 900) < 0.15; // ~15% 异常卡\n  const at = 0.3 + rand(i + 1600) * 0.3; // 染色随机时刻\n  return { hidden, kind, ini, icon, h, delay, flagged, at };\n});\n\nconst LEGEND: Array<[string, string]> = [\n  ['Active', '#37C46B'],\n  ['Pending', '#F5A524'],\n  ['Inactive', '#F0453A'],\n];\n\nexport const AvatarGridRadialBuildColorize: React.FC = () => {\n  const t = useT();\n  // 标题淡入 + 轻微 scale；图例随后浮现\n  const tIn = seg(t, 0.02, 0.1, E.outQuad);\n  const legendIn = seg(t, 0.26, 0.36, E.outQuad);\n  return (\n    <DesignStage bg={PAPER} raster=\"zoom\">\n      <div\n        style={{\n          position: 'absolute',\n          inset: 0,\n          overflow: 'hidden',\n          fontFamily: \"-apple-system,'Helvetica Neue',Helvetica,Arial,sans-serif\",\n        }}\n      >\n        <div style={{ position: 'absolute', inset: 16 }}>\n          {CELLS.map((cl, i) => {\n            const r = Math.floor(i / COLS);\n            const c = i % COLS;\n            const f = 0.09 + cl.delay;\n            const o = seg(t, f, f + 0.018, E.linear);\n            const sc = seg(t, f, f + 0.03, E.outQuad);\n            // 异常卡：底色染浅红、边框染粉、状态点转红\n            const cT = cl.flagged ? seg(t, cl.at, cl.at + 0.036, E.outQuad) : 0;\n            return (\n              <div\n                key={i}\n                style={{\n                  position: 'absolute',\n                  left: c * (CELL_W + GAP),\n                  top: r * (CELL_H + GAP),\n                  width: CELL_W,\n                  height: CELL_H,\n                  boxSizing: 'border-box', // 等价 grid stretch：边框计入轨道尺寸\n                  borderRadius: 8,\n                  background: cl.flagged ? mix(CARD_WHITE, FLAG_BG, cT) : CARD_WHITE,\n                  border: `1px solid ${cl.flagged ? mix('#E7E7E4', '#F6CFCF', cT) : '#E7E7E4'}`,\n                  display: 'flex',\n                  alignItems: 'center',\n                  justifyContent: 'center',\n                  overflow: 'hidden',\n                  fontWeight: 700,\n                  fontSize: cl.kind === 1 ? 13 : 11,\n                  lineHeight: 1,\n                  fontFamily: '-apple-system,Helvetica,sans-serif', // Remotion 无头浏览器缺 -apple-system，补 Helvetica 对齐原片 SF 字形\n                  color: cl.kind === 1 ? '#6a6f7c' : '#3A3A3E',\n                  opacity: o,\n                  boxShadow: '0 1px 2px rgba(0,0,0,.04)',\n                  visibility: cl.hidden ? 'hidden' : 'visible',\n                  transform: `scale(${lerp(sc, 0.8, 1)})`,\n                }}\n              >\n                {cl.kind === 0 ? cl.ini : cl.kind === 1 ? cl.icon : null}\n                {cl.kind === 2 && (\n                  <div\n                    style={{\n                      position: 'absolute',\n                      inset: 0,\n                      borderRadius: 7,\n                      background: `linear-gradient(${45 + (cl.h % 90)}deg, hsl(${cl.h},18%,78%), hsl(${\n                        (cl.h + 40) % 360\n                      },22%,62%))`,\n                    }}\n                  />\n                )}\n                <div\n                  style={{\n                    position: 'absolute',\n                    right: 4,\n                    top: 4,\n                    width: 5,\n                    height: 5,\n                    borderRadius: '50%',\n                    background: cl.flagged ? mix('#37C46B', '#F0453A', cT) : '#37C46B',\n                  }}\n                />\n              </div>\n            );\n          })}\n        </div>\n        <div\n          style={{\n            position: 'absolute',\n            left: '50%',\n            top: '45%',\n            transform: `translate(-50%,-50%) scale(${lerp(tIn, 0.98, 1)})`,\n            fontWeight: 800,\n            fontSize: 30,\n            lineHeight: 1.2,\n            fontFamily: \"-apple-system,'Helvetica Neue',sans-serif\",\n            letterSpacing: -1.2,\n            color: INK,\n            textAlign: 'center',\n            zIndex: 5,\n            whiteSpace: 'nowrap',\n            opacity: tIn,\n          }}\n        >\n          Let's bring them back in\n        </div>\n        <div\n          style={{\n            position: 'absolute',\n            left: '50%',\n            top: '56%',\n            transform: 'translateX(-50%)',\n            display: 'flex',\n            gap: 14,\n            alignItems: 'center',\n            zIndex: 5,\n            padding: '4px 10px',\n            opacity: legendIn,\n            whiteSpace: 'nowrap',\n          }}\n        >\n          {LEGEND.map(([txt, col]) => (\n            <div\n              key={txt}\n              style={{\n                display: 'flex',\n                alignItems: 'center',\n                gap: 5,\n                fontWeight: 600,\n                fontSize: 11,\n                lineHeight: 1,\n                fontFamily: '-apple-system,Helvetica,sans-serif', // Remotion 无头浏览器缺 -apple-system，补 Helvetica 对齐原片 SF 字形\n                color: MID,\n              }}\n            >\n              <span\n                style={{ width: 6, height: 6, borderRadius: '50%', background: col, display: 'inline-block' }}\n              />\n              <span>{txt}</span>\n            </div>\n          ))}\n        </div>\n      </div>\n    </DesignStage>\n  );\n};\n\n\n"
}