Introduction: LED Flame With a Plasma Effect

About: I like to make things for the internets. I also sell a pretty cool calendar at supamoto.co. You'll like it.

Controlling LEDs with regular patterns can get old. Even the best can be predictable and a little boring. But controlling them with an algorithm that can create a more organic feel is way more fun and has a nicer result.

This Instructable goes over the process of working with the noise algorithm to adjust these effects as much as you want. And of course all the files you need to make your own.

Step 1: The Stuff

What you need to make this:

The microcontroller I'm using here is the Pro Trinket. But it's a bit older and deprecated and there are many better options now to go for now. It's just what I have. Some other small, low-cost boards that should work (but I haven't tested):

Step 2: The Printed Pieces

Files are attached and also at Thingiverse.

The flame is made up of three pieces: the flame itself and the base pieces.

Flame piece

  • Natural PLA
  • 0.2mm layer height
  • 3 perimeters
  • 0% infill
  • No bottom layer
  • Printed with a brim so it would better stick to the bed while printing.

Base pieces (standard PLA settings)

  • Black PLA
  • 0.2mm layer height
  • 2 perimeters
  • 20% infill

There's one extra piece that attaches to the base from the bottom. I used this to attach the flame to a 7/8" dowel for a Halloween prop. There are also two lids. One has cutouts for attaching wires to the underside of the LED ring and the other does not.

Step 3: Assembly

The base pieces can be attached with the M3 bolts. The bolts should tap the bottom piece as they go in. This way there's no need for an insert.

The flame can be twisted into the base on its own. If it doesn't go in so well, you may need to trim off some of the brim that is still attached. If it's still not working you nay need to do some sanding or scale down the size of the flame piece a tiny bit.

Step 4: The Electronics

I soldered a Micro-JST piece onto the Pro Trinket for the battery and added a switch to the battery wire to power it on and off.

As for the NeoPixel ring, it needs to be attached/soldered to 5V, ground, and a digital pin. I'm using pin 12, but of course this is easy to change in the Arduino sketch if you want to use a different pin.

Step 5: The Code!

This is the Github repo for the Arduino sketch for running the LEDs. You can use this and be done. The following steps go over the noise algorithm and what different variables will affect.

Note: this requires the FastLED library.

Github repo

Step 6: Perlin Noise

The algorithm that is controlling the effect here is Perlin Noise. Perlin Noise generates images that are meant to add a natural-feeling bit of texture. This was created back in 1983 for computer animation since there was no room to store images for texture maps, but there was room for an algorithm to create those textures.

If we want to animate the noise, we move in the next dimension. So if there's a one-dimensional array of LEDs, a line, then we move that line in the Y dimension.

Similarly, if we want to animate a 2D image, it needs to move in the third dimension.

To make better sense of this, let's look at the noise function.

noise(x, y, z) = number from 0 to 1

This can take 1 to 3 arguments. So the y and z parameters are optional and these will all work. All the values here will be different for you.

noise(.34) = .453

noise(.34, .53) = .634

noise(.34, .53, .92) = .536

And as for animation, we can fix one parameter and change the last one. So a very simple Arduino sketch could be:

float y = 0;

void loop() {

y += .01;

noise(.34, y);

}

or if we wanted to animate a 2D image, each block would be:

float z = 0;
void loop() {

z += .01;

noise(.34, .53, z);

}

This is the minimum for animating the noise. Others have added extra small movement to the x and y axes in addition to the z movement to make it more dynamic. It think this is more necessary for when the noise fills the screen than just a few LEDs so I haven't used it here.

Step 7: Brightness

I'm controlling the light values with the HSB model of Hue/Saturation/Brightness.

Brightness controls just how bright the LED is shining, from totally off to full brightness or any subset of of that range.

Here, the values for brightness range from 0 to 100. Since Perlin noise outputs in the range of 0 to 1 we can multiply the result by the max.

So here, a simple example would be:

brightnessValue = 100 * noise(x, y, z);

Step 8: Saturation

Saturation is very similar to brightness. It is also on a scale of 0 to 100 and it determines the intensity of the hue. Zero would be fully desaturated, which is white. 100 would be the richest form of that color.

Once again, a simple example of this is:

saturationValue = 100 * noise(x, y, z);

Step 9: Hue

Hue is the color that we are working with and is different from the others because it is cyclical and not linear. The hues are determined using the color wheel. Red is the origin for values here and is often mapped to 0 to 360 for moving clockwise around it.

We could use the same formula as before to start with:

hueValue = 360 * noise(x, y, z);

This would apply the noise values to the full range of hues, centered on green. But let's say we wanted to center it on a different color, centerColor.

Then this would be 360 * noise(x, y, z) - 180 + centerColor;

The 360 we were using is really the range that we are mapping the noise values to. Subtracting 180 is subtracting half of the range. Then we can add the centerColor value.

But what if the resulting number is negative or over 360? We can take care of this with:

(360 * noise(x, y, z) - 180 + centerColor + 360) % 360;

Adding the 360 makes the value positive and it will still be the same color angle. Using the modulo (%) for 360 gets rid of any values above 360.

The modulo is like the remainder after dividing. So 5 % 2 = 1, 6 % 2 = 0, and 540 % 360 = 180.

We could take this further with:

(range * noise(x, y, z) - range / 2 + centerColor + 360) % 360;

With this we can change the range of colors and have a variable for the center so that we can start color cycling. On every loop we can increment the colorCenter like this:

centerColor = (centerColor += 1) % 360;

of we can add a variable again:

centerColor = (centerColor += colorIncrement) % 360;

if (centerColor < 0) { centerColor += 360; }

The second line here takes care of negative colorIncrement values.

Step 10: Putting It All Together

If we merge everything together, we get this pastel-ish animated stuff. It may look a bit weird here, but this is meant to be used as a basis for LEDs which display light in a different way than your screen.

Step 11: Refactoring Brightness and Saturation

Following the changes for range in Hue, the same can be done for brightness and saturation. Since they're the same linear scale we can use similar functions and drop any specific name so:

value = 100 * noise(x, y, z)

can become

value = range * noise(x, y, z) + startValue

And if you try some values that might push the result out of the 0-100 range, use the constrain function:

value = constrain( range * noise(x, y, z) + startValue, 0, 100)

Or use some other limits.

value = constrain( range * noise(x, y, z) + startValue, minValue, maxValue)

Step 12: Playing With Speed, Scale, and Range

The main aspects to play with are speed, scale, and range

Speed

Pretty straightforward. Just how fast do you want your animation to happen?

Scale

The scale is the distance between each neighboring coordinate in your field of noise. A small scale will result in bigger blobs of noise, while a larger scale will have lots of smaller blobs of noise within view.

Range

The values that come from noise are in a bell curve shape with the majority of values in the middle 60% of the full possible output range. If you want a "fuller" representation of your intended range to show up you will need to move and scale up the resulting values coming from the noise function.

Or you may want to scale down the resulting value and move them to the top for a different targeted range. It's entirely up to you.

Step 13: Just Keep Playing With It

The most important thing to do is mess with your variables a LOT and see what the results feels like. Getting the right mood takes plenty of trial and error.

Good luck!