Render fills the bottom ~85px of every frame with the page background color when falling back to system Chrome (preview is fine)
Summary
On macOS, when no dedicated chrome-headless-shell is installed and the render falls back to the system Google Chrome, every rendered frame gets a solid horizontal band across the bottom ~85px. The band is the <body> background color, not anything in the composition. Live preview renders correctly — the band only appears in the rendered video.
Installing the dedicated chrome-headless-shell makes the band disappear, so this looks specific to the system-Chrome capture path.
Environment
- hyperframes 0.7.5
- macOS 26.3 (build 25D125), Apple M4
- Node v22.22.0
- Render browser: system Google Chrome 149.0.7827.155 (no
chrome-headless-shell in ~/.cache/puppeteer; hyperframes browser ensure reports Source: system)
- Capture mode:
screenshot (macOS, so not the Linux beginframe path)
Repro
- On a machine with no
chrome-headless-shell installed (so render uses system Chrome).
- Any 1920×1080 composition whose
<body>/html has a visible background color, e.g.:
<head><style>html,body{margin:0;width:1920px;height:1080px;background:#FAF9F5;}</style></head>
with a #root that paints a full-bleed gradient to the bottom edge.
npx hyperframes render .
- Inspect any frame: the bottom ~85px is a flat band of the
<body> color, with a hard horizontal edge that also clips the composition's content (e.g. card shadows) above it.
In our case the edge sits at ~y=988 of 1080; the band measures (251,248,245), exactly the body #FAF9F5.
Evidence that the band is the page background
Setting the body background to red and re-rendering turns the bottom band red (255,24,0) — confirming the band is the page background bleeding through, not a composition layer.
This matches what pageScreenshotCapture does: Page.captureScreenshot with clip {x:0,y:0,width,height}, captureBeyondViewport: false, and no Emulation.setDefaultBackgroundColorOverride. If the captured surface is shorter than the clip, Chrome backfills the out-of-surface region with the page's base background color.
What fixes it
Installing the dedicated headless shell and pointing the renderer at it:
npx -y @puppeteer/browsers install chrome-headless-shell@stable --path ~/.cache/puppeteer
# or, ad hoc:
PRODUCER_HEADLESS_SHELL_PATH=/path/to/chrome-headless-shell npx hyperframes render .
With chrome-headless-shell the full 1080px renders correctly and the band is gone — same composition, same flags, same machine.
Notes / what I could and couldn't isolate
- The captured surface appears to be ~85px shorter than the requested 1080 only on the system-Chrome path.
createCaptureSession does call page.setViewport({width, height, deviceScaleFactor}), so the layout viewport is 1080 and the composition lays out fine — but the captured surface (fromSurface: true) ends up short.
- I could not reproduce it with a standalone
puppeteer-core script launching the same system Chrome binary with --window-size=1920,1080, setViewport(1920×1080), then Page.captureScreenshot({clip, captureBeyondViewport:false}) — that produces a correct full-height frame. So it does not seem to be simply "system Chrome cannot render at 1080"; something in the HyperFrames launch/capture setup interacts with the system-Chrome fallback to produce the short surface.
Questions
- Is the system-Chrome fallback expected to produce correct renders, or is
chrome-headless-shell effectively required? If the dedicated shell is required for correct output, would it make sense for hyperframes doctor / browser ensure to warn (rather than silently pass) when only system Chrome is available?
- Independently of the surface issue, would it be reasonable for the opaque capture path to defend against a short surface — e.g.
captureBeyondViewport: true, or setDefaultBackgroundColorOverride to transparent — so a surface/clip mismatch can't bleed the page background into the frame?
Happy to share a minimal repo, full frames, and the red-background comparison if useful.
Render fills the bottom ~85px of every frame with the page background color when falling back to system Chrome (preview is fine)
Summary
On macOS, when no dedicated
chrome-headless-shellis installed and the render falls back to the system Google Chrome, every rendered frame gets a solid horizontal band across the bottom ~85px. The band is the<body>background color, not anything in the composition. Live preview renders correctly — the band only appears in the rendered video.Installing the dedicated
chrome-headless-shellmakes the band disappear, so this looks specific to the system-Chrome capture path.Environment
chrome-headless-shellin~/.cache/puppeteer;hyperframes browser ensurereportsSource: system)screenshot(macOS, so not the Linuxbeginframepath)Repro
chrome-headless-shellinstalled (so render uses system Chrome).<body>/htmlhas a visible background color, e.g.:#rootthat paints a full-bleed gradient to the bottom edge.npx hyperframes render .<body>color, with a hard horizontal edge that also clips the composition's content (e.g. card shadows) above it.In our case the edge sits at ~y=988 of 1080; the band measures
(251,248,245), exactly the body#FAF9F5.Evidence that the band is the page background
Setting the body background to red and re-rendering turns the bottom band red
(255,24,0)— confirming the band is the page background bleeding through, not a composition layer.This matches what
pageScreenshotCapturedoes:Page.captureScreenshotwithclip {x:0,y:0,width,height},captureBeyondViewport: false, and noEmulation.setDefaultBackgroundColorOverride. If the captured surface is shorter than the clip, Chrome backfills the out-of-surface region with the page's base background color.What fixes it
Installing the dedicated headless shell and pointing the renderer at it:
With
chrome-headless-shellthe full 1080px renders correctly and the band is gone — same composition, same flags, same machine.Notes / what I could and couldn't isolate
createCaptureSessiondoes callpage.setViewport({width, height, deviceScaleFactor}), so the layout viewport is 1080 and the composition lays out fine — but the captured surface (fromSurface: true) ends up short.puppeteer-corescript launching the same system Chrome binary with--window-size=1920,1080,setViewport(1920×1080), thenPage.captureScreenshot({clip, captureBeyondViewport:false})— that produces a correct full-height frame. So it does not seem to be simply "system Chrome cannot render at 1080"; something in the HyperFrames launch/capture setup interacts with the system-Chrome fallback to produce the short surface.Questions
chrome-headless-shelleffectively required? If the dedicated shell is required for correct output, would it make sense forhyperframes doctor/browser ensureto warn (rather than silently pass) when only system Chrome is available?captureBeyondViewport: true, orsetDefaultBackgroundColorOverrideto transparent — so a surface/clip mismatch can't bleed the page background into the frame?Happy to share a minimal repo, full frames, and the red-background comparison if useful.