LED Flame With a Plasma Effect

13,189

99

9

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.

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

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!

2 People Made This Project!

Recommendations

  • Raspberry Pi Contest 2020

    Raspberry Pi Contest 2020
  • Wearables Contest

    Wearables Contest
  • Fix It Contest

    Fix It Contest

9 Discussions

0
bpark1000
bpark1000

Tip 2 months ago

There are several ways you can evade the need to go to 3 dimensions to get rid of the "seam". Simplest in concept is to make the 2D "noise array" wider then your display. For example, let's say your display is 8 pixels circle and the noise array calculations are done on 16 wide array. Each display pixel is driven from 2 in the noise array (display #1 driven from noise array 1 & #9, #2 from 2 & 10, etc.) You taper the weights down toward the edges of the noise array (but the sum of the 2 weights always equals 1), blending the seams. The other way to do this is to do the noise calculations on a "cylindrical surface" (where the plane is rolled up so the left side and the right side come together). This is still a 2D surface, with animation being along the axis and pixel # around the circumference. A single stripe from this will have no seams. This will save calculations!

0
fungus amungus
fungus amungus

Reply 2 months ago

The calculations aren't too hard and only done once in setup. They assign x and y coordinates for each of the LEDs. This is just a matter of determining the angle between each LED (2 PI / # of LEDs) and going through a for loop to determine the x and y values with sine and cosine.

Not sure about the first method. Might need some more explanation on that one.

The second method is the same as what I'm doing here. In wrapping the plane into a cylinder, you're turning each line into a circular array of points.

And to be honest, this is a bit of overkill for a 16-LED circular array. As you can see in the video, for larger scales in the noise the seam is not such a dealbreaker. I first came up with this approach for a much larger circular array of about 100 LEDs.

Ultimately, I'm really using this as an example for talking about noise, and animation within noise, in general.

0
greyaria
greyaria

2 months ago

I love stuff like this, but I don't have access to a 3D printer. Is there any way to complete this project without one?

0
fungus amungus
fungus amungus

Reply 2 months ago

Definitely, anything that can diffuse the lights would work. This can even just be tissue paper. Or you can leave them bare, although I'd prefer not to. As for the light setup, you can try this out with a Circuit Playground Express. Has fewer LEDs, but super easy to start with.

1
beat_meier
beat_meier

2 months ago

This is a very useful and clear tutorial into perlin noise! Please more of that kind of instructable. I love it, thank you!

0
fungus amungus
fungus amungus

Reply 2 months ago

Thanks. I'm glad you liked it. I stumbled across Perlin noise through Coding Train videos and only after playing with it for a while did I see the usefulness of it. I was trying to get that understanding across faster here.