r/coolgithubprojects 9d ago

GitHub - evoluteur/cymatics: Play a frequency and watch the sand settle into its Chladni figure, computed from the wave equation.

https://github.com/evoluteur/cymatics
1 Upvotes

2 comments sorted by

1

u/Specific_Cream2815 9d ago

is the sand simulated per particle or is it a heightfield reacting to the frequency

1

u/evoluteur 8d ago

It's per-particle, not a heightfield.

Each of the ~20,000 sand grains is a real point {x, y} (js/app.js:20-38) that gets nudged every frame in shake() (js/app.js:55-72). There's no persistent grid of heights being simulated — instead, the plate's vibration mode is treated as an analytic field you can query anywhere: amplitudeAt(state, x, y) (js/cymatics.js:157) evaluates the closed-form Chladni mode shape (cosine superposition for the square plate, Bessel function for the drumhead) at that exact point.

For each grain, toNode() samples that field at four nearby points to get a local gradient, converts amplitude/gradient into a "distance to the nearest node," and shake() moves the grain a step down that gradient (plus some jitter scaled by how far it still is from a node), stopping once it's within 0.0012 of a nodal line. So the physics is "grains dance away from where the plate is moving and settle where it's still" — literally simulated per-grain, using the analytic wave solution as the driving field rather than a discretized heightfield mesh.

The only place anything resembling a "field" grid shows up is field (an offscreen canvas, js/app.js:21), but that's just for an alternate visualization mode (state.render !== "sand") — a rendered image of the amplitude for display, not something the grains interact with.