Skip to content

Update to webpack 5 / Node.js 22 / A-Frame master / three r184#218

Open
vincentfretin wants to merge 29 commits into
supermedium:masterfrom
vincentfretin:update-webpack5
Open

Update to webpack 5 / Node.js 22 / A-Frame master / three r184#218
vincentfretin wants to merge 29 commits into
supermedium:masterfrom
vincentfretin:update-webpack5

Conversation

@vincentfretin
Copy link
Copy Markdown

@vincentfretin vincentfretin commented Dec 29, 2025

I didn't manage to run that project on an old version of Node.js with nvm,
so I updated webpack from version 4 to 5 so we can run the project on latest Node.js LTS 22.
I removed all babel transforms, none were used actually, also removed the unused zip-loader dependency.
webpack 5 doesn't include nodejs polyfills anymore, so it was easier to rewrite the zip worker, Claude Claude Opus 4.5 replaced unzip-js + Node.js polyfills with fflate - a modern, fast, pure JS zip library that works natively in browsers without polyfills. It's also smaller and faster.

Things that were fixed are the star, controllers reconnection and haptics (with the update to aframe master), the hover on the keyboard letters, the song cover, better perf running at 90fps (because latest aframe contains the pool component optimization, that was in Dec 24 2022 included in 1.4.0 aframe release), remove the subscribe to newsletter that doesn't exist anymore.

Nice to have before merging:

Comment thread src/workers/zip.js
const difficulty = difficultyBeatmap._difficulty;
const beatmapFilename = difficultyBeatmap._beatmapFilename;
if (beatFiles[beatmapFilename] === undefined) {
continue;
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked about that particular change Claude did.

Original behavior: If beatFiles[beatmapFilename] was undefined, it would return from the callback and never call postMessage. This meant silent failure - the worker would just hang and never respond.

New behavior: With continue, we skip missing beatmaps but always call postMessage. If data.beats ends up empty, zip-loader properly emits songloaderror.

So the new code has better error handling - it fails gracefully with an error message instead of silently hanging.

Windswipe added a commit to Windswipe/Moon-Rider-REBORN that referenced this pull request Apr 17, 2026
vincentfretin and others added 26 commits April 19, 2026 11:15
beat-generator.onClearGame iterates #beatContainer.children, which per
scene.html is the container attribute for every beat and plume pool —
i.e. it holds all pooled entities, in-use and already-returned.
Calling returnToPool on the already-returned ones produced "returned
entity was not previously pooled" warnings from A-Frame's pool (30+
per pool on every game clear, matching each pool's initial size).

Move the "am I currently in use?" check into returnToPool itself so
the component owns that question and any caller can safely call it
more than once. Drop the position.set and visible=false fixups along
the way: a returned entity is paused and invisible, and its position
is set fresh the next time it's handed back out.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Forward-fix for A-Frame 1.6.0+: since the component update rework in
aframevr/aframe#5474, Component.destroy() actively nulls this.data.
play-sound's handler reads this.data.enabled on every event, so a
lingering listener after destroy fires with this.data === undefined
and throws "Cannot read properties of undefined (reading 'enabled')".

Store the bound handler on the component and detach it in a new
remove hook.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dispose is idempotent on three.js BufferGeometry, but nulling out
this.geometry afterwards makes the `if (this.geometry)` check actually
mean "there's a live geometry to dispose" and releases the stale
reference so GC can collect the disposed instance.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A-Frame 1.6.0 (commit 4a89bb6e) defers component/system init until
document.readyState === 'complete', so the envmap template image is
already fully loaded by the time this system runs. The
addEventListener('load', ...) callback was attaching after the load
event had fired and never executed, leaving envmapImg.src empty and
wall shaders sampling a blank texture — wall interiors rendered black
instead of reflecting the cyan/pink gradient.

Drop the load listener and the DOMContentLoaded guard around
createMaterials() while here — both are dead code under the new
timing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Non-normal material blending modes only take effect in three.js's
transparent render pass, so the additive bloom around the merkaba
star at the horizon was silently dropped and the planes rendered
opaquely, exposing the underlying pink merkaba as a small pink blob
instead of the bright white glow. Adding transparent: true puts them
back in the transparent pass where their additive blending and
depthTest: false are actually honored.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A-Frame 1.5.0 (commit c8e8241b, "Redesign enter session UI") renamed
the component from vr-mode-ui to xr-mode-ui. Without the rename no
component is registered, so the querySelector + click-listener code
that wires up our custom #vrButton never runs and the "Click Here to
Enter VR" button is dead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A-Frame 1.7 renamed oculus-touch-controls to meta-touch-controls,
removed the orientationOffset schema property (no longer honored), and
switched tracked-controls to hardcoded gripSpace. A-Frame also
dropped daydream-controls and gearvr-controls some releases ago.

- controller.js: setAttribute 'meta-touch-controls' instead of
  'oculus-touch-controls'; drop the no-op orientationOffset; drop
  daydream-controls registration and cursor config.
- debug-controller.js, super-keyboard.js, state/index.js: reflect the
  same rename; drop daydream-controls/gearvr-controls entries (the
  has3DOFVR list collapses to a single value).
- scene.html: the old per-controller blade rotation
  (controllerType.indexOf('oculus') !== -1 ? '135 0 0' : '90 0 0')
  was compensating for targetRaySpace pose. With gripSpace as the new
  default, '90 0 0' is correct for every controller — verified in VR.
  Drop the conditional.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
We load A-Frame from a CDN <script> tag in index.html; the local
vendor/aframe-master{,.min}.js{,.map} copies haven't been referenced
since that switch and just added noise to the repo.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
A-Frame already attaches THREE.BufferGeometryUtils (see
aframe/src/lib/three.js), so the r103-era trimmed copy in
vendor/BufferGeometryUtils.js is redundant. It also broke under
webpack 5 because it referenced THREE as an implicit global.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Audited vendor/Curve.js and vendor/CatmullRomCurve3.js against modern
three.js: the CatmullRomCurve3 fixes (intPoint modulo, this.points
reset in fromJSON) are upstream already, and the Curve.js
optional-target GC patches for getPoints/getSpacedPoints/getLengths
are never exercised on a hot path in supercurve.js — the per-frame
getPointAt/getTangentAt calls already go through the upstream
optional-target overloads on getPoint/getTangent.

Use upstream THREE.Curve / THREE.CatmullRomCurve3 directly via the
THREE global A-Frame exposes and drop the two require() lines at the
top of supercurve.js.

Add `externals: { three: 'THREE' }` to webpack.config.js so the one
`import ... from 'three'` in src/lib/FontLoader.js resolves to
window.THREE instead of bundling a second copy of three.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
THREE.Math was removed from three.js in r125 — the helpers live on
THREE.MathUtils now.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- panel-shader.js, supercurve-shader.js: A-Frame aframevr/aframe#5310
  removed updateVariables and the attributes array from the Shader
  base class. Delegate the uniform-update work to the inherited base
  update via the prototype chain so our registerPanel/registerCurve
  side effect still runs.
- trail.js: THREE.VertexColors was removed in three.js r125 — the
  material property is now a plain boolean.
- scene.html: set renderer="colorManagement: false" so colors and
  textures keep the pre-1.3.0 behavior our custom GLSL was authored
  against. Without this A-Frame enables THREE.ColorManagement and
  switches outputColorSpace to sRGB, which throws off every shader
  that mixes raw color uniforms.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The *BufferGeometry aliases were removed from three.js:
- tail.js: THREE.PlaneBufferGeometry → THREE.PlaneGeometry
- wall.js: THREE.BoxBufferGeometry  → THREE.BoxGeometry

aframe-slice9-component still references THREE.PlaneBufferGeometry
internally, so alias it back to THREE.PlaneGeometry in src/index.js
before the require.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
beatsaver now serves cover images with permissive CORS headers, so
the beatproxy rewrite is dead weight. Use the raw coverURL from
versions[0] directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The supermedium-sync in 8456119 dropped two pieces that are required
for keyboards that ship a hover sprite (our `superkeyboard` model
does — `keyboard-hover.png`):

1. Setting `src: hoverImg` on the color plane material (normal
   blending, sampled texture) instead of the additive-solid-color
   glow.
2. UV manipulation so the per-key plane samples only that key's
   region of the sprite. Since modern three.js replaced
   `geometry.faceVertexUvs` with BufferGeometry, switch to
   `texture.offset` + `texture.repeat` which gives the same result.

Without this, the hover plane was drawn as a flat white (or
`keyHoverColor`) rectangle on top of each key — an opaque white
square with the letter erased behind it.

The additive-glow path is preserved for keyboards without a hover
sprite, and setAttribute declares `blending: 'additive'` directly
instead of relying on a componentinitialized listener (which now
fires synchronously and was being attached too late).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vincentfretin and others added 2 commits April 19, 2026 11:25
- package.json: vendor/ was removed from the repo, so drop it from
  the predeploy cp list.
- webpack.config.js: set publicPath to 'auto'. On moonrider.xyz it
  still resolves to /build/ (same as the old '/build'), and it also
  lets the build work when served from a subpath (e.g. a GitHub
  Pages fork at /moonrider/).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Drop the "Get Updates on Our Upcoming VR Projects!" email capture
form — the endpoint (supermedium.com/mail/subscribe) and associated
tracking no longer make sense here. Keep a small Discord link in the
bottom-left corner so the community pointer is still visible.

Removes: the #subscribeForm block in index.html, the associated CSS
(#subscribeForm, inputs, buttons), and initSubscribeForm() plus its
DOMContentLoaded listener in src/index.js.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vincentfretin vincentfretin changed the title Update to webpack 5 to work on latest Node.js 22 Update to webpack 5 / Node.js 22 / A-Frame master / three r184 Apr 19, 2026
The absolute /assets/img/banner.jpg path only resolves correctly
when the site is served from the root of a domain. A relative path
works regardless of the base path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant