Three.js, semver, and addons

Starting a new topic to continue branching discussion from The examples/js directory will be removed with r148. Pulling a few quotes from the earlier thread:

1 Like

This would be nice but if this were to happen I’d think there should be a distinction between “addons” that are versioned and “example” files which are wild west…

I worry this division would be mixing “purpose of code” with “code quality” in a way that it shouldn’t. For example, we have 30+ loaders. Some of them are carefully maintained; others have no active maintainer. It’s going to be an uphill battle explaining to new users why one loader is an “addon” and others are “examples”, and the three.js-specific meanings of those terms that we’ve invented…

It’s much easier just to indicate maturity of the code, with existing concepts like “experimental”, “alpha”, “beta”, or “stable”. If it isn’t “stable” then it doesn’t get any semver guarantees. If it doesn’t have a maintainer then it probably isn’t stable. It would be nice to have that visibility into addon status.

I’m also not really eager to turn addons into separately-versioned packages. If a specific addon would benefit from this, sure, it should just be spun into a new repository (and npm package) by its maintainer. No need to put that package management complexity on the three.js project itself.


I agree, the thought of converting add-ons into separately-versioned packages sounds like a really bad idea. It’s the difference between having to wonder

  • “Why is GLTFLoader 2.4.3 not working with THREE r152?”
  • “Which version of GLTFLoader is compatible with THREE r152?”

versus just knowing that

  • GLTFLoader r152 will be compatible with THREE r152

I’d imagine that switching to independent semver addons would change the focus of the Three.js contributors from technical innovation to mostly project-management :smile:



@makc3d are you thinking of addons being one big npm package, separate from three? Or one package per export?

I don’t know if I see how a single large addons package really improves anything, it might as well remain part of three unless there’s some advantage to that? I’m not opposed to this, I just don’t think it changes the story in terms of semantic versioning.

And if we have one npm package per export, I don’t think it’s as simple as described above. Someone has to keep each package pinned to a compatible range of three versions. Some addons packages will depend on other addons packages. Sometimes mismatched versions not in peer dependencies will cause build tools to bundle a package twice, which can have weird downstream effects (like broken instanceof checks). Working with CDNs will be harder too, as you’ll have to figure out the right versions on your own.

1 Like

i think the main advantage is to free threejs from the burden of having to maintain core, and then 500 extra packages. it could mean that more maintainers and contributors could help out and care for these packages, which often collect dust as changes can take years to come through, or bugs aren’t fixed because of backward compat.

i would otherwise suggest to semver the whole thing, target a threejs version and if anything breaks or relies on a newer three, bump major. or, a monorepo, where each entry has its own package.json - although that’s harder to set up, it could have benefits, especially for documentation and examples. tools like codesandbox could run examples just by linking the directory towards it.

but in the end, this will never happen anyway, right? the maintainers have often expressed that they are against splitting core from examples.

1 Like

I’m mostly worried it’d do the opposite… We have a group of people maintaining *waves hands at all of this*, and the project has been structured in a way that supports this group’s work. Refactoring within a monorepo is easier than refactoring across multiple repos. I don’t imagine new contributors will come knocking as a result of any of the changes we’re discussing. So in practice I think it means a much smaller number of examples can be maintained, compared to what is (mostly successfully) maintained today.

But, I don’t think splitting core from examples is that necessary for semver? There are always going to be some examples where we care more about backward compat (OrbitControls, GLTFLoader) and others we don’t, or don’t yet (NodeMaterial, WebGPURenderer, …). It isn’t really a core vs. examples split.

1 Like

I think taking a few steps back would be beneficial for this topic. For example here - why are all of these bunched up together? In addition to maintainers, is it safe to say that OBJLoader or GLTFLoader have comparatively more users than another more esoteric loader? If so, doesn’t this already classify them differently?

