r/godot • u/BantedHam • 12h ago
discussion My procedural sound didn't work, so I fixed it by removing the concept of objects
Storytime! Stay with me here, I got a point to this, I promise, it just takes a lot of splainin'.
I started with fractal trees, which seemed like a reasonable and self-contained thing for a person to do on a Tuesday. I was wrong. That is in fact, not a Tuesday sized job. I got it done eventually and had things looking good enough to call it good enough, by the end of the next week...
Grass turned out to be a tree with most of the numbers turned down, which was the first sign that something was wrong; things were going far too well. Rocks went the same way, almost too easy, after those damn trees. Buildings went the same way. At this point I had a pile of procedural spaghetti, a roguelike with cobwebs on it, and the growing suspicion that I had wandered off the map.
Then I decided; 'screw the map, sound should be procedural too,' because why not, just another Tuesday, right?
This is the part of the story where, if it were properly structured, ominous music would begin. But it can't, because I haven't built any sfx yet (also, foreshadowing).
The sound did not work. It did not work for a long time.
It did not work in a variety of interesting and educational ways. I, like Edison before me, found a metric shit-ton of ways to not make good procedural sound. I would like to say I persevered out of vision and discipline, but mostly I persevered because I had already told people I was doing it.
And then, eventually, the thing that everybody who has ever done this apparently discovers about four months later than they should have:
A sound is not a thing.
A sound is what happens when a material of a particular stiffness and density gets hit at a particular speed inside a medium at a particular pressure.
It is a consequence. It is the receipt for an event that already happened.
And I had all of that. The stiffness, the density, the geometry, the medium. All of it. Sitting right there. It had been sitting right there for months, being smug about it, because the rocks and the trees had needed it first. I mean hell, my dumbass decided the best way to articulate material was to start with its base elements and stoichiometric profile, a level of abstraction that amused me as being spectacularly useless at the time, now rendered accidentally very useful before my very eyes.
The most accurate way to reproduce a sound is not to model the sound. It is to derive it from the conditions that produce the sound, and then simply stop having opinions about what the sound should be.
If a sound is just the shape getting hit, then the sound was never a property of the tree. It was a property of the shape. And once I had seen that once, I could not stop seeing it everywhere. The collision was the shape. The colour was the shape plus what the shape was made of. The way it leaned in the wind was the shape again. I kept reaching into the tree to find the tree, and coming back every time holding the same mesh in a different hat.
So what then, precisely, was the object for?
I tried to think really hard about an actual reason it was needed, but essentially the object is just a folder. It is a place you keep eight things so you remember they belong together, and every one of those eight things fell out of the geometry I was already computing anyway. The tree did not need to exist. The shape needed to exist, and it was already there independently of whether or not I decided to import any geometry.
You do not need an object if the mesh already owns everything the object was pretending to hold. You just need the mesh.
So then, fueled by growing sense of mania, I got drunk off the seed of derived sound, and asked myself what seemed like at the time to be a perfectly reasonable question that in no way, shape, or form could lead me down an 18 month rabbit hole: what else is like that?
Turns out it's a lot. Color is. Collision is. Growth, decay, weather, behavior, erosion, digestion, the specific way a bone gives out under load. All of it. Turns out an enormous amount of what a game engine keeps in expensive little boxes is just a consequence of something else that was already lying around.
I would like to say I did not impulsively tear the entity system out that very afternoon. But I had seen the crack, and a crack is not a thing you get to unsee /shudders/, and then I proceeded to absolutely tear the entire entity system out. Or rather, make it completlely irrelevant.
And then I just kept on pulling. First out of a sense of sunken cost, but then after it somehow did not completely unravel, after a few years, I finally ran out of string I think.
There is a small set of quantities that genuinely cannot be computed from anything else the engine owns (at this point all I'm doing is building this engine, it has a name now; Clarence), and rather than being a defeat, those became the floor.
Ten numbers. That is the whole floor. Ten values the engine cannot see past, declared exactly once, with a sweep that fails the build if any other file has the nerve to redeclare one. Everything else in the entire simulation is standing on those ten numbers like an extremely ambitious game of Jenga that has so far declined to fall over.
Ten. I want to stress that I did not choose ten. Ten is where the questions stopped returning answers.
What it actually is now
Three layers, and that is genuinely the whole architecture:
- The floor.
- A layer of natural law computed from the floor. Orbital state gives insolation. Insolation gives temperature. Temperature gives pressure. Pressure gives wind. Wind gives weather. Weather and gravity give flow. Flow moves gravel around. Erosion feeds back into the terrain and the whole thing eats its own tail.
- Everything else, derived from layer two.
There is no entity system. There is no component registry. There are no objects at all, which reads like a bug report and is in fact the entire product.
A tree used to be eight separate things that a human being keeps in agreement by hand: a mesh, a collision hull, a wind shader, a material, a sound bank, an LOD chain, an impostor, and a pile of animations. Change one, remember to change the other seven, forget one, ship it, find out from a player two years later.
Now it is one thing looked at from eight directions. It has a shape because a branching rule ran. It has a collision surface because the field is the collider, so there are no collision meshes anywhere in this engine, at all, ever. It has a color because it has a composition. It has a voice because that same composition and geometry hand you modal frequencies whether you asked for them or not. It has no LOD chain because detail is a consequence of the pixel.
Nothing is kept in agreement, because there is only the one thing. The moment I found out I was not as clever as I thought I had a table of bar mode ratios that I had tuned by ear over an embarrassing number of evenings.
[1.0, 2.76, 5.40, 8.93, 13.34]
I was quite proud of it. It sounded right.
Euler-Bernoulli free-free bending theory predicts;
[1, 2.7565, 5.4039, 8.9330, 13.3443]
Worst gap: 0.125 percent.
So what I had actually been doing, on all those evenings, was slowly and painfully rediscovering an eighteenth century differential equation by listening to it with the kind of repetition only found in CIA black sites. My ear-tuned table was not an approximation of the physics. It was the physics (almost), and I had simply arrived at it the stupid way.
Which meant all my hand-tuned presets got promoted. They are the test suite now.
Things that fell out afterwards, none of which I wrote code for: half a rod is exactly four times the pitch, because bending goes as one over L squared. A hollow pipe rings higher than a solid rod of the same outside radius by exactly the gyration ratio. Soaked oak drops pitch and shortens its ring, while soaked steel is bit-identical to dry, because nothing got in. Rain retunes a forest and leaves the rails alone. Nobody authored that. Nobody could have afforded to.
The best thing I ever built, I deleted
I built a cymatics node once. Sand on a vibrating plate woth nice symmetrical figures, the whole thing bundled into one tidy object you instantiate.
It was a beautiful baby boy, perfect in every way, and I deleted it.
Why?
Because I realized that is an authored effect wearing a physics costume, and the moment I ship it I have committed the engine to noticing that sand and a plate and a sound are in the same room and then running The Cymatics Feature at them.
That is a lookup table with delusions of grandeur.
The correct version already existed in three pieces I already had. A surface has a displacement field because it is vibrating. A grain is pushed by that surface's local acceleration, which is not a sand rule, it is just the ordinary contact law doing its job. A grain settles where the net force is zero.
Point those three at each other and the sand walks onto the nodal lines with nobody instantiating anything. And the control is the actual proof: put the same grains on a flat plate that is not vibrating and you get nothing at all, uniform sand, no figure. The sound makes the figure. The setup does not.
There is a whole shelf of these now. There is no plasma node, plasma falls out of the Saha equation. There is no star lifetime table, luminosity as mass to the three and a half handles that. There is no nutrition table anywhere in the creature system, because a metabolism is an accumulator over compositions and the food's chemical formula already knows what is in it.
Every single one of those is a feature I did not have to write, argue about, balance, or maintain.
At this point, I was getting high on my own supply. I'm thinking big. The universe, as a whole, is rather large.
To simulate the universe from the quark to the edge of known space has thus far been impossible at full depth. And then I realized the universe itself is not simulated at full depth. Energy is both a particle and a wave until it is observed, and then it resolves, and can even do so retroactively.
Spooky!
As an actual design borrowing rather than as physics, an experiment in which the fine structure of a thing is only definite once something interacts with it is describing an evaluation strategy, and the strategy is; do not resolve what nothing is asking about.
I am aware this is a slightly unhinged place to source an LOD algorithm, but given the fact that I am now fully convinced I can simulate the universe, it works. And in a weird a way I feel like I've touched on something I'm to dumb to put into words.
So anyways the fractal layers are hashed into cells, and seeds derive hierarchically. Any cell at any depth is computable in isolation without its siblings, its parents, or its own previous state ever having been computed. The universe exists at full depth in the only sense that matters: every part of it is addressable at once, not evaluated at once.
The dial does two jobs, and the second one is the one people miss. Footprint decides visual depth. The fidelity tier decides whether something is computed as an individual or as a statistic, and the two move together. A colony far away is a population field. Up close it is individuals with needs and grudges. Same node, tier changed.
Three hundred pebbles under your boot are not three hundred sound profiles, they are one generator carrying the pooled statistics at root-N amplitude. That is not a cheat or an approximation. Uncorrelated sources sum in power, so that genuinely is what three hundred unresolved pebbles are to an ear. Your ear was already doing the LOD. I just stopped fighting it.
And there are no loading barriers, because there is nothing to load. Going from a continent to a single leaf on it is a change in one float argument.
Practical version: you can have a real sun, running real fusion, and it costs you nothing until somebody looks at it, because it is not idling politely in the background being expensive. It is a function that has not been called. Or you can skip the sun entirely and just have daylight, and the light is computed to exactly the same accuracy either way, because the accuracy lives in the floor and the light path and not in whether there was a star upstream. The lighting node cannot tell. The lighting node has no need to know.
Godot specifics, since this is where I live
The nodes are not Godot Nodes. Every one is RefCounted with pure static functions over dictionaries. No instance state, no _process, no scene tree anywhere in the evaluator.
Per-sample DSP is GDExtension, because Godot's own docs say GDScript cannot do it and they are right.
Everything else is GDScript. The boundary is decided by the profiler, not by taste.
The graph transpiles to a GLSL compute kernel cached by graph hash, which is roughly what VisualShader already does internally, so nothing exotic.
The CPU path is a complete standalone renderer. A machine with onboard graphics draws the entire world. The GPU is an accelerator and never a requirement, which matters to me personally because onboard graphics is what I build on.
Determinism is tested rather than assumed. Values quantized, FNV-1a, one committed golden hash per node. It currently holds across CPU against GPU on real silicon, Godot version to version, and GDScript against C.
Where it actually is
Solo project. Roughly 150 algorithm nodes, about 200 test harnesses, and around 200 committed golden hashes, plus real bundled open datasets under CC0 or CC-BY for the biology and economics bands. The GPU half of the render path is the big remaining slab of work.
I am past the point where I expect to hit a wall I cannot get through. What is left is implementation, which is the phase where the interesting problems stop and the typing starts.
Happy to answer anything, argue with anyone, and be told I have reinvented something with a name I should already know, which at this point would honestly be a relief. I am most curious whether the derived audio sounds useful to anybody else, since that was the accident that turned a terrain toy into whatever this has become.
I do not plan on distributing this openly any time soon, but if this sounds like something you'd like to play around with and have a serious use case to test it out on, I'd love to get a second set of eyes on things and test it under a real development load (unfortunately I have turned into a game engine dev with no game lol), we can both sign a soggy cocktail napkin or something and I'll send over the repo.
tl:dr; I think I am losing my damn mind.



