{"record":{"id":"cf8c82a2182fe8f5","repo":"heygen-com/hyperframes","slug":"ghost-renders-a-canvas-webgl-motion-trail-but-t","errorCode":null,"errorMessage":"--ghost renders a canvas/WebGL motion trail, but this composition has no <canvas>. Use the default --shot onion for DOM/SVG transform motion.","messagePattern":"--ghost renders a canvas/WebGL motion trail, but this composition has no <canvas>\\. Use the default --shot onion for DOM/SVG transform motion\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/cli/src/commands/motionShot.ts","lineNumber":526,"sourceCode":"\n// Rendered (\"ghost\") onion-skin: screenshot the REAL painted stage at each\n// sample and composite them as translucent ghosts. This is the onion-skin for\n// canvas/WebGL motion the marker sampler is blind to (the markers project a\n// bbox; the pixels are the motion). Works for any visual composition, but\n// requires a <canvas> (DOM/SVG transform motion already shows up in the\n// default marker onion).\nasync function captureGhostOnionSkin(\n  page: import(\"puppeteer-core\").Page,\n  requests: ShotRequest[],\n  times: number[],\n  size: FrameSize,\n  camera: OrbitCamera,\n  outPath: string,\n): Promise<string> {\n  await applyOrbitCameraIfAngled(page, requests, camera);\n  const hasCanvas = await page.evaluate(() => document.querySelectorAll(\"canvas\").length > 0);\n  if (!hasCanvas) {\n    throw new Error(\n      \"--ghost renders a canvas/WebGL motion trail, but this composition has no <canvas>. Use the default --shot onion for DOM/SVG transform motion.\",\n    );\n  }\n  const frames: string[] = [];\n  for (const t of times) {\n    frames.push(await captureGhostFrame(page, t));\n  }\n  const label = `${cameraLabel(camera)}  ·  rendered onion  ·  ${times.length} frames  ·  t ${times[0]}–${times[times.length - 1]}s`;\n  const dataUrl = (await page.evaluate(\n    compositeGhostFrames,\n    frames,\n    ghostAlphas(frames.length),\n    size.width,\n    size.height,\n    label,\n  )) as string;\n  const b64 = String(dataUrl).replace(/^data:image\\/png;base64,/, \"\");\n  if (!b64) throw new Error(\"ghost composite returned no data\");","sourceCodeStart":508,"sourceCodeEnd":544,"githubUrl":"https://github.com/heygen-com/hyperframes/blob/c2996c8626135db5253519359d8a063d3bafad8d/packages/cli/src/commands/motionShot.ts#L508-L544","documentation":"Thrown by captureGhostOnionSkin when --ghost is requested but the composition contains no <canvas> element. The ghost mode composites real painted canvas/WebGL pixels across sample times to produce a rendered motion trail; without a canvas there are no pixels to capture. DOM/SVG transform motion is already covered by the default marker onion-skin and does not need --ghost.","triggerScenarios":"Running `hyperframes shot --ghost out.png` on a composition that animates only DOM elements (divs, SVG) with CSS/GSAP transforms and contains no <canvas>.","commonSituations":"Agents that default to --ghost for all shots. Users who assume --ghost improves any motion capture. Compositions that use WebGL conditionally (the canvas is added by JS that hasn't run).","solutions":["Drop --ghost to use the default marker onion-skin, which captures DOM/SVG transform motion","Only use --ghost when the composition has a <canvas> (WebGL/2D canvas rendering)","Verify document.querySelectorAll('canvas').length > 0 in the rendered page before using --ghost"],"exampleFix":"// before — DOM/SVG composition, no canvas\nhyperframes shot --ghost out.png\n// after — default marker onion for DOM/SVG motion\nhyperframes shot out.png","handlingStrategy":"validation","validationCode":"import { parseHTML } from 'linkedom';\nfunction compositionHasCanvas(html: string): boolean {\n  return parseHTML(html).document.querySelectorAll('canvas').length > 0;\n}\n// before running --ghost\nif (!compositionHasCanvas(html)) {\n  throw new Error('No <canvas> — use the default marker onion instead of --ghost');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only use --ghost for canvas/WebGL compositions","Default to the marker onion (no --ghost) for DOM/SVG transform motion"],"tags":["motion-shot","ghost","canvas","cli","validation"],"backgroundTag":null,"analyzedSha":"c2996c8626135db5253519359d8a063d3bafad8d","analyzedAt":"2026-08-12T22:18:56.877Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}