Meshlets have quickly become a very popular construction in graphics, over, I’d say, about the past ~10 years. Mesh shaders (hardware meshlets abstraction) are quite ubiquitous in engines today, and with the advancing adoption of virtual geometry tech across the industry - meshlets are becoming even more relevant. With the most recent example being clustered BLAS tech from NVIDIA
My decision to go with meshlets is, at least in part, based on that direction. But yeah - they are a pain to integrate, API does nothing for you, there are no libraries in JS space and learning materials are sparse and have a fairly high technical base as a pre-requisite.
About the multi-threading. It’s a mixed bag. I do use workers, and I’m glad they exist. The problem is that these are not true treads.
Communication with workers can be done 1 of 2 ways:
- messaging
- shared memory
The shared memory is several times slower than private memory in Chrome, so it’s not a great option if you’re chasing performance.
Messaging comes with it’s own set of problems. Let’s compare this to something like rust, for example. Not great.
Another problem with workers is to do with how they are created and their lifecycle. A worker is essentially a brand new runtime context, this comes with memory overhead. Spinning up workers takes time, even if it’s “fast”, it’s at least a couple of orders of magnitude slower than native threading abstractions. Workers are essentially heavy threads, but like… extra heavy.
Last but not least - you have a pitiful capacity. You can spin up, say 8 threads, if you try to start 9th - you’re going to fail. 8 threads is nothing when it comes to running parallel code. Let’s say you get 16 instead, or, let’s dream big and say it’s 32 threads - that’s still a limit that you have to work around.
Here’s a practical example - if you use draco in your three.js project, it will spin up 8 threads for decompression by default, and it will hold onto them… forever
A common pattern is to do a lot of processing at the start of an application, decode meshes, textures, convert formats, generate missing attributes such as tangents/bitangets etc. And a common practice in software engineering is to abstract and separate concerns.
Now, if your decoders each grabs a few threads - it’s a game of musical chair, someone is going to left standing when the music stops. And your application crashes.
Anyway, I don’t want to say that workers are bad. I just don’t really consider them as something that makes JavaScript multi-threaded, the limits are just too limiting.
Add to that the deployment aspect. How do you start a new worker? Well, you need a separate JS file and be able to point to it. Building that is a pain. Pretty much every build framework out there, including vite and webpack gave it a go, trying to make using workers painless. They all failed 
Heck, meep (my engine) has a worker abstraction as well, solving some of these issues I outlined, and there are at least a handful other libraries out there working in that direciton, but there’s only so much you can do.
Fully agree. That’s why Shade is GPU-driven, to keep the number of GPU commands issued by the CPU each frame as low as possible.