js-threejs-vendor-file-protocol-classic-scripts
Done. The problem is diagnosed and the fix is verified end-to-end on this actual GPU-less SwiftShader host. Full solution: ~/neon-rush/SOLUTION.md (project + writeup live in ~/neon-rush).
file:// pages have origin "null". Chromium's module loader is CORS-enforced, so every ESM script — including importmap-mapped local files — is blocked with a console error:
Access to script at 'file://.../three.module.js' from origin 'null' has been blocked by CORS policy...
Reproduced exactly via importmap.html / scripts/repro-importmap.js / tests/pitfall.spec.js — the module never executes, and each block is a console error that fails every test. Classic <script src> tags are exempt from this check, so the fix is to vendor as classic scripts on window.THREE (the historical examples/js pattern, which r148+ no longer ships prebuilt).
scripts/vendor.js, mechanical & idempotent)import { A, B as C } from 'three' → const { A, B: C } = THREE inside an IIFE (each converted module; avoids the shared global-lexical Identifier 'Pass' has already been declared trap)export { E, F as G } → THREE.E = E; THREE.G = Fnode --check each generated file — a parse error can never reach the browserindex.html loads ./vendor/* in dependency order (shaders → Pass → passes → composer → bloom → game), game last, no async/defernew THREE.EffectComposer / RenderPass / UnrealBloomPass / OutputPass at startup — no dynamic CDN importsgetExtension('WEBGL_debug_renderer_info').UNMASKED_RENDERER_WEBGL matched against /swiftshader|llvmpipe|software/i → bloom skipped (bloomEnabled:false on this SwiftShader host, OutputPass keeps tone mapping); probed real renderer string confirms the matchwaitForFunction for landing→running (15s), distance >= 60 (20s), slide→running (15s each way); distance integrated from real dt so results hold at ~5fps| run | result |
|---|---|
npm test (regenerates vendor via pretest) |
4 passed (11.6s), zero console errors on file:// |
npx playwright test --repeat-each=2 |
8 passed (23.2s), no flakes |
npm run test:pitfall |
1 passed — documents the CORS block |
| In-game state dump | {state: "running", distance: 67, software: true, bloomEnabled: false, frames: 216}, console errors: [] |
| Screenshot pixel analysis | live neon tunnel renders (1391 colors, not black screen) |
# Evidence - Problem class: js-threejs-vendor-file-protocol-classic-scripts - Model: openrouter/deepseek/deepseek-v4-flash-0731 - Solved: 2026-08-30T21:40:02.430Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "NEON RUSH (bankai) QA-BAN-001: vendoring three.js 0.160.0 + jsm addons into a file:// static game. Pitfall: importmap-based local ESM vendoring FAILS on file:// pages \u2014 Chromium blocks ES module scripts from origin null with a CORS error (Access to script at file://... blocked), which is itself a console error failing every test. Fix: vendor as CLASSIC scripts mounted on window.THREE (historical examples/js pattern) in dependency order, convert ESM headers/footers mechanically, node --check each file, load via <script src=./vendor/...> tags; construct EffectComposer etc. synchronously instead of dynamic CDN imports. Also: detect software WebGL via WEBGL_debug_renderer_info UNMASKED_RENDERER_WEBGL regex /swiftshader|llvmpipe|software/i and skip UnrealBloomPass on software renderers (SwiftShader ~4fps). Timing-tolerant Playwright: replace fixed waitForTimeout+distance-assert with waitForFunction(dist threshold, 20s) and 15s waits for landing/slide state transitions. Verified: npm test 4/4 on GPU-less SwiftShader host, judge PASS 53d61a42.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "js-threejs-vendor-file-protocol-classic-scripts", "provider": "openrouter", "solved_at": "2026-08-30T21:40:02.431Z", "version": ""}Done. The problem is diagnosed and the fix is verified end-to-end on this actual GPU-less SwiftShader host. Full solution: ~/neon-rush/SOLUTION.md (project + writeup live in ~/neon-rush).
file:// pages have origin "null". Chromium's module loader is CORS-enforced, so every ESM script — including importmap-mapped local files — is blocked with a console error:
Access to script at 'file://.../three.module.js' from origin 'null' has been blocked by CORS policy...
Reproduced exactly via importmap.html / scripts/repro-importmap.js / tests/pitfall.spec.js — the module never executes, and each block is a console error that fails every test. Classic <script src> tags are exempt from this check, so the fix is to vendor as classic scripts on window.THREE (the historical examples/js pattern, which r148+ no longer ships prebuilt).
scripts/vendor.js, mechanical & idempotent)import { A, B as C } from 'three' → const { A, B: C } = THREE inside an IIFE (each converted module; avoids the shared global-lexical Identifier 'Pass' has already been declared trap)export { E, F as G } → THREE.E = E; THREE.G = Fnode --check each generated file — a parse error can never reach the browserindex.html loads ./vendor/* in dependency order (shaders → Pass → passes → composer → bloom → game), game last, no async/defernew THREE.EffectComposer / RenderPass / UnrealBloomPass / OutputPass at startup — no dynamic CDN importsgetExtension('WEBGL_debug_renderer_info').UNMASKED_RENDERER_WEBGL matched against /swiftshader|llvmpipe|software/i → bloom skipped (bloomEnabled:false on this SwiftShader host, OutputPass keeps tone mapping); probed real renderer string confirms the matchwaitForFunction for landing→running (15s), distance >= 60 (20s), slide→running (15s each way); distance integrated from real dt so results hold at ~5fps| run | result |
|---|---|
npm test (regenerates vendor via pretest) |
4 passed (11.6s), zero console errors on file:// |
npx playwright test --repeat-each=2 |
8 passed (23.2s), no flakes |
npm run test:pitfall |
1 passed — documents the CORS block |
| In-game state dump | {state: "running", distance: 67, software: true, bloomEnabled: false, frames: 216}, console errors: [] |
| Screenshot pixel analysis | live neon tunnel renders (1391 colors, not black screen) |
# Evidence - Problem class: js-threejs-vendor-file-protocol-classic-scripts - Model: openrouter/deepseek/deepseek-v4-flash-0731 - Solved: 2026-08-30T21:40:02.430Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "NEON RUSH (bankai) QA-BAN-001: vendoring three.js 0.160.0 + jsm addons into a file:// static game. Pitfall: importmap-based local ESM vendoring FAILS on file:// pages \u2014 Chromium blocks ES module scripts from origin null with a CORS error (Access to script at file://... blocked), which is itself a console error failing every test. Fix: vendor as CLASSIC scripts mounted on window.THREE (historical examples/js pattern) in dependency order, convert ESM headers/footers mechanically, node --check each file, load via <script src=./vendor/...> tags; construct EffectComposer etc. synchronously instead of dynamic CDN imports. Also: detect software WebGL via WEBGL_debug_renderer_info UNMASKED_RENDERER_WEBGL regex /swiftshader|llvmpipe|software/i and skip UnrealBloomPass on software renderers (SwiftShader ~4fps). Timing-tolerant Playwright: replace fixed waitForTimeout+distance-assert with waitForFunction(dist threshold, 20s) and 15s waits for landing/slide state transitions. Verified: npm test 4/4 on GPU-less SwiftShader host, judge PASS 53d61a42.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4-flash-0731", "problem_class": "js-threejs-vendor-file-protocol-classic-scripts", "provider": "openrouter", "solved_at": "2026-08-30T21:40:02.431Z", "version": ""}