{
  "slug": "glitch-cycle",
  "title": "Glitch status cycle",
  "tagline": "The same slot cycles through 4 status phrases, each scrambling fully at the head and tail with occasional mid-phrase jitter, RGB split and a full-line shift at each switch; the last phrase resolves clean.",
  "description": "The same slot cycles through 4 status phrases, each scrambling fully at the head and tail with occasional mid-phrase jitter, RGB split and a full-line shift a",
  "category": "typography",
  "tags": [
    "shotcraft",
    "remotion",
    "typography"
  ],
  "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/glitch-cycle/poster-8533c9373170.jpg",
    "video": "https://gimgs.net/motion/media/glitch-cycle/video-dfcdb4b01b8b.mp4"
  },
  "variants": [
    {
      "key": "glitch-cycle",
      "label": "glitch-cycle · Glitch status cycle",
      "description": "The same fixed-width slot cycles through 4 status phrases, each scrambling fully at the head and tail on probability keyframes [1,0,0,0.1,0,0,1] with occasional single-character jitter mid-phrase, RGB split and a full-line shift stacking at each switch; the last phrase's probability resolves to 0 for a clean finish.",
      "use": "",
      "video": "https://gimgs.net/motion/media/glitch-cycle/video-dfcdb4b01b8b.mp4",
      "poster": "https://gimgs.net/motion/media/glitch-cycle/poster-8533c9373170.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/glitch-cycle.html",
  "json_url": "https://gimgs.net/motion/glitch-cycle.json",
  "template_url": "https://gimgs.net/motion/glitch-cycle/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/glitch-cycle.md",
  "updated_at": "2026-09-24T07:39:58.838Z",
  "prompt": "## When to use\n\nStatus broadcasting for loading/build/deploy processes; the \"system self-narration\" of a tech-flavored opener; a backdrop for advancing time with a machine voice.\n\n## Intention\n\nThe clumsiest way to broadcast status is four lines fading in one after another. Cycling compresses them into **one slot**: each phrase \"burns out\" into scramble at head and tail then \"solidifies\", staying almost clean mid-phrase with only occasional twitches — what the audience reads is \"the system is progressing step by step\", not four independent captions. Scramble density is this card's grammar: dense = switching, clean = currently in this state. A progress bar at the bottom strings the four beats into one timeline, and the last phrase's probability resolves to 0 so it ends the moment it settles.\n\n## Duration & energy\n\n- Duration: About 5.6s (168f@30fps, 4 phrases at 42f each)\n- Energy: Medium-high (a sustained high-frequency noise pulse, with switch points as peaks)\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n状态播报最笨的做法是四行字逐条淡入。轮播把它们压进**一个槽位**：\n短语头尾用乱码\"熔断\"再\"凝固\"，中段几乎干净只偶尔抽一下——观众读到的\n是\"系统在一步步推进\"，而不是四条独立文案。乱码浓度是这卡的语法：\n浓 = 正在切换，淡 = 正在这一状态里。底部进度条把四拍串成一条时间线，\n最后一条把概率收到 0，落定即结束。\n\n## 动效核心\n- 槽位按最长短语预建 `MAXCH` 个 span（`min-width:0.66em` 固定槽宽），\n  短短语右侧填空格——**字符不增删，只换内容**，整行永不重排\n- 时段等分：`slot = floor(t·4)`，短语内进度 `p = t·4 - slot`（每条 42f）\n- glitch 概率关键帧 `KF=[1,0,0,0.1,0,0,1]` 按 p 线性插值：**开头 1.0 全乱、\n  中段 0、约 p=0.5 处回弹到 0.1（偶发抽字）、结尾又回 1.0 熔进下一条**\n- 末条专用 `KF_LAST=[1,0,0,0.1,0,0,0]`，结尾概率归零 → t=1 画面干净\n- 逐字符判定：`rand(i·31 + bucket·17 + slot·97) < g` 才替换成字池字符，\n  命中字随机上 #6c8cff 蓝或 #4a5270 暗灰；`bucket = floor(frame/2)`，\n  即每 2 帧重掷一次\n- 整行抖动与 RGB 分离强度全部乘 g：`jx = ±g·10`px、`jy = ±g·4`px，\n  `text-shadow` 左红右青各偏 `g·3`px（g>0.04 才开）——**噪声浓度一个变量\n  同时驱动换字、位移、色散**，所以三者永不打架；底部 120×2px 进度条\n  `width = t·100%` 线性走满，与四拍无关地铺底\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 短语表 | 4 条，最长 17 字符（`COMPILING SHADERS`） | 每条 42f 是\"读得完一个状态\"的下限；5 条以上单条不足 34f 观众只看到闪 |\n| 概率关键帧 | `[1,0,0,0.1,0,0,1]`（6 段） | 中间那个 0.1 是\"活着\"的证据，删了短语中段死板；把 0 段调到 0.3+ 全片糊成故障 |\n| 重掷频率 | 每 2 帧（`floor(frame/2)`） | 1 帧一换糊成灰带；4 帧以上乱码看得清是\"另一个词\"，不像噪声 |\n| 抖动幅度 | `x ±g·10` / `y ±g·4`px | x 大于 y 是横向撕裂的关键（等比抖动读作地震）；>20px 整行跳出槽位 |\n| RGB 分离 | 左红右青各 `g·3`px，阈值 g>0.04 | 这是\"电子故障\"的身份证；无阈值时中段那 0.1 的概率会让字常年带毛边 |\n| 槽宽字体 | SF Mono 26px、`letter-spacing:3px`、槽 `0.66em` | 等宽 + 固定槽宽是刚需；字距 3px 让乱码之间有呼吸，挤在一起读作乱码块 |\n| 进度条 | 120×2px，`#6c8cff` 填充，线性 | 唯一的线性元素，给四拍噪声一个稳定参照；删了轮播读不出\"在推进\" |\n\n## 已知坑\n- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿，\n  首次实战须以真实素材回验\n- **换项目文案时四条短语字符数要接近（demo 12–17 字符）**：槽位数按最长\n  短语一次建好，短短语靠右填空格——如果一条只有 4 字符、另一条 30 字符，\n  短的那条在 30 格里孤零零居左，行宽全程被最长条撑着，节奏读作漂移\n- 概率关键帧是 6 段等分映射到短语内进度：短语条数变了单条时长跟着变，\n  但关键帧形状不变——所以条数越多，\"全乱码\"那两段的绝对时长越短，\n  切换感越弱，超过 5 条就该把首尾的 1.0 段拉宽\n- 末条必须用 `KF_LAST`（结尾收 0）：用普通 KF 会让 t=1 停在满乱码上，\n  接下一镜时读作没渲染完\n- 深底专用：#0a0b10 底 + 冷蓝噪声是整套配色的地基，浅底上 RGB 分离\n  几乎不可见；声音也强依赖——四个切换点天然是四个节奏点，实战必须配\n  电子故障/开关音；\n  配法纪律见 sound-design.md（同类元素同音，连发交替双样本防机枪感）\n- 与 scramble 分工：那卡是**一次性解密**（乱→定，不再乱），本卡是\n  **循环播报**（乱→定→乱→定）；同片只留一个，两个并置读作同一手法用了两遍\n\n## 参考实现\ndemos/typography/glitch-cycle/\n（GlitchCycle.tsx）\n\n## Reference implementation (Remotion)\n\n- demos/typography/glitch-cycle/GlitchCycle.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/typography/glitch-cycle/GlitchCycle.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/typography/glitch-cycle/GlitchCycle.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/typography/glitch-cycle/GlitchCycle.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/typography/glitch-cycle/GlitchCycle.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/typography/glitch-cycle/GlitchCycle.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/typography/glitch-cycle/GlitchCycle.tsx =====\n// glitch-cycle — Glitch Cycle 乱码轮播（motion-lab 定稿转原生 Remotion）\n// 同一位置循环轮播状态短语，每条短语头尾乱码、中段偶发轻微抖动\n// （glitch 概率关键帧 [1,0,0,0.1,0,0,1]，最后一条结尾收为 0 保证 t=1 画面干净），\n// 切换瞬间伴随 RGB 分离与位移抖动；底部细进度条随 t 匀速填满。\n// 设计坐标 480×270（DesignStage 等比放大），参数表数值以此坐标系标定。\nimport React from 'react';\nimport { DesignStage, lerp, rand, useT } from '../../_fixtures/Motion';\n\nexport const GLITCH_CYCLE_DURATION = 168; // 5600ms @30fps\n\nconst PHRASES = ['INITIALIZING', 'LOADING ASSETS', 'COMPILING SHADERS', 'READY TO SHIP'];\nconst POOL = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#$%&<>/\\\\';\n// glitch 概率关键帧 [1,0,0,0.1,0,0,1]（最后一条结尾收为 0，保证 t=1 画面干净）\nconst KF = [1, 0, 0, 0.1, 0, 0, 1];\nconst KF_LAST = [1, 0, 0, 0.1, 0, 0, 0];\nconst MAXCH = Math.max(...PHRASES.map((p) => p.length));\n\n// 关键帧折线采样：p∈[0,1] 映射到 kf 段内线性插值\nconst glitchAt = (kf: number[], p: number) => {\n  const segs = kf.length - 1;\n  const x = Math.min(segs - 1e-6, Math.max(0, p * segs));\n  const i = Math.floor(x);\n  return lerp(x - i, kf[i], kf[i + 1]);\n};\n\nexport const GlitchCycle: React.FC = () => {\n  const t = useT();\n  const N = PHRASES.length;\n  const slot = Math.min(N - 1, Math.floor(t * N));\n  const p = t * N - slot; // 短语内进度 0..1\n  const text = PHRASES[slot];\n  const g = glitchAt(slot === N - 1 ? KF_LAST : KF, p);\n  const frame = Math.floor(t * 168);\n  const bucket = Math.floor(frame / 2); // 乱码跳字节流：每 2 帧换一批随机字符\n  // 整行抖动 + RGB 分离，强度跟随 glitch 概率\n  const jx = (rand(bucket * 5 + slot) - 0.5) * g * 10;\n  const jy = (rand(bucket * 9 + slot + 40) - 0.5) * g * 4;\n  return (\n    <DesignStage bg=\"#0a0b10\">\n      <div\n        style={{\n          position: 'absolute',\n          inset: 0,\n          display: 'flex',\n          alignItems: 'center',\n          justifyContent: 'center',\n          background: '#0a0b10',\n        }}\n      >\n        <div\n          style={{\n            display: 'flex',\n            fontFamily: '\"SF Mono\",Menlo,monospace',\n            fontSize: 26,\n            letterSpacing: 3,\n            color: '#dfe6f5',\n            transform: `translate(${jx}px,${jy}px)`,\n            textShadow:\n              g > 0.04\n                ? `${g * 3}px 0 rgba(255,60,90,${g * 0.8}), ${-g * 3}px 0 rgba(60,220,255,${g * 0.8})`\n                : 'none',\n          }}\n        >\n          {Array.from({ length: MAXCH }, (_, i) => {\n            const ch = i < text.length ? text[i] : ' ';\n            let content = ch;\n            let color: string | undefined;\n            if (ch !== ' ') {\n              // 逐字符按种子掷 glitch：命中显示乱码 + 变色，未命中显示真字符\n              const hit = rand(i * 31 + bucket * 17 + slot * 97) < g;\n              if (hit) {\n                content = POOL[Math.floor(rand(i * 131 + bucket * 7 + slot * 13) * POOL.length)];\n                color = rand(i + bucket) > 0.5 ? '#6c8cff' : '#4a5270';\n              } else {\n                color = '#dfe6f5';\n              }\n            }\n            return (\n              <span key={i} style={{ minWidth: '0.66em', textAlign: 'center', color }}>\n                {content}\n              </span>\n            );\n          })}\n        </div>\n      </div>\n      {/* 底部细进度条：随 t 匀速填满 */}\n      <div\n        style={{\n          position: 'absolute',\n          left: '50%',\n          top: '63%',\n          width: 120,\n          height: 2,\n          marginLeft: -60,\n          background: '#232840',\n          borderRadius: 1,\n          overflow: 'hidden',\n        }}\n      >\n        <div style={{ height: '100%', width: `${t * 100}%`, background: '#6c8cff' }} />\n      </div>\n    </DesignStage>\n  );\n};\n\n\n"
}