Writing a Physics engine from scratch is a great fun. You can get results super quickly, it's very, very optimisable, and there's no end of features you can support. It's also surprisingly doable, in 2D. 3D is an entirely different can of worms.
It's also an incredibly humbling experience to write one, and benchmark it against Box2D to see just how slow your implementation is.
Fully agree! Years ago, I attended a course where we basically did just that. (For extra fun: I also attended a parallel computing course at the time, where 90% of our exercises turned out to be heat distribution simulation on a toy cluster. Both courses ended up being back-to-back in adjacent rooms)
Modeling solid or even soft bodies as simple spring-mass-damper systems is surprisingly straight forward and gets impressive results fast. Getting results that are numerically stable, is a bit of a step up from there. IIRC the step up to rigid body simulation was a tough one, but mainly because of the annoying collision detection & handling logic. It's a bonanza of trade offs and everything you do essentially has different classes of issues. Keeping track of both performance and stability is bit of a PITA. I'd guess that's also your main difference between doing just 2D vs 3D, all of the other math should be the exact same.
I remember we did a strange detour into Navier-Stokes equations and 2D fluid simulation towards the end, which I'd argue was a much bigger step up. I ended up the only one with a (very slow but working) solution, having asked a friend with a physics degree for help.
We did some little project assignments at the end where I implemented a simple height map based water surface simulation[1]. Another surprisingly simple little project I recommend trying, that also yields impressive results fast.
I recall another guy who implemented cloth simulation and ran into massive problems with the numeric stability. Turns out, doing straight forward spring-mass-damper systems with Euler integration literally explodes in your face all the time. Multi step Runge-Kutta is of course much more stable, but again, requires a lot of tweaking to get up to reasonable speed.
I took a very similar course, and we were taking a course in Rendering at the same time. Most people in my year ended up using their physics simulations to demo their rendering and vice versa.
I went down the route of fracture & deformation rather than fluid sim route, and ended up implementing [0] (or, some amount of it. As you mentioned, the numerical stability aspect is tough).
Context: This is a widely used C++ library for 2D physics. It has always been super flexible/hackable, easy to setup and rock stable physics. Therefore a popular choice for games. Good to see that some of the code idiosyncrasies have disappeared (it had a lot of pointers and callbacks)
Or do you mean threads that aren’t built on top of webworkers? I don’t think there’s a meaningful difference, since both the code and memory are shared across workers using SharedArrayBuffers
In C? Tons. Because arrays in C decay to simple pointers so at runtime there is no such thing as an "array" with "bounds" there's just "memory goes brrr" and it's up to you to implement bounds checking yourself on a case by case basis.
You can also do what I do and treat Lua as essentially a C framework and use Lua tables.
This is something the compiler can provide better than a library. I would go one step farther and ask for full Lisp-style macros in C. Let me generate code that can't be expressed with the basic C syntax. Then I can extend the type system with a dynamic array type that is really hard to misuse.
It's also an incredibly humbling experience to write one, and benchmark it against Box2D to see just how slow your implementation is.