My problems with JS:

1. Allocating objects or closures results in death by garbage collection.
2. So I spend tons of effort managing indices, juggling reused objects, and passing them around awkwardly because functions can only return 1 value.
3. As for the objects, only TypedArrays/ArrayBuffers are fast, but they too require juggling due to having fixed maximum lengths.

If I started using Rust or something instead, would it ease these specific problems? Or is this just my life now?

Judging by the kind replies it sounds like Rust would help some, but I’d still end up juggling indices and maximum lengths. Should probably learn to deal with those better in JS before considering other languages. ECS time?

@jonikorpi some useful resources I liked when setting mine up:

- https://moonside.games/posts/archetypal-ecs-considered-harmful/
- https://www.remedygames.com/article/how-northlight-makes-alan-wake-2-shine
- https://www.gdcvault.com/play/1024001/-Overwatch-Gameplay-Architecture-and

all of these use "sparse" ecs implementations, allowing ad-hoc adding/removing of components, which imo strikes a nice balance of developer friendliness for iteration and experimentation, while retaining most of the performance benefits of ECS (e.g. things are still packed into arrays, and most situations will experience good cache line utility)

Archetypal ECS Considered Harmful?

The Inherent Mendacity of Benchmarks

Moonside Games

@jonikorpi the alternate, is things like DOTs, which was cool! but requires a lot more ahead-of-time decl, which does benefit perf, but makes iteration a bit more cumbersome.

small example, this is the system that processes animations in my engine; by allowing adhoc adding of components to particular entities, I can easily add useful tag components. In this case, it's a "staged" animation, which is just a series of connected anims (a start anim, a loop/idle anim, and a completion anim).

@jonikorpi I do wanna echo @dotstdy s point though, that its not a panacea. I've just found it as a generally useful data structure which breaks direct deps between systems nicely
@jonikorpi Are you talking about deploying to the web or? If native it's going to be a lot easier to make fast without javascript. If web / wasm, probably going to be a pain no matter what.
@jonikorpi Regardless any other design things are going to be pretty tied to your concrete game and the problems you're having. "ECS" isn't any kind of magic really.

@dotstdy @jonikorpi also, this haha

wasm is a bit of a pain, setting it up with SDL2/3 wasn't _too_ bad.

@dotstdy I’m doing the web thing yeah, wrapping with Tauri for distribution. I guess I like pain!

@jonikorpi Is it really a CPU bound Problem? From my personal experience most JS performance are caused by IO or initial bundle size.

Also most garbage collectors for JS are generational, which means they are fast for short-lived objects and the real problem are medium-duration objects, that fill up the old space. (I haven't measured it, so take with a grain of salt).

@Marthog Well all the code that I’m finding difficult/tiresome due to the above runs very often, or in large quantities.

Terrain generation and game logic in enormous bursts. Graphics and physics stuff 60–120 times per sec, audio synthesis 44000–96000 (aaagh) times per sec.

That’s a lot of CPU! And much of it suffers from any kind of GC hitches.

@jonikorpi those specific problems, yes - massive bottleneck for JS performance

new problems: understand & working with Rust's ownership and borrowing

learning new language: something like Odin/Zig allows low level alloc/dealloc, pointers etc

halfway option is to offload any compute heavy tasks to Wasm with shared array buffers. Zig compiles super easy to Wasm. Workers allow concurrency.

@jonikorpi Rust would actively encourage you to use an index-based approach rather than object-soups (way easier to satisfy the borrow checker), so you still need to juggling indices

@jonikorpi my life now as well. 😌

My assumption would be similar problems, but better tooling, in Rust (etc) …but I haven’t worked a lot with these languages either.

Maybe there are coding patterns and/or tooling that would help in JS? I’ve been finding the flyweight pattern helpful — some CPU overhead but not the memory/GC issues. Recent example:

https://github.com/CesiumGS/cesium/pull/13212

BufferPrimitiveCollection: Add point/line/polygon data models by donmccurdy · Pull Request #13212 · CesiumGS/cesium

Description Highlights Adds performance- and memory-oriented APIs for working efficiently with large vector collections: BufferPrimitive BufferPoint BufferPolyline BufferPolygon BufferPrimitive...

GitHub
@donmccurdy I guess ECS is the most popular bigger pattern? I’m curious about it, but unsure how to apply it properly outside very simulation-oriented use cases.

@jonikorpi yes, because Rust actually has a notion of "place in memory", you can be very explicit about where allocations happen, and many things that can only be solved by boxing allocation in JS can be done with by-value or by-reference.

  • Lambdas don't automatically cause allocations even if they capture variables from their scope;
  • References instead of index juggling, but it does support tuples by value so you can cheaply return multiple values;
  • You still need to care to avoid reallocations with variable sized collections, but it does have the tools for that.
  • Whatever you do, test performance only in a release build though xD