We are preparing a big tree-shaking improvement for Babylon.js, planned for the beginning of next week.
The short version: Babylon.js will add a new pure barrel import path for @babylonjs/core. This lets apps import from a convenient single entry point while still allowing bundlers to remove unused Babylon.js code more effectively.
The main idea is to import everything from a single pure barrel:
import { Engine, Scene, Vector3 } from "@babylonjs/core/pure";
Some features that used to be enabled automatically by imports will need explicit registration when using the pure path. The docs explain the new pattern, when registration is needed, and how to troubleshoot missing registrations.
I’m really looking forward to this release!
I believe this will not only benefit standard use cases but also improve worker thread startup times and reduce memory consumption for OffscreenCanvas.
Just to confirm, will this be properly tree-shaken even with an import style like the following?
import * as BABYLON from "@babylonjs/core/pure";
Also, are there any plans to move toward a more tree-shaking-friendly implementation in the future (even if it potentially breaks backward compatibility)?
For example, cameras are currently created with several InputsManager instances registered by default. Consequently, even if we call inputs.clear() at runtime and don’t use the managers at all, they likely won’t be tree-shaken. It would be even better if the implementation became fully tree-shakable in that regard.
Regardless, I have high expectations for this improvement.
Thank you for this great framework!
wouldn’t work as expected, as long as you register the correct side effects. I personally would not do that because i don’t like namespacing like that, but this is just my personal opinion.
notice that the pure barrel is NOT a magical solution. We are still maintaining backwards-compatibility, so we can’t make major changes to the code structure (which is, sadly, a bit too “coupled” in some areas).
The benefit of the pure barrel is to be able to tree-shake while importing from a single import path.
I have one more question/request: when only using WebGPU engine (and not using materials or other features that require WGSL-unsupported shaders), will the GLSL (or GLSL-related) code in Babylon.js be tree-shaken?
No. We haven’t changed the architecture for shader loading. But we are working on a different solution that you will probably be happy with. More in the coming weeks.
Update - the changes were just merged. 9.8.0 was also released with the new “pure” barrel. Documentation is being built right now with the latest changes.
Asking everyone with issues - please write me if anything is not working! Thank you so much!
I wanted to say a special thank you for this change. Now, the transition from babylonjs-* to @babylonjs/*finally makes sense and it actually saves on final bundle size (around 0.9MB in my case). Before the 9.8.0, the mentioned transition was blowing my output bundle with about 1.5MB extra, that’s why I wasn’t rushing to switch.
In case anyone is interested, here’s my vite config for: “@babylonjs/core”: “^9.8.0”, “@babylonjs/havok”: “1.3.12”, “typescript”: “6.0.3”, “vite”: “8.0.13”
//import { visualizer } from "rollup-plugin-visualizer";
import { defineConfig } from 'vite';
import { fileURLToPath, URL } from 'node:url';
export default defineConfig({
/*plugins: [
visualizer({
open: true,
filename: "bundle-analise.html",
gzipSize: true,
brotliSize: true
})
],*/
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
build: {
chunkSizeWarningLimit: 4000,
target: 'esnext',
modulePreload: false,
rollupOptions: {
output: {
// @ts-ignore - Os tipos do Vite ainda estão em transição para o Rolldown
codeSplitting: false,
entryFileNames: 'assets/js/bundle-[hash].js',
assetFileNames: (assetInfo: { name?: string }) => {
if (assetInfo.name?.endsWith('.wasm')) {
return 'assets/wasm/[name][extname]'; // no hash — consistent path for HavokPhysics.wasm
}
return 'assets/[name]-[hash][extname]';
}
},
},
},
optimizeDeps: {
exclude: ['@babylonjs/havok']
}
});
I am currently attempting a migration in my project.
First, I am calling RegisterFullEngineExtensions and RegisterEnginesExtensionsEngineRawTexture. Additionally, because doing only that outputs engine.rawtexture needs to be imported before as it contains a side-effect required by your code to the console, I have also imported @babylonjs/core/Engines/WebGPU/Extensions/engine.rawTexture (is this the correct approach?).
However, even after doing that, I still get a TypeError: Cannot read properties of undefined (reading 'texture') when the application starts.
Additionally, I tried calling functions like RegisterTexture and RegisterCubeTexture, but it made no difference.
Finally, regarding RegisterPBRMaterial, the function itself seems to be undefined. A warning is displayed during the build (vite): Warning: Import 'RegisterPBRMaterial' will always be undefined because there is no matching export in '../../node_modules/.pnpm/@babylonjs+core@9.10.1/node_modules/@babylonjs…, and it is indeed undefined at runtime as well.
Am I doing something wrong? I’ve read the documentation and believe I’ve followed all the necessary steps.
Let’s see what happened here. Is is possible to share the code with me? or some reproduction? this way I will know we both have the same context.
There is an interesting tradeoff with the pure implementation and types - typescript doesn’t support optional typing. the d.ts files are either there, or not. The issue with typing here is - the Register function doesn’t register the types. For that you need to import the types directly, or load the side-effect wrapper. It’s documented here - Babylon.js docs .
I will also check what happened to the register PBR material. will keep you updated.
Thank you for your reply.
I haven’t committed the pure barrel version yet because the migration hasn’t been successful. Also, the codebase is quite large, so I’m not sure if it will be helpful, but I’ll share the branch just in case:
for the typing, i am pretty sure the issue is importing the types, but I’ll spend some time later today on your branch to see how I can help. Studying and fixing these issues are my top priority - thanks for being an early adopter
For reference, here is the location of the Vite configuration file:
Just to clarify, the TypeError: Cannot read properties of undefined (reading 'texture') is a runtime JavaScript error occurring inside Babylon, so it doesn’t seem to be related to the type definitions.
Note: This project has a dev build (pnpm dev) and a regular build (pnpm build). The dev build throws a different error (e.g., _PostProcessRenderPipelineManagerClass is not a constructor). However, since this might be an issue stemming from the unique build method, for now, our primary goal is to resolve the error occurring in the regular build (Cannot read properties of undefined (reading 'texture')).
RegisterPostProcessRenderPipelineManagerSceneComponent(PostProcessRenderPipelineManager) . I am checking now why there wasn’t any better error message.
For better structure, I moved the runtime initialization to a babylonRuntime.ts file:
import * as BABYLON from '@babylonjs/core/pure';
export function registerBabylonRuntime(): void {
BABYLON.RegisterFullEngineExtensions();
BABYLON.RegisterFullWebGPUEngineExtensions();
BABYLON.RegisterBufferAlign();
BABYLON.RegisterCubeTexture();
BABYLON.RegisterStandardMaterial();
BABYLON.RegisterCollisionCoordinator();
BABYLON.RegisterPostProcessRenderPipelineManagerSceneComponent(
BABYLON.PostProcessRenderPipelineManager,
);
}
Then anywhere that uses “Register” function imports this function and calls it.
This also avoids possible race conditions (usage before initialization).
This also solves the pnpm build issue.
Side note - this is the same “problem” we had with deep imports (i.e. - not importing from the index barrel but directly from the files). It not simple to find the right register functions needed. I will think of a good solution. will also check if the current solutions (dev-mode stub injection) would work here