How to reduce bundle size with Webpack?

I’m finally learning how to use three with Webpack,

So far everything is fine, but I expected my bundle to only include the parts of three that I needed (tree-shaking ?). But the little test I’ve done only imports THREE.Scene and makes a bundle of 3 275kb… I’m sure I’m doing it wrong somehow, can somebody help please ?

This is the entry file of my bundle (app.js) :

expand
import { Scene } from 'three';

const scene = new Scene();

console.log('hello from the bundle: ', scene)

And this is webpack.config.js :

expand
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

let pages = [ 'index', 'secondPage' ];

pagesConf = pages.map( (name)=> {
	return new HtmlWebpackPlugin({
		title: name,
		filename: name + '.html',
		template: path.resolve(__dirname, `examples/${ name }.html`),
		inject: false
	});
});

module.exports = {

	mode: "development",

	entry: './src/app.js',

	plugins: pagesConf,

	devtool: 'eval-source-map',

	devServer: {
		contentBase: './dist'
	},

	output: {
		filename: 'bundle.js',
		path: path.resolve(__dirname, 'dist')
	}

}

As far as I understand, three-shaking will not work until three.js is written using ES6 classes.

BoxBufferGeometry and BoxGeometry have been converted to classes already. You could test whether these are in your final bundle or not to see if tree shaking is working. I don’t use webpack so I’m not sure if your setup is OK or not.

a bundle of 3 275kb

Maybe the source maps are included?

2 Likes

You can easily add minification to a rollup project setup by using terser. Check out this starter three.js project for more details.

The final build size is of the bundle is 604 KB.

2 Likes

Yes they are included and it’s not minified, so yes that’s the biggest possible outcome, now I see that I can reduce a lot with minification.

Thank you both for your answers :blush:

i do it manually, i have been getting it down to 80kb and less that way.

  1. create a new index, use only what you need, you can go as far as stubbing or mocking objects that three thinks it’s depending on (look around, there are multiple classes that i return as an empty object and if that’s not enough i’ll stub its methods): https://github.com/drcmda/testlighting/blob/faed149a35d44d116b5c30434f26fb3f98b0d07b/src/utils/three.js

  2. alias “three” to this file: https://github.com/drcmda/testlighting/blob/faed149a35d44d116b5c30434f26fb3f98b0d07b/config-overrides.js

it’s not much work and gets you quite some savings.

ultimatively i hope bundle size is something that will become more of a focus. i would normally consider anything over 50kb unacceptable.

3 Likes

Hey that’s not a bad idea!

I use rollup for bundling my sources, but the set-up is pretty similar. I suppose it reduces bundle time by a crazy amount too (currently sitting at 4~7 seconds each time my code "compiles’).

does it work? from what i have heard nothing tree shakes threejs atm. but there have been tons of changes to rollup since i tried last time. what is the smallest you could get for, say, a box-geom, a light and a meshStandardMaterial?

I’d have to try out.

Didn’t attempt this yet, hence the “Hey that’s not a bad idea!” :smiley:

Me too. Especially if it really is as simple as converting to classes. However, I (and many others) have been bringing this up over on the repo for several years now and there’s simply no interest in prioritizing this.

i do it manually, i have been getting it down to 80kb and less that way.

I do the same, but got bored around 150kb. Thanks for sharing, gonna steal your file and see if I can reach your level :grin:

That seems pretty high. I use rollup as well and consider anything over ~2s too long for dev. Not a scientific number, but below that, I feel like the compile is instant. Currently, my compile time is around 1.1s.
Can you split the rollup config into dev and production? Then remove whatever you can from dev - for example, babel and terser.

It’s not that there isn’t interest, but that there is a long process of steps that need to happen in order to get there… We can’t use ES classes in src/ without migrating examples/ code first, and we can’t (or have decided not to) migrate examples/js because it’s in the middle of a deprecation. All three of those are large codebase-wide changes.

hmm, i don’t think it’s so easy. webglrenderer looks pretty wired and shaderchunk is pulls in everything. the snapshot below is what you get when you just fetch box+pointlight+standardmaterial (95kb). there is still a TON of stuff in it that’s excess, probably more than half.

would be a bummer if that’s so, with a leaner profile three would reach a much wider audience. it’s not feasible today if you plan to support slow networks.

1 Like

I wasn’t intending to dismiss that. However, we have made very little progress on most of these steps. We converted the examples to modules, which finished last year some time, and then just stopped. This should be given higher priority.

This is a complex issue, no doubt, but it’s something that we could solve in a matter of months, not years.

We can’t use ES classes in src/ without migrating examples/ code first,

That’s not true for all of src/. We converted BoxBufferGeometry and BoxGeometry to classes about 6 months ago so it’s clearly possible. Everything that is not extended in examples could be converted right now.

Yeah, I was discounting the renderer code. But at least the rest of the source should be easy and would drop a few hundred kb from the bundle.

i think classes shouldn’t have any effect on /jsm, it seems curious nonetheless. but as for /js, can’t they simply be deleted? these files have no meaning, they can’t be imported or readily be used.

Some people do still use the old <script> tag style. Hard to believe I know :grin:

In any case, as @donmccurdy says, examples/js is going to be deprecated. I’m not sure if there’s a set timeframe for that, but hopefully, it’s soon. Then we will have no excuses for failing to progress with this issue :sweat_smile:

I guess that a big part of the reason for delaying here is that we will have complaints when we finally deprecate examples/js. Some people don’t want to use ES6 modules. That will probably be the case no matter how long we wait though.

Another consideration, more important, is that many old examples around the web will stop working since they reference the files in examples/js directly from the GitHub CDN. For example, I think that all of westlangley’s examples on GitHub do this.

This sounds like a valid reason for a “Three 2.0” imho.

Semantic versioning is something most people understand. Going from 1.x to 2.x is bound to break things for people that use the library as a dependency.

Then again, I’m sure a LOT of people will have complaints about this way too :joy:

4 Likes

You must admit that the usage of JavaScript is not getting simpler, and most of this complexification is favored by bundling.

I remind you that a lot of people who learn Three.js do it without prior experience in web development. How many questions is there in this forum asking for help just running three examples locally ? How does a “hello three.js” tutorial looks like with bundling ?

If it’s just for saving a few http requests, bundling sounds more like micro optimization, so forcing people to learn this before learning three.js is not ideal in my opinion.

Not denying the usefulness of the thing, but you must consider everybody’s need. Personally I’ve learned coding with three.js, and I’m glad bundling was not mendatory. I’ve made quite a big game that everybody tell me works well, with script tags, . Now that I learned to use three efficiently enough for my purpose, I’m learning bundling for micro-optilization. I think this is the logical order.

2 Likes

but how many, and is it more important than the billions of actual users that get served pages that aren’t accessible on their networks? devs can still use examples/js since it’s always part of GH history, or they install node and move on. users on the other hand don’t have that choice, if their network is slow they’re waiting a minute for a website to come up.

what i meant though was that these files are more like a reading/learning resource. in order to use them you go in, copy stuff by hand until something starts working, or you’re dumping the entire folder onto your server. nothing “breaks” if they are removed/deprecated on GH.

1 Like

Compared to the days when we had to use jQuery because no two browsers agreed on syntax? And before all the user-friendly ES6 stuff like classes, arrow functions, const, let, etc. was added?

I think using JS has become much simpler, actually. At the same time, I think that the things we are building on the web have become much more complicated.

I’m glad bundling was not mendatory

Bundling will never be mandatory. You don’t need to use a bundler to use modules. Look at any three.js example. All the code uses modules and there’s no bundler:

I’ve made quite a big game that everybody tell me works well, with script tags,

Your game is great. But I bet if you decided to refactor it to use <script type="module"> you’d be surprised how little work is involved. There’s not even a performance penalty anymore as long as you serve your files with HTTP/2. In some cases leaving your code split into small files might even result in faster download times.

Once you’ve done that, then you can worry about adding a bundler. But it’s not something you have to do upfront before you can use modules. New users don’t have to learn how to use a bundler before they can use three.js.

Yes, exactly. Whenever we make this switch, we can just add a link in the readme to the final three.js release (on GH and NPM) that included these files and be done with it. Whenever anyone says they still need to use the old files we can point them there.

2 Likes

This is really interesting and curious. Where is this data coming from? Do you have an idea how many people are we talking about and what’s their reasoning? I can’t see any professional work being done efficiently today by not using modern standards.

1 Like

I think that this is overall a bad approach. I’m not sure if three does this, but most books you pick up have some kind of a prerequisite they tell you you should know. OpenGL and CUDA are very complicated. Still, if there were something analogous to threejs, (maybe ogre?) I would expect problems if I tried to learn this without knowing anything about C++.

Actually, this is the very reason I don’t do OpenGL - on top of not knowing how pointers work for example, I’ve no idea how to link, compile and ultimately run my application. I think this is very similar to not knowing how web pages / js work.

Why not simply try to learn this first if it’s a prerequisite?

Could you elaborate a bit more on this. Is the core dependent on stuff that can be found in examples?