Burning Fire in 5 minutes

About

This time we will learn how not draw burning fire using JavaScript. This project requires just a bit of mathematics and knowledge of CSS color schemes (things like gradients).
First time I saw such fire visualization in late 1990-s and it was implemented using Borland Pascal and used more or less the same approach.
Idea is that we should choose our pallete in a smart way so that it contains colors that make up a fire - mainly red, yellow and organge. And then we should calculate color of each pixel so that it give us some feeling of burning fire.
It turns out that it is more simple than it looks. Let's take a look how it can be done.

Live coding

Here is a live coding session that shows how it can be done.

Duration: 4:17 (coding - 4:09)

Disclaimer: never use this code in production. It was created for fun.

Breakdown

Let's break down the solution and comment on some complex or interesting things.

0:05 - as usual let's start with HTML template. This time our main element will be HTML canvas that will be used for drawing.
During initialization we'll use couple of parameters: width and height represent size of canvas. Also we are having pixelSize that will control the zoom leve of the canvas.
You can play around with these parameters to see how it will affect the end result. But I think having canvas of 200x120px is a reasonable default.

0:40 - we will need pixel function to draw individual "pixel" at position x and y using color color. Unfortunatelly HTML canvas does not have native support for pixel-level operations therefore we are tricking it by just filling a rectangle. Note that we are taking pixelSize into account here.

0:56 - defining frame function that will be used to draw a single frame of the fire. Note that we are using window.requestAnimationFrame function to instruct the browser that we want to call our frame function right before the browser will trigger repaint. This way we can ensure smooth visualization.
For now this function is empty - we will fill it a in a second.
You can get more details about window.requestAnimationFrame here.

1:07 - our fire model will be represented with a linear array that will contain width * height elements.
Each element of the array will represent the brightness of the pixel: 0 would mean black pixel with no intensity and 255 will represent the brigthest pixel. All values in between should be reasonably mapped to the colors.
For convenience we are defining 2 functions - get and set - to respectively get and set value of pixel at coordinates x and y.

1:27 - let's start implementation of frame function.
The fire goes from bottom to top - meaning that the bottom row should the brightest one and it should be random.
Filling every pixel of it with some random values from 0..255 interval.

1:43 - every other row will be based on the bottom rows with the following logics.

After some experiments it turns out the for the cell called X we can look at average value of cells called A, B, C and D:
X
ABC
D
Basically - 3 neighbour cells from the row below and one cell 2 rows below.
So for value of X we can just take (A + B + C + D) / 4. But you can play around with the the division factor to make flame higher or lower. I chose to go with 4.1.
Also please note that we are calculating brightness of the cells from top row and going down. As homework - how will the visualization behave if we go other way around (i.e. change direction of for loop for y)?

2:31 - color function is used to transform pixel's brightness to the actual color that we will draw on canvas.
You might have realized why we have chosen intensity interval from 0 to 255 - correct, it's used as red component in rgb color scheme like this:


This way our color function will be as simple as this:
return 'rgb(' + color + ', 0, 0)';
At this point in time we can check how it looks in the browser. You should see that we got nice visual effect of burning fire. Can we do anything better with it?

2:45 - let's try to add more color to the flame. Right now we used a pallete that is based on red color intensity.
We can use more complex gradients (actually - a combination of linear gradients) to get a mixture of red, yellow and organge.
After some experiments I came to the following linear-gradient:
linear-gradient(90deg, rgba(0,0,0,1) 10%, rgba(255,255,0,1) 15%, rgba(255,135,0,1) 30%, rgba(255,0,0,1) 100%);
It looks like this:


It has few differences comparing to original pallete: Also I found a great library called chroma.js (see here) that helps a lot while working with colors and gradients.
We can express CSS-based property for linear-gradient as choma.js expression like this:
var gradient = chroma.scale(['#000000', '#000000', '#ffff00', '#ff8700', '#ff0000']).domain([0, 10, 15, 30, 100])
And then query the color as needed (e.g. let's get a color for 33% gradient value):
gradient(33).toString()
It will give us rgb-style expression.
With that we can just replace our color function and have completely new pallete.
Check the results in the browser and you can notice the difference. If you feel that the fire is too yellow - you can play around with pallete to adjust it for your needs. I recommend using https://cssgradient.io/ to compose linear-gradient for your needs.

3:35 - as last step we can add some text over the fire.
HTML canvas has nice tools how to to it using drawText function.
Here again we had to play a bit with positioning of the text to get nice placement - ideally the fire should disolve just below the text.
You can check the browser to see the final result.

This concludes this mini-project. As you saw - using HTML canvas it is possible to draw nice visualizations. For me the most challenging part was to find the right pallete to give realistic fire effect.

Resources

Sources: https://github.com/5minute/examples/tree/main/burning_fire

See live results:https://jsfiddle.net/2npqegax/