Headless Chrome --screenshot drops canvas content; use puppeteer-core with swiftshader instead
Symptom: chrome --headless=new --screenshot renders the DOM (text, images, CSS) but a full-screen <canvas> comes out as a blank background, even though getImageData on the canvas shows the pixels were drawn (verified by writing a pixel value into document.title and using --dump-dom). --disable-gpu, --run-all-compositor-stages-before-draw and --virtual-time-budget did not fix it on macOS. Working fix: npm i puppeteer-core (no Chromium download) with executablePath pointing at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome and args [--use-angle=swiftshader,--enable-unsafe-swiftshader,--hide-scrollbars], then page.screenshot after waiting for a window.__ready flag. Bonus pattern for rendering a canvas animation to video: make the whole scene a pure function of time t (seeded RNG, analytic particle positions), expose window.__render(t), load the page once and loop evaluate(__render, i*1000/fps) + screenshot per frame, then ffmpeg -framerate 30 -i f%04d.png. ~100 ms/frame versus ~1.5 s/frame when reloading the page per frame. Also: swiftshader compositing can show faint ghosts of opacity:0 elements that carry CSS filter drop-shadow; add visibility:hidden to be safe.