Use Audio Modulation and Easing to make Sparks Fly

Sparks flying from a campfire — Photo by Courtney Prather on Unsplash

In this article and the accompanying video, I’ll explain how to use easing and audio modulation in a Sinewav3 plugin, to drive the behavior and appearance of a particle system that simulates the sort of sparks you often see flying off a roaring campfire.

The video at the end of this article does a deep dive into how the plugin actually works. If you’re more of a hands-on person, you can check out the code for the Sparks Fly plugin on GitHub, or if you’ve already created a free Sinewav3 account, simply copy the Sparks Fly example plugin, and edit it like this:

Copying the Sparks Fly plugin inside Sinewav3

Behavior of the Plugin

It is best to write your plugins so that they look good both with and without audio. Even though Sinewav3 is primarily about creating audio modulated visuals, everything doesn’t have to be tied to the music.

First, lets see what the plugin looks like when there’s no audio playing…

Sparks Fly plugin, no audio

Separate easing functions control each particle’s motion in all three dimensions, as well as its life span, size, and the rate at which it fades out.

Next, let’s see the plugin with an audio track playing. All the same easing functions are controlling things, but additionally, the aspects of the music like its bass, midrange, high, and overall volume are modulating each of the values output by the easing functions.

Sparks Fly plugin, with audio modulation.

Plugin Settings

As a plugin developer you could throw all the settings into one group called something like ‘Params’, and that’s ok if there are only a few. But when you expose a lot of settings, it’s easier for the user to navigate them (and for you to reason about them) if you group them logically.

In order to give the user control over all the stuff going on in Sparks Fly, the plugin exposes a bunch of settings in three groups: Emitter, Particle, and Particle Motion.

The Emitter Settings

The Sparks Fly plugin’s Emitter settings

The source of the particles is the emitter, which is merely a point in space where the next particle should be generated. It’s got an X, Y, and Z position so that the user can place it wherever they want in their world. But it looks pretty boring when all the particles come from the same pointe, so we’re going to swing the emitter around in space. Swing Duration controls how long it takes to swing around. Swing Radius defines a sphere around the emitter and we’ll sweep from one point on that sphere to another over time. Swing Curve is an easing function that controls how we’ll actually move the emitter while swinging it. And finally, Swing Mod is a modulator that will control various aspects of the swing after all the above considerations, and by default it’s set to use the Audio Bass.

The Particle Settings

The Sparks Fly plugin’s Particle settings

Here, the user can control the color of the particles, and their maximum size, and life span. Size Mod allows an aspect of the music to modulate the particle size (up to the maximum set by the user). Life Span Mod allows the music to further limit the life span (again, up to that set by the user).

The Particle Motion Settings

The Sparks Fly plugin’s Particle Motion settings

The user is able to set the minimum and maximum distance that a spark will fly from the emitter after it is spawned. X Curve, Y Curve, and Z Curve afford customization of particle movement in for each axis.

If you notice in the screen captures above, the particles move in a very natural way, almost seeming to be blown by unseen currents in the air. This is because we’re using different easing functions for each axis.

If we move the same distance on each axis as we ease from one point to another, then visually, we’ll be moving in a straight line regardless of what angle we view it from. But if we vary the curve used to move on each axis, then we end up seeing the spark follow a curved path from every perspective.

And Now, the Deep Dive

If you really want to know what’s going on with the code in this plugin, watch the video. And when you’re done, there’s an extra credit assignment below!

Extra Credit Assignment

So you’ve studied the code and/or watched the video and hopefully are psyched to make something happen yourself and see some quick results.

Right now, the particle system emits only one spark per frame. Let’s turn that into a firehose.

Here’s the assignment:

  1. Sign in and copy the example plugin.
  2. Make a project and add a world that uses the ‘Sparks Fly copy’ and ‘Background Color’ plugins. When you visualize you should see orange sparks like in the screen captures above.
  3. Go back to the ‘Sparks Fly copy’ plugin and add a setting to the Emitter group called ‘Spray Density’, an integer with a range of 1 to 100, with a default of say 10.
  4. At the bottom of the setup() function, add a new function to context.memory called spray().
  5. Inside the spray() function, get the Emitter/Spray Density setting value and then in a loop for that many times, call context.memory.spawn().
  6. In the render() function, instead of calling context.memory.spawn(), call context.memory.spray().
  7. Save the plugin, go back to the project and update the plugin to get the changes (i.e., click the little refresh icon on the plugin).
  8. Visualize and you should see more sparks already. Open the settings menu and tweak the Emitter/Spray Density setting to see the change in the effect.
  9. Now go back to the plugin, and add an Emitter/Spray Mod setting, of type Modulator, and set it to Audio Volume.
  10. In your new context.memory.spray() function, get the Emitter/Spray Mod setting, and the modulator value for that setting. Multiply the spray density by the modulator value and use the result for your loop’s upper bound.
  11. Save the plugin, go back to the project, refresh the plugin to get your changes, and select one of the built in audio tracks on the audio tab.
  12. Visualize the project and you should see the spray density change with the music.

Happy coding!