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.
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.
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.
X
we can look at average value of cells called A
, B
, C
and D
:
X | ||||
A | B | C | ||
D | ||||
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
.
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:
color
function will be as simple as this:
return 'rgb(' + color + ', 0, 0)';
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:
chroma.js
(see here) that helps a lot while working with colors and gradients.
linear-gradient
as choma.js
expression like this:
var gradient = chroma.scale(['#000000', '#000000', '#ffff00', '#ff8700', '#ff0000']).domain([0, 10, 15, 30, 100])
gradient(33).toString()
rgb
-style expression.
color
function and have completely new pallete.
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.
Sources: https://github.com/5minute/examples/tree/main/burning_fire
See live results:https://jsfiddle.net/2npqegax/