FBX Loader: Out of Memory when importing Mixamo animation (v9.31.3 / v9.32.0)

I encountered an out-of-memory issue when importing an animated Mixamo FBX file using the built-in FBX loader in Babylon.js.

I initially reproduced the issue with Babylon.js 9.31.3, and I have now confirmed that it also occurs with Babylon.js 9.32.0.

I asked ChatGPT to help investigate the problem, as I don’t fully understand the internal implementation of the FBX loader.

ChatGPT suggested several diagnostic tests and workarounds, which I tested myself in Babylon.js Playground.

Minimal reproduction

Playground (Out of Memory):

Playground (Workaround applied):

FBX file:

https://cx20.github.io/fbx-test/assets/models/fbx/mixamo.fbx

The minimal reproduction simply loads the FBX file using BABYLON.SceneLoader.ImportMeshAsync() and attempts to play its animation.

Chrome runs out of memory before the import completes, and the renderer process crashes.

I confirmed that this issue occurs with both Babylon.js 9.31.3 and 9.32.0.

There is no custom animation retargeting, frame baking, or other complex processing involved in the minimal example.

For reference, my original, more complex Dude × Mixamo Playground is:

However, the official FBX loader samples (#DZBTQU#0 and #DZBTQU#11) work correctly.

Test results

Based on ChatGPT’s suggestions, I tested the following with Babylon.js 9.31.3:

Test Result
Normal FBX import Out of Memory
Disable animation group creation Import succeeds, no animation
Skip inherited bone animations Import succeeds, but animation is incorrect
Disable _refineSampleTimes() Import succeeds, animation works correctly

The following workaround, suggested by ChatGPT, resolves the issue:

BABYLON.SceneLoader.OnPluginActivatedObservable.addOnce(
    (plugin) => {
        if (plugin.name === "fbx") {
            plugin.constructor._refineSampleTimes =
                (times) => times;

            console.log("FBX workaround applied");
        }
    }
);

This code must be registered before calling BABYLON.SceneLoader.ImportMeshAsync().

Workaround verification (Babylon.js 9.31.3)

With this workaround, the FBX file loads successfully.

Babylon.js v9.31.3
Starting FBX import
FBX loader activated
TEST: no-refine only

IMPORT SUCCESS {
    meshes: 2,
    skeletons: 1,
    animationGroups: 1
}

Animation: {
    name: "mixamo.com",
    from: 0,
    to: 505,
    channels: 49
}

I also confirmed that the dance animation plays correctly and that my original Dude × Mixamo Playground works with this workaround.

Workaround verification (Babylon.js 9.32.0)

I subsequently tested the same workaround with Babylon.js 9.32.0.

Workaround Playground:

The console output was:

Babylon.js v9.32.0 - WebGL2
Starting FBX import
FBX workaround applied

IMPORT SUCCESS {
    meshes: 2,
    skeletons: 1,
    animationGroups: 1
}

The FBX import completed successfully without an Out of Memory error.

Therefore, I have confirmed that disabling _refineSampleTimes() avoids the crash in both Babylon.js 9.31.3 and 9.32.0.

The dance animation was visually verified with the workaround in v9.31.3. For v9.32.0, I have confirmed successful import and creation of the animation group.

Possible cause (suggested by ChatGPT)

ChatGPT examined the Babylon.js FBX loader source code and pointed out that _buildInheritedRigBoneAnimations() repeatedly calls _refineSampleTimes() while processing multiple bones.

It suggested that the sample-time array might grow excessively because each bone refines the sample times accumulated from previous bones.

This could potentially lead to an unexpectedly large number of samples and excessive memory consumption.

However, this is ChatGPT’s hypothesis, not something I have independently verified.

I have confirmed the crash, the diagnostic results, and the workaround through actual testing, but I have not measured the internal sample counts or memory allocations.

The workaround modifies a private method, so it is intended only as a temporary solution for investigation.

Environment

  • Babylon.js versions: 9.31.3 and 9.32.0
  • Browser: Google Chrome
  • Rendering engine: WebGL2
  • Error: Out of Memory (renderer process crash)
  • FBX file size: Approximately 3 MB

Summary

The same FBX file causes an Out of Memory crash with the standard Babylon.js FBX loader, but loads successfully when _refineSampleTimes() is disabled.

This behavior is reproducible in a minimal Playground without any custom retargeting code.

Could someone familiar with the FBX loader implementation check whether repeated sample-time refinement in _buildInheritedRigBoneAnimations() might be causing the issue?

Thank you!

Interesting find, especially since disabling _refineSampleTimes() seems to avoid the crash in both versions.

I wonder if logging the sample count before and after each refinement would help narrow this down. If the array keeps growing as more bones are processed, that might explain why a relatively small FBX file ends up consuming so much memory.

Maybe a useful comparison would be the sample counts from the working official FBX examples versus the Mixamo file.

I’m not familiar enough with the loader internals to know if that’s the actual cause, but the workaround does seem to point toward that part of the code.

Thanks for putting together such a detailed reproduction!

Thanks for the detailed reproduction and for narrowing this down!

I reproduced the excessive sample growth with your mixamo.fbx and opened a fix here:

BabylonJS/Babylon.js#19050

Your hypothesis was correct: when baking inherited bone animations, each bone was refining the sample times already refined by the previous bones. The subdivision-depth limit restarted for each bone, so the sample list could keep growing across the rig.

The fix makes every bone refine the same original sample intervals, then merges the results. This keeps adaptive refinement enabled while preventing the cumulative growth. It also normalizes the quaternions used for the angular-error check, so small quaternion length errors do not trigger unnecessary subdivision.

I tested your file with the patched loader against Babylon.js 9.32.0 in the browser, without the workaround. It imports successfully with 2 meshes, 1 skeleton, and 1 animation group containing 49 channels, and the dance animation plays. Regression tests have also been added.

The PR is not merged yet. Once it is merged and the updated loader reaches the Playground build, the private-method workaround should no longer be necessary.