// Browser. Does the game's FLIP flip at 60 fps, hold at 30 fps and in still mode, and how much does the picture // change from one frame to the next? The cutscene demo in headless Chromium (SwiftShader), held at one moment, // with the animation clock (document.timeline) faked to step 16.67 or 33.3 ms per frame. // node --input-type=module - < flipcheck.mjs.txt (PLAYWRIGHT=/path/to/playwright if needed) import { spawn } from "node:child_process"; import { createRequire } from "node:module"; import path from "node:path"; const require = createRequire(import.meta.url); const pw = require(process.env.PLAYWRIGHT || "playwright"); const repo = process.argv[2] || "."; const root = path.resolve(repo, "public/ideas/starlock/.tests"); const port = 47123; const srv = spawn(process.execPath, [path.join(root, "serve.mjs"), String(port)], { stdio: "ignore" }); await new Promise((r) => setTimeout(r, 800)); const browser = await pw.chromium.launch({ args: ["--use-angle=swiftshader", "--enable-unsafe-swiftshader"] }); for (const [step, extra] of [[16.67, ''], [33.3, ''], [16.67, '&still=1']]) { const page = await browser.newPage({ viewport: { width: 390, height: 844 } }); page.on("pageerror", (e) => console.log("pageerror", e.message)); await page.addInitScript((step) => { let c = 0; Object.defineProperty(document.timeline, "currentTime", { get: () => (c += step) }); }, step); await page.goto(`http://127.0.0.1:${port}/ideas/starlock/.tests/cutscene-demo.html?render=3d&n=4&t=9000&paint=full${extra}`); await page.waitForSelector("body[data-ready]", { timeout: 60000 }); const flips = await page.evaluate(async () => { const { FLIP } = await import("../reveal3d.js"); const seen = []; for (let i = 0; i < 80; i++) { window.__frame(9000); if (i >= 70) seen.push(FLIP.value); } return seen; }); const a = await page.screenshot({ timeout: 300000 }); await page.evaluate(() => window.__frame(9000)); const b = await page.screenshot({ timeout: 300000 }); await page.evaluate(() => window.__frame(9000)); const c = await page.screenshot({ timeout: 300000 }); const sharp = createRequire(path.resolve(repo, "package.json"))("sharp"); const raw = async (p) => (await sharp(p).raw().toBuffer()); const [A, B, C] = await Promise.all([a, b, c].map(raw)); let dAB = 0, maxAB = 0, dAC = 0; for (let i = 0; i < A.length; i++) { const d = Math.abs(A[i] - B[i]); if (d) dAB++; maxAB = Math.max(maxAB, d); if (A[i] !== C[i]) dAC++; } console.log(`frame step ${step} ms${extra}: FLIP over frames 70-79 ${flips.join("")}; consecutive frames differ in ${(100 * dAB / A.length).toFixed(1)}% of channels (max ${maxAB}), two apart in ${(100 * dAC / A.length).toFixed(1)}%`); await page.close(); } await browser.close(); srv.kill();