{
  "slug": "depth-layer-moves",
  "title": "Layered depth camera moves",
  "tagline": "Two layered-depth camera moves: multiplane parallax dolly (3 layers at graded speeds pan out depth) and fake dolly-zoom (subject pinned, background swells inward).",
  "description": "Two layered-depth camera moves: multiplane parallax dolly (3 layers at graded speeds pan out depth) and fake dolly-zoom (subject pinned, background swells inw",
  "category": "camera",
  "tags": [
    "shotcraft",
    "remotion",
    "camera"
  ],
  "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/depth-layer-moves/poster-8099a9cc240b.jpg",
    "video": "https://gimgs.net/motion/media/depth-layer-moves/video-34fc6f5c979a.mp4"
  },
  "variants": [
    {
      "key": "multiplane",
      "label": "multiplane · Multiplane parallax dolly",
      "description": "Multiplane parallax dolly — 3 layers of material pan horizontally at graded speeds (near fast, far slow), flat screenshots slide out into depth.",
      "use": "",
      "video": "https://gimgs.net/motion/media/depth-layer-moves/video-34fc6f5c979a.mp4",
      "poster": "https://gimgs.net/motion/media/depth-layer-moves/poster-8099a9cc240b.jpg"
    },
    {
      "key": "dolly-zoom",
      "label": "dolly-zoom · Fake dolly-zoom",
      "description": "Fake dolly-zoom — subject pinned motionless while the background layer keeps swelling inward, the mounting pressure of a dramatic build-up.",
      "use": "",
      "video": "https://gimgs.net/motion/media/depth-layer-moves/video-ff447c6b6673.mp4",
      "poster": "https://gimgs.net/motion/media/depth-layer-moves/poster-dolly-zoom-9d394d43f523.jpg"
    }
  ],
  "credits": {
    "author": "video-shotcraft (Vincent Wei)",
    "url": "https://github.com/Vincentwei1021/video-shotcraft"
  },
  "page_url": "https://gimgs.net/motion/depth-layer-moves.html",
  "json_url": "https://gimgs.net/motion/depth-layer-moves.json",
  "template_url": "https://gimgs.net/motion/depth-layer-moves/template.html",
  "play_url": null,
  "markdown_url": "https://gimgs.net/motion/depth-layer-moves.md",
  "updated_at": "2026-09-24T07:39:55.273Z",
  "prompt": "## When to use\n\nFor sections where flat screenshots need to feel like they have depth; use dolly-zoom for dramatic build-up moments (≤1 per film).\n\n## Intention\n\nA whole-page pan is \"looking at the page\"; layered depth is \"being inside the page's space\". Parallax dolly adds depth to a lateral pan (Disney's multiplane-camera principle, the same texture seen in Linear's films); the fake dolly-zoom reverses it — the subject stays perfectly still while the whole world presses in, building tension for a dramatic moment.\n\n## Duration & energy\n\n- Duration: Parallax dolly: 4–5s sustained; dolly-zoom: 3–4s single-direction travel.\n- Energy: Parallax = Medium (textural); dolly-zoom = Medium-high (mounting pressure)\n\n## Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)\n\n## 意图\n整页平移是\"看页面\"，分层深度是\"在页面的空间里\"。视差滑轨给横移\n加纵深（迪士尼多平面摄影机原理，Linear 片同款质感）；伪 dolly-zoom\n反过来——主角纹丝不动、全世界压过来，给戏剧性时刻蓄力。\n\n## 动效核心\n- **视差滑轨（3 层）**：背景整页（系数 0.35 + blur 2px + 降饱和\n  0.92 + opacity 0.85，退成环境）、中景真实卡切片（系数 0.7，\n  主阅读层，无 blur）、前景浮块（系数 1.4 + blur 3px，掠过镜头）。\n  同一条 drive 位移乘各层系数\n- **伪 dolly-zoom**：主体高清卡固定 px 钉屏中（不参与任何变换）；\n  背景层（整页 + 卡群一体）scale 1→2.25 从画面中心膨胀 + blur 0→3.5px\n  渐深；主体落影随进程加深（12→28px offset）强化\"钉住感\"\n- 两款都不拆真实页面排版：整页作为一体成层，独立元素用切片另起层\n  ——层间关系是\"景深\"，不是\"页面被撕开\"\n\n## 参数表\n| 参数 | 典型值 | 调节手感 |\n|------|--------|----------|\n| 视差层系数 | 0.35 / 0.7 / 1.4 | 梯度 ≥2 倍层间才可辨；层数 ≤4 防过载 |\n| 层次锚 | 前后景 blur + 背景降饱和 | 没有 blur/饱和锚，读作\"贴片乱飞\" |\n| dolly 膨胀行程 | scale 1→2.0–2.5 | 行程小了压迫感不足；背景 opacity ≤0.6 防读作故障 |\n\n## 已知坑\n- 主阅读层/主体必须高清纹理且无 blur——观众在读它\n- 视差与 PageCam rot 3D 的组合（斜拍视差）未验证，实战先渲 still 确认\n- dolly-zoom 是\"戏剧调料\"：一支片 ≤1 次，日常段落用视差滑轨\n- 参数经占位素材调校转正，非实战定稿，首次实战后回验\n\n## 参考实现\ndemos/camera/depth-layer-moves/\n（DollyZoomReal.tsx / MultiplaneReal.tsx）\n\n## Reference implementation (Remotion)\n\n- demos/camera/depth-layer-moves/DollyZoomReal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/camera/depth-layer-moves/DollyZoomReal.tsx\n- demos/camera/depth-layer-moves/MultiplaneReal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/camera/depth-layer-moves/MultiplaneReal.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/camera/depth-layer-moves/DollyZoomReal.tsx, demos/camera/depth-layer-moves/MultiplaneReal.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/camera/depth-layer-moves/DollyZoomReal.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/camera/depth-layer-moves/DollyZoomReal.tsx"
    },
    {
      "path": "demos/camera/depth-layer-moves/MultiplaneReal.tsx",
      "url": "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/camera/depth-layer-moves/MultiplaneReal.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/camera/depth-layer-moves/DollyZoomReal.tsx",
      "https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/camera/depth-layer-moves/MultiplaneReal.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/camera/depth-layer-moves/DollyZoomReal.tsx =====\n// dolly-zoom 滑动变焦（轮 F）——主体卡（card4-hires）大小锁定屏中，\n// 背景真实卡群 + 整页反向膨胀逼近（scale + blur 渐深），\n// \"世界压过来\"而主角纹丝不动。伪 dolly-zoom：无需 3D，分层反向补偿。\nimport { AbsoluteFill, Img, interpolate, staticFile, useCurrentFrame, Easing } from 'remotion';\nimport layout from '../../_textures/live-layout.json';\n\nexport const DOLLYZOOM_DUR = 135;\n\nconst BG_CARDS = layout.projects.cards.filter((_, i) => i !== 3);\n\nexport const DollyZoomReal: React.FC = () => {\n  const frame = useCurrentFrame();\n  const t = interpolate(frame, [15, 110], [0, 1], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n    easing: Easing.bezier(0.4, 0, 0.3, 1),\n  });\n  const bgScale = 1 + t * 1.25;\n  const bgBlur = t * 3.5;\n  return (\n    <AbsoluteFill style={{ backgroundColor: '#efece6', overflow: 'hidden' }}>\n      {/* 背景：整页 + 卡群，从画面中心膨胀逼近 */}\n      <div\n        style={{\n          position: 'absolute', inset: 0,\n          transform: `scale(${bgScale})`, transformOrigin: '960px 540px',\n          filter: `blur(${bgBlur}px) saturate(0.9)`,\n        }}\n      >\n        <Img src={staticFile('textures/live/projects-full.png')} style={{ position: 'absolute', left: 0, top: -400, width: 1920, opacity: 0.55 }} />\n        {BG_CARDS.slice(0, 8).map((c, k) => (\n          <Img\n            key={c.file + k}\n            src={staticFile(`textures/live/${c.file}`)}\n            style={{\n              position: 'absolute',\n              left: [180, 1280, 240, 1220, 700, 760, 60, 1500][k],\n              top: [140, 120, 700, 720, 60, 840, 420, 430][k],\n              width: 320, borderRadius: 12, opacity: 0.9,\n              boxShadow: '0 6px 20px rgba(31,28,23,0.12)',\n            }}\n          />\n        ))}\n      </div>\n      {/* 主体：高清卡视觉大小恒定钉在屏中，落影渐深强调\"世界在动我不动\" */}\n      <Img\n        src={staticFile('textures/live/card4-hires.png')}\n        style={{\n          position: 'absolute', left: 960 - 260, top: 540 - 228, width: 520,\n          borderRadius: 14,\n          boxShadow: `0 ${12 + t * 16}px ${40 + t * 28}px rgba(31,28,23,${0.16 + t * 0.10})`,\n        }}\n      />\n    </AbsoluteFill>\n  );\n};\n\n\n// ===== demos/camera/depth-layer-moves/MultiplaneReal.tsx =====\n// multiplane parallax（轮 E）——真实页面拆 3 层深度横移：\n// 背景整页（0.35x，微 blur 退后）+ 中景真实卡组（0.7x）+\n// 前景浮块（1.4x，search 切片 + 高清卡，轻 blur 拉焦平面）。\n// 系数克制（背景不动排版，卡组独立成层）防\"排版散架\"。\nimport { AbsoluteFill, Img, interpolate, staticFile, useCurrentFrame, Easing } from 'remotion';\nimport layout from '../../_textures/live-layout.json';\n\nexport const MULTIPLANE_DUR = 135;\n\nconst CARDS = layout.projects.cards.slice(0, 6);\n\nexport const MultiplaneReal: React.FC = () => {\n  const frame = useCurrentFrame();\n  const drive = interpolate(frame, [10, 125], [0, 1000], {\n    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',\n    easing: Easing.bezier(0.35, 0, 0.25, 1),\n  });\n  return (\n    <AbsoluteFill style={{ backgroundColor: '#efece6', overflow: 'hidden' }}>\n      {/* 背景层 0.35x：整页微退焦 */}\n      <div style={{ position: 'absolute', inset: 0, transform: `translateX(${-drive * 0.35}px) scale(1.05)`, filter: 'blur(2px) saturate(0.92)', opacity: 0.85 }}>\n        <Img src={staticFile('textures/live/projects-full.png')} style={{ position: 'absolute', left: 0, top: -300, width: 2100 }} />\n      </div>\n      {/* 中景层 0.7x：真实卡组横排（主阅读层） */}\n      <div style={{ position: 'absolute', top: 330, transform: `translateX(${-drive * 0.7}px)` }}>\n        {CARDS.map((c, k) => (\n          <Img\n            key={c.file}\n            src={staticFile(`textures/live/${c.file}`)}\n            style={{\n              position: 'absolute', left: 200 + k * 480, top: (k % 2) * 40,\n              width: 420, borderRadius: 12,\n              boxShadow: '0 8px 28px rgba(31,28,23,0.14)',\n            }}\n          />\n        ))}\n      </div>\n      {/* 前景层 1.4x：浮块掠过（search 切片 + 高清卡），轻 blur */}\n      <div style={{ position: 'absolute', inset: 0, transform: `translateX(${-drive * 1.4}px)`, filter: 'blur(3px)' }}>\n        <Img src={staticFile('textures/live/float-search.png')} style={{ position: 'absolute', left: 900, top: 150, width: 700, borderRadius: 22, boxShadow: '0 12px 36px rgba(31,28,23,0.18)' }} />\n        <Img src={staticFile('textures/live/card4-hires.png')} style={{ position: 'absolute', left: 2100, top: 700, width: 380, borderRadius: 12, boxShadow: '0 12px 36px rgba(31,28,23,0.20)' }} />\n      </div>\n    </AbsoluteFill>\n  );\n};\n\n\n"
}