Fix HTML5 rendering differences in us vs Neopets

I pulled the source map for the Neopets renderer, and had Claude compare it to ours. It noticed the key issue responsible for a high number of unsolved rendering issues: we weren't setting up the `MotionGuidePlugin`, which I've never heard of before. Whoops!

In addition to this, we made some other minor fixes for consistency:
- Use whatever Stage object the library exports (will sometimes be StageGL)
- Resize the stage rather than the clip (shouldn't matter?)
- Send a callback to the library when done (I'm not aware of any anims that use this but some may!)

The specific item I was debugging was "Food Fight Shower", and it works now! But I also know we've had a solid handful of similar inexplicable wild rendering bugs, which I imagine this solves as well.

We might want to consider auditing our Known Glitches on SWF assets to see how many of them can be removed, now that this is resolved.
This commit is contained in:
Emi Matchu 2026-01-19 10:47:57 -08:00
parent 6aff39dfed
commit dd2f6be79f
2 changed files with 15 additions and 6 deletions

View file

@ -150,13 +150,13 @@ function updateStage() {
function updateCanvasDimensions() { function updateCanvasDimensions() {
// Set the canvas's internal dimensions to be higher, if the device has high // Set the canvas's internal dimensions to be higher, if the device has high
// DPI. Scale the movie clip to match, too. // DPI. Scale the stage to match, too.
const internalWidth = canvas.offsetWidth * window.devicePixelRatio; const internalWidth = canvas.offsetWidth * window.devicePixelRatio;
const internalHeight = canvas.offsetHeight * window.devicePixelRatio; const internalHeight = canvas.offsetHeight * window.devicePixelRatio;
canvas.width = internalWidth; canvas.width = internalWidth;
canvas.height = internalHeight; canvas.height = internalHeight;
movieClip.scaleX = internalWidth / library.properties.width; stage.scaleX = internalWidth / library.properties.width;
movieClip.scaleY = internalHeight / library.properties.height; stage.scaleY = internalHeight / library.properties.height;
} }
window.addEventListener("resize", () => { window.addEventListener("resize", () => {
@ -176,23 +176,28 @@ window.addEventListener("resize", () => {
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
async function startMovie() { async function startMovie() {
// Install the MotionGuidePlugin, which is needed for motion path animations.
createjs.MotionGuidePlugin.install();
// Load the movie's library (from the JS file already run), and use it to // Load the movie's library (from the JS file already run), and use it to
// build a movie clip. // build a movie clip.
library = await getLibrary(); library = await getLibrary();
movieClip = buildMovieClip(library); movieClip = buildMovieClip(library);
updateCanvasDimensions();
if (canvas.getContext("2d") == null) { if (canvas.getContext("2d") == null) {
console.warn(`Out of memory, can't use canvas for ${libraryUrl}.`); console.warn(`Out of memory, can't use canvas for ${libraryUrl}.`);
// TODO: "Too many animations!" // TODO: "Too many animations!"
return; return;
} }
stage = new window.createjs.Stage(canvas); stage = new library.Stage(canvas);
stage.addChild(movieClip); stage.addChild(movieClip);
updateCanvasDimensions();
updateStage(); updateStage();
// Signal to the library that the composition is ready.
AdobeAn.compositionLoaded(library.properties.id);
loadingStatus = "loaded"; loadingStatus = "loaded";
canvas.setAttribute("data-status", "loaded"); canvas.setAttribute("data-status", "loaded");

View file

@ -390,6 +390,10 @@ export function loadMovieLibrary(librarySrc, { preferArchive = false } = {}) {
); );
} }
delete window.AdobeAn.compositions[compositionId]; delete window.AdobeAn.compositions[compositionId];
// Install the MotionGuidePlugin, which is needed for motion path animations.
window.createjs.MotionGuidePlugin.install();
const library = composition.getLibrary(); const library = composition.getLibrary();
// One more loading step as part of loading this library is loading the // One more loading step as part of loading this library is loading the