In the early days of programming, hackers around the world participated in what was referred to as the “demo scene.” It still thrives today but I am mostly familiar with the “old school” phase that mainly involved using hardware hacks and other tricks to push personal computers to their limits. We could “trick” the video processor into drawing in areas it wasn’t supposed to and with clever timing produce more colors or sprites than were “allowed.” It was a fun time and I learned a lot.
One popular effect is called plasma. It not only looks cool but can be processor intensive so pulling it off is considered a great trick. Really good programmers were able to generate transparent plasma and overlay multiple layers, generate “interference” patterns and even rotate the canvas on the fly.
Hey, look! If you're more of a Rust person, I did the same experiment with Rust! Check it out:
Now, back to Gophers and Go...
For my first attempt, I followed the excellent “The world’s easiest introduction to WebAssembly with Go” tutorial. I took a similar approach and set up everything inside the Go app to perform interop with the HTML DOM. I won’t duplicate the effort here to get started, but in general after you have a current copy of
wasm_exec.js and some bootstrap code, compiling Go to WASM is as simple as:
GOOS=js GOARCH=wasm go build -o plasma.wasm plasma.go
My setup looked like this:
This uses the
Another method sets up a sine table. This is what produces the plasma cycling effect. It is generated into an array to minimize the overhead of real-time computations.
The “main loop” is concerned with advancing pointers through the sine table and mapping the palette based on the position. It updates several variables that are part of the global state and cycle through the sine table and palette. For the first attempt, I set the fill style and rendered the pixels directly from Go.
The last piece is to kick off the application, ensure it always stays loaded, and repeatedly call the update method.
plasmaLoop is a channel that never clears so everything continues to run. The
renderer wraps the call to
setTimeout (yes, I am aware this could be
requestAnimationFrame as well).
plasma.js file and added this method to parse and render the buffer.
Notice that I can’t accept an array directly (hmmm … or can I? If you know a better way, please weigh in!) so I accept a base-64 encoded string and convert it back to a string. Then I simply iterate the pixels, set the fill style and draw the rectangles.
To pass the array, I changed the colors from string to structures with bytes for the red, green, and blue values (I don’t use blue but have it there in case I ever go back to change the palette) and added a buffer to pass back.
The palette is populated by setting the values rather than generating strings.
This improved the speed tremendously, but the performance still wasn’t as fluid as I wanted.
ImageData class that is a buffer of pixel information for a bitmap. You can create an instance and populate it. It expects the bytes to represent red, green, blue, and alpha channel, so there are four bytes for every pixel.
The code needs to decompose the base-64 encoded value into an unsigned byte array. It is simple enough to iterate through each byte and populate the target array. Then, the image data is created and drawn in one pass on the canvas.
The performance for this approach is amazing!