On this note, three.js is available on npm, which enforces a semver. Like i said, semver is just an idea, a view, and three.js can and is actually semvered when you use npm, yarn and such package managers. It’s just not done correctly, i think the minor version is always bumped and that this is incorrect, it should be major.

My conclusion is that three has it’s own alias for what is essentially semver, but that it’s wrongfully calling R100 0.100.0 instead of 100.0.0. I don’t know why.

  1. Why would there ever be a 2.4.3 loader, and why would three still not be referred to in semver? If r152 is 0.152.0 then the loader should also probably be at 0.152.X. It would make more sense if it were 152.0.0 though. But thats just my two cents. React is three years younger than three but has like 20 versions, not 150. Still i think there are many react-thing that have their own versioning. Hardly anything ever broke, and deprecation warnings show up meaningful in dev mode like years ahead of things actually breaking. They don’t explode your console when there’s methodA and methodA_alias. So, plenty of ways to know this.

My question would be, why would a GLTF loader even change at all over the years in terms of compatibility with three.js? In the end, all it does is create a bunch of new Mesh( geometry, material) which oddly, never broke with three.js.

The thing is GLTFLoader is not always even changed when it gets a new version. I don’t think that there the number is even relevant. This code is just actively maintained and as such gets changes often and i guess warrants new versions (although, are they all breaking, or just bug fixes). But there are versions where nothing happened. The log doesn’t seem to mention any changes in r144. Why would the version even change for that particular thing, if it didn’t change at all?
You expect what you wrote in the quote, but then take the lego loader (is it l-draw?) or something as esoteric as that, do you:

in this case as well?

The question here is, why do all these loaders have all these versions when some change and some apparently don’t change at all? Well, because they’re all “part” of three.js, but not really.

Bus factor.

Is the core of this conversation stipulating that 3js is moving towards a react / npm environment?

We call this poor soul a package maintainer :sweat_smile: well, all you need to do is just put r123+ as a dependency, and wait for bug reports. Once the “does not work with r156” appears, you edit that to r123-r155 and work on a fix that changes the range back to r156+ (or r138+ whatever the fix allows for)

1 Like

This is more or less what happens today? Except the bug reporting is subtle, it can happen over a few versions.

This is exactly the type of work that nobody would want to take on. You’re essentially asking the threejs contributors to become version managers on top of everything else. As Don said: “the project has been structured in a way that supports this group’s work”

I still don’t see how breaking up into separate packages benefits anybody. It seems like more tedious work for maintainers, and more version-hunting for users. I understand it’s not that hard but that doesn’t mean it’s easier or better than the current system. Who really would benefit from this extra work? Nobody needs Controls or Loaders, etc. to be standalone.

more like nobody cares if they are. iirc 3js transform controls were taken by their author to its own repo, which caused issues when trying to backport the changes into 3js old controls code. another example would be yomotsu camera controls.

Not really, IMO. I do generally encourage people to install from npm and not to copy/paste or use CDNs, but I think that’s unrelated here. The core of the conversation is that people would prefer if three.js grouped “breaking changes” together somewhat, rather than sprinkling a few into every monthly release.

My question would be, why would a GLTF loader even change at all over the years in terms of compatibility with three.js?

We’ve made breaking changes to GLTFLoader in the past, and we’ll eventually make them again. Updates from glTF 1.0 to glTF 2.0, removing extensions, changing the scene structure of the result in ways that can subtly affect projects, etc. We just removed support for spec/gloss materials. Everything evolves.

My conclusion is that three has it’s own alias for what is essentially semver, but that it’s wrongfully calling R100 0.100.0 instead of 100.0.0 . I don’t know why.

This isn’t an accident, semver treats versions <1.0 differently. Tools like npm understand that v0.1 → v0.2 may contain breaking changes, whereas v1.1 → v1.2 should not. I think the goal when people say they want semver, is not (just) to use whole numbers, but that they don’t want every release to have breaking changes.

This is more or less what happens today? Except the bug reporting is subtle, it can happen over a few versions.

We try to ensure that every addon gets used in at least one example, and that all examples render correctly. The goal is to be sure that every addon is consistent with the release of three.js it’s being included in. Anything beyond that is best effort, we cannot realistically test permutations of “three.js [version] ⨉ addon [version]”. Mixing and matching addons with a different release of the three.js library is not supported. I think this is far easier to explain and support than the alternative, and if not every part of three.js changes with every release, that’s fine.


Exactly - prefer to have some bug fixes published separately from some features. Ie. why would every monthly release have to have new features, why not just postpone them slightly?

This still may very well be the case with three. It’s possible that some range of versions, even if only r99-r100 is actually a minor/patch version. It’s just a roll of the dice, so the only safe thing to do is assume that every is major (even if, by chance, it isn’t).

There’s a lot to unpack in your post, but maybe this is a good start. Are you an authority on this subject? Maybe yes, maybe no, i don’t know that without more information. But comparing something like this:



It isn’t really that much of a difference. I still don’t know how to interpret this. Are 800k people using three.js or only 700k? Or 700k plus one, or a few - the team behind this one package on npm.

This is really hard to gauge and i doubt that anyone can claim to be an expert here given how peculiar three.js is.

I’d be curious, say five years ago, how many people were downloading jquery via npm vs copy pasting some CDN link, and what was that ratio with a library such as react. My guess is that it was much more npm involved with react. Three.js is weird in this context because it’s serving both purposes sort of, but it comes with complexities such as webgl, where the DOM could be seen as a simpler thing.

But why does a system such as npm exist then for other software?
Do you need a new version and new features every month?
Be honest, do you actually update every month even if you do? :slight_smile:

Why? How can it be more tedious work to maintain just the /src and not also /examples? Is it not less code?

i’m not sure. our problem was:

  • every three release breaks, building an eco system, or even just professional software on top of this, without semver no less, is almost pointless. for a time drei broke every month
  • the whole wide world uses modules, node, npm, next, nuxt, sveltekit, etc, but three/examples/jsm won’t even run in a modern stack
  • there are no types, again, in the professional world that makes it a huge risk
  • patches and features need years to come through, many bugs simply aren’t fixed because people may rely on it
  • there are 2-3 people managing and reviewing 500 packages, which imo creates a huge burden for three and bottleneck for everyone

a standalone fixes all of these. stdlib, and i urge you to visit this link is near 100k npm installs per week now. we invite more and more contributors to share stake, people we know are trustworthy due to their responsible maintenance or prs. all these people can come together in code reviews etc. since we have semver we can allow breaking changes, new features, etc. managing versions is simple because each PR just needs to target three (and its own dependencies), if one PR needs a higher revision up the peerDependency goes.

in my opinion i don’t think stdlib and forking examples in general is good because it splits efforts. i wish addons was a separate, versioned package with npm compatible modules, optional d.ts annotation types, maintained by a larger group of contributors, with real examples, docs, etc. and most importantly, going forward rapidly, extending features.

addons is a treasure, but i honestly don’t think that three even recognizes it as one.

1 Like

I think mrdoob said at one point that he does not want examples as a separate repo (there was a talk about having the whole examples folder as a separate repo, not one by one) because it was mentally easier for him to not switch folders. So as long as they cant be bothered, this discussion is pointless :sweat_smile:

1 Like

yes, the maintainers have spoken out against it. discussing it won’t hurt. it’s just that not having a standalone will continue to hurt adoption, split efforts and the community. more professional entities, sites and apps will use alternatives, as they already do.

im here for 15 years now, i’ve seen people argue three cannot possibly be expressed as modules and we must stick to IIFE, classes cannot be used and we must stick to ES5, npm should not be used, it shouldn’t be bundled, we don’t need rollup, types are evil, tree-shaking is useless and 500k bundles are fine, versioning is pointless, semver no need, …

people just kept pressuring, because they need three to function in the real world and in their projects, and eventually it had to face reality and caved for many of these.