fire

fire

by Csaba Hellinger

HTML

<canvas id="canvas" width="400" height="200"></canvas>

CSS

body {
  background: #222;
}

canvas {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}

JavaScript

const FLAMES = 14;
const FLAME_WIDTH = 20;
const TEMP_MIN = 200;
const TEMP_MAX = 255;

const canvas = document.getElementById('canvas');
const width = canvas.offsetWidth;
const height = canvas.offsetHeight;
const ctx = canvas.getContext('2d');
const fire = new Uint8ClampedArray(width * height).fill(0);

// generate color map
const stops = [
  [0,   0,0,0],
  [90,  255,0,0],  
  [130, 255,155,0],
  [230, 255,255,255],
  [255, 255,255,255]
];
let stop = -1;
const mapMix = (min,max,mix) => Math.round(min + (max - min) * mix);
const map = new Array(256).fill(0).map((x,index) => {
  if (stops[stop+1][0] === index) {
    stop += 1;
    return stops[stop].slice(1);
  }
  const [min, minR, minG, minB] = stops[stop];
  const [max, maxR, maxG, maxB] = stops[stop+1];
  const mix = (index-min) / (max-min);
  return [
    mapMix(minR, maxR, mix),
    mapMix(minG, maxG, mix),
    mapMix(minB, maxB, mix)
  ];
});

ctx.fillStyle = '#000';
ctx.fillRect(0, 0, width, height);

const MAX = 5000;
let counter = 0;

function draw() {
	// draw flame roots
  for (let f=0; f<FLAMES; f+=1) {  
    const flameX = Math.trunc(Math.random() * (width-FLAME_WIDTH));
    const flameLeft = (height-1)*width + flameX;  
    const flameRight = flameLeft + FLAME_WIDTH;
    const temp = TEMP_MIN + Math.random() * (TEMP_MAX-TEMP_MIN);
    for (let i=flameLeft; i<flameRight; i+=1) fire[i] = temp;
  }

  // blur up
  for (let y=0; y<height; y+=1) {
    const yIndex = y * width;
    const upIndex = yIndex - width;
    const downIndex = yIndex + width;    
    for (let x=0; x<width; x+=1) {
      fire[x + yIndex] = (
        fire[x   + upIndex] +       
        fire[x-1 + downIndex] + 
        fire[x   + downIndex] + 
        fire[x+1 + downIndex]
      ) >> 2;
    }
  }
  
  // write to canvas
  const imgData = ctx.createImageData(width, height);
  const data = imgData.data;
  for (i=0; i<width*height; i+=1) {
    const [r,g,b] = map[fire[i]];
    data[i*4] = r;
    data[i*4+1] = g;
    data[i*4+2] =...