// A stretch of Starlock's real cutscene (public/ideas/starlock/.tests/cutscene-demo.html), filmed frame by frame // by headless Chromium on a phone-sized page (390 x 844) at a pixel ratio of 2, or as given: each frame drawn at its own moment and saved as // a lossless PNG, so nothing is lost before the footage is cut. Run it from a checkout's root to film that // checkout's cutscene (the before and after footage are two checkouts: before the glow change and after it). // // node --input-type=module - [from-ms to-ms fps dpr] < research/2026-10-22-starlock-glow/code/film.mjs.txt // // (named .txt because the site serves code/ as text; see README.md). The software GPU is slow, so the page // isn't timed: each frame is drawn at t = from + i * 1000 / fps, whatever the wall clock says. import { spawn } from 'node:child_process'; import { createRequire } from 'node:module'; import fs from 'node:fs'; import net from 'node:net'; import path from 'node:path'; const require = createRequire(path.join(process.cwd(), 'x.js')); const { chromium } = (() => { for (const id of [process.env.PLAYWRIGHT, 'playwright', '/opt/node-tools/node_modules/playwright'].filter(Boolean)) { try { return require(id); } catch {} } throw new Error('Playwright not found: set PLAYWRIGHT to its path.'); })(); const [out, from = '7600', to = '10800', fps = '30', dpr = '2'] = process.argv.slice(2); fs.mkdirSync(out, { recursive: true }); const port = await new Promise((r) => { const s = net.createServer().listen(0, () => { const { port } = s.address(); s.close(() => r(port)); }); }); const srv = spawn(process.execPath, [path.join(process.cwd(), 'public/ideas/starlock/.tests/serve.mjs'), String(port)], { stdio: 'ignore' }); const url = `http://127.0.0.1:${port}/ideas/starlock/.tests/cutscene-demo.html`; for (let i = 0; i < 100 && !(await fetch(url).then((r) => r.ok, () => false)); i++) await new Promise((r) => setTimeout(r, 50)); const browser = await chromium.launch({ args: ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] }); try { const page = await browser.newPage({ viewport: { width: 390, height: 844 }, deviceScaleFactor: +dpr }); await page.goto(`${url}?render=3d&n=4&t=${from}&paint=full`); await page.waitForFunction(() => document.body.dataset.ready === '1', null, { timeout: 120000 }); const n = Math.round(((+to - +from) * +fps) / 1000); for (let i = 0; i <= n; i++) { await page.evaluate((t) => window.__frame(t), +from + (i * 1000) / +fps); await page.screenshot({ path: path.join(out, `f${String(i).padStart(4, '0')}.png`), timeout: 180000 }); } } finally { await browser.close(); srv.kill(); }