Our initialization functions sets that initial state of the circles and our time step function then updates the positions based on the velocities and bouncing off the edges. The details of the functions aren't actually that important.
The xy and r values of each circle can be represented in a C struct, with each entry set as a float data type. It's still effectively the same representation and memory as three floats in a row we had manually in our typed array.
The velocity data can then be stored in the same way with the x and y components of velocity as floats. To actually initialize the circle data, we need to create an array of these circle structs. The way we initialize that in C is with the struct circle and then the name of the array that we're creating.
Now we're ready to write our inaudible time step functions. Converting their signatures into valid C function signatures, both these functions have no return value so they are void. The display width and display height are both float arguments.
To iterate over the array of circle data, we no longer need to do this complex iteration. We can simply have a single integer which is iterating over the full circle counts and incrementing by one. C can take care of the rest for us.
Updating the indices we then can access our struct items individually. The first item is now called x. We no longer need to do a custom increment, we can just represent the y value as y and the r value as r. The same for the velocity data.
We're going to still be calling math.random underneath because we don't have access to another random source in WebAssembly, but we're going to make it as an external function. We can just define our math.random signature as returning a float value, and then we can call it random F.
By simply defining the signature it will be treated as an external function and we can then replace those calls. The C file should really remain the source of truth for where the circle count is defined.
I'm going to create a helper function that returns an integer called get circle data offset. The return value is the memory address of the circle data array, when compiled this becomes the exact WebAssembly memory address.
To quickly compile I'm going to copy the C code into wasm fiddle. Once the build completes successfully the .wast format comes up at the bottom of the page and we can then download the WebAssembly binary file.
Checking the .wast output, we can see that we're importing the random F function from an environment module that we're going to populate shortly.
We're also exporting our memory as well as all of the functions that we've created in our C file in a new file that's now wiring the WebAssembly. First copy in a helper function to load the WebAssembly. I'm going to call it with the path to our WebAssembly file which I have located in a folder called libdynamics.wasm.
The second argument contains the imports for the wasm module. We need to set the environment import which contains a random F function, which we assign from math.random. This returns a promise which resolves to our instantiated WebAssembly module.
Let's work backwards from the arguments to our random function. First thing we want is the circle count. We can get that by calling the get circle count function that we created directly off the WebAssembly module instance.
In the same way we can call the get circle data offset function to get the memory address of the circle data array in wasm memory. To get our circle data typed array we can use a different form of the float 32-array constructor.
This allows us to create a typed array from any buffer source. In this case we can use the exported memory object from the wasm module accessing the buffer property. This constructor form then takes two further arguments -- the offsetting bytes within the buffer and the length in bytes within the buffer to generate the typed array from.
The offset is exactly our exported offset and then the length is exactly the length that we had at the very beginning -- the number of circles times three, the total size of our circle position data and memory. Our inaudible time step methods can then be accessed directly from the instance exports, completing our WebAssembly conversion.