In this post we will create two-dimensional flames like the one below:
Before we can create a roaring fire, we first need to define how a single flame behaves. For our purposes, a single flame is defined uniquely by four properties: x0, y0, v0 and θ. From these four defining properties, in addition to a globally-defined angular dispersion φ and flame height h both of which are the same for all flames, we can define some other useful variables as follows:
- Each flame has four points, consisting of (x,y) coordinate pairs, which will change over the course of the animation. These points will be called Left, Right, Top and Bottom.
- The Bottom point is initially set to (x0,y0).
- The Left point is initially set to (x0 + 0.5 h cos (θ - φ) , y0 + 0.5 h sin (θ - φ)).
- The Right point is initially set to (x0 + 0.5 h cos (θ + φ) , y0 + 0.5 h sin (θ + φ)).
- The Top point is initially set to (x0 + h cosθ , y0 + h sinθ).
- All four points have an associated velocity, which is initially set to (v0 cosθ , v0 sinθ) for all four.
In addition, we define an attraction force A which has constant magnitude but points left everywhere to the right of x0, and points right everywhere to the left of x0. We used a value of 2 for the magnitude of A, for a 20x20 canvas. We also used h = 5 and φ = 30°.
We step forward a single point on the flame by performing the following actions:
- Add the attraction force A to the point's velocity.
- Add the velocity to the position of the point.
We step forward the entire flame by doing the following:
- If the Top point is not in the bottom quarter of the canvas, we step forward the Left, Right, and Bottom points.
- If the Top point is in the top half of the canvas, we step it forward with a 50% probability and we pin it with a 50% probability. Pinning the Top point means that it will no longer be available for stepping forward.
Every time we step forward the flame, we render it by doing the following:
- Create a radial gradient centered at the Bottom point, with inner radius 0 and outer radius 2 h.
- Color stop 0 should be yellow and color stop 1 should be red.
- Set the fill style to the gradient you just created.
- Create a path and move to the Bottom point.
- Use a Bezier curve to move to the Top point, using the Left point for both of the control points.
- Use a Bezier curve to move back to the Bottom point, using the Right point for both of the control points.
- Close the path and fill it.
Creating the Fire
Now that we have successfully created a single flame from the variables (x0,y0,v0,θ), we can move on to creating an entire fire. To do so, we will need three new variables: (u,η,N). Note that u represents the maximum speed of flames coming out of it, η is the average angular dispersion of the flames, and N is the number of flames that are added per step. To generate the above animation, we used values of u = 5, η = 30°, and N = 10. With these new values, we do the following for each new flame added to the fire:
- Set x0 to half the canvas width and set y0 to the canvas height.
- Set v0 to a random value between 0 and u.
- Generate a random number r between 0 and 1, and let θ = r η + (π - η)/2
To step forward the fire a single time, create N flames of these specifications and add them to the current list of flames. Then step forward all of the flames in the list.
It should be noted that the fire will start small and will quickly grow large, so if you want to store a few images of the flame and cycle through them for an animation (as we did) you will notice that the flame will keep changing size every time it cycles. We recommend running the fire through a few paces, allowing it grow to its full size, before storing its images. This will give a flame of constant size.
This technique will create a realistic-looking fire in two dimensions. In the next post we will use canvas transformations to make the fire look three-dimensional, a technique that was used to great effect in the game Brimstone.