ashish rajwani

realistic fire

Shaders

About

webgl realistic fire with mouse hover effects

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds realistic fire to your project and fits it to your theme.

Code

How realistic fire is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import React, { useEffect, useRef } from "react";

/**
 * ProceduralFireBackground
 * High-performance cinematic fire background (slow & smooth).
 * Production-safe WebGL implementation.
 */
const ProceduralFireBackground: React.FC = () => {
  const canvasRef = useRef<HTMLCanvasElement>(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;

    const gl = canvas.getContext("webgl", { antialias: false });
    if (!gl) {
      console.warn("WebGL not supported");
      return;
    }

    /* ===================== SHADERS ===================== */

    const vertexShaderSource = `
      attribute vec2 position;
      void main() {
        gl_Position = vec4(position, 0.0, 1.0);
      }
    `;

    const fragmentShaderSource = `
      precision highp float;

      uniform float u_time;
      uniform vec2 u_resolution;
      uniform vec2 u_mouse;

      float hash(vec2 p) {
        p = fract(p * vec2(123.34, 456.21));
        p += dot(p, p + 45.32);
        return fract(p.x * p.y);
      }

      float noise(vec2 p) {
        vec2 i = floor(p);
        vec2 f = fract(p);
        float a = hash(i);
        float b = hash(i + vec2(1.0, 0.0));
        float c = hash(i + vec2(0.0, 1.0));
        float d = hash(i + vec2(1.0, 1.0));
        vec2 u = f * f * (3.0 - 2.0 * f);
        return mix(a, b, u.x) +
               (c - a) * u.y * (1.0 - u.x) +
               (d - b) * u.x * u.y;
      }

      float fbm(vec2 p) {
        float v = 0.0;
        float a = 0.5;
        for (int i = 0; i < 6; i++) {
          v += a * noise(p);
          p *= 2.0;
          a *= 0.5;
        }
        return v;
      }

      void main() {
        vec2 uv = gl_FragCoord.xy / u_resolution.xy;
        float aspect = u_resolution.x / u_resolution.y;
        vec2 pos = uv;
        pos.x *= aspect;

        vec2 m = u_mouse;
        m.x *= aspect;

        float dist = distance(pos, m);
        float mouseInfluence = smoothstep(0.35, 0.0, dist) * 0.35;

        vec2 q = uv;
        q.y -= u_time * 0.18;          // 🔥 slower vertical movement
        q.x += sin(u_time * 0.15) * 0.03;

        float baseNoise = fbm(q * 3.0);
        float detailNoise = fbm(q * 6.0 + baseNoise);

        float flameShape = pow(1.1 - uv.y, 2.2);
        float strength = (baseNoise * 0.6 + detailNoise * 0.4 + mouseInfluence) * flameShape;

        vec3 col = vec3(0.0);
        col = mix(vec3(0.08, 0.01, 0.0), vec3(0.8, 0.15, 0.0), smoothstep(0.1, 0.45, strength));
        col = mix(col, vec3(1.0, 0.45, 0.0), smoothstep(0.45, 0.75, strength));
        col = mix(col, vec3(1.0, 0.95, 0.7), smoothstep(0.75, 1.0, strength));

        gl_FragColor = vec4(col * strength, 1.0);
      }
    `;

    /* ===================== HELPERS ===================== */

    const createShader = (type: number, source: string) => {
      const shader = gl.createShader(type);
      if (!shader) return null;
      gl.shaderSource(shader, source);
      gl.compileShader(shader);

      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        console.error(gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
      }
      return shader;
    };

    const program = gl.createProgram();
    if (!program) return;

    const vs = createShader(gl.VERTEX_SHADER, vertexShaderSource);
    const fs = createShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
    if (!vs || !fs) return;

    gl.attachShader(program, vs);
    gl.attachShader(program, fs);
    gl.linkProgram(program);

    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
      console.error(gl.getProgramInfoLog(program));
      return;
    }

    gl.useProgram(program);

    /* ===================== GEOMETRY ===================== */

    const buffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
    gl.bufferData(
      gl.ARRAY_BUFFER,
      new Float32Array([
        -1, -1,
         1, -1,
        -1,  1,
        -1,  1,
         1, -1,
         1,  1,
      ]),
      gl.STATIC_DRAW
    );

    const posLoc = gl.getAttribLocation(program, "position");
    gl.enableVertexAttribArray(posLoc);
    gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);

    const uTime = gl.getUniformLocation(program, "u_time");
    const uRes = gl.getUniformLocation(program, "u_resolution");
    const uMouse = gl.getUniformLocation(program, "u_mouse");

    /* ===================== RESIZE (RETINA SAFE) ===================== */

    const resize = () => {
      const dpr = window.devicePixelRatio || 1;
      const width = window.innerWidth;
      const height = window.innerHeight;

      canvas.width = width * dpr;
      canvas.height = height * dpr;
      canvas.style.width = `${width}px`;
      canvas.style.height = `${height}px`;

      gl.viewport(0, 0, canvas.width, canvas.height);
    };

    resize();
    window.addEventListener("resize", resize);

    /* ===================== MOUSE ===================== */

    let mouse = [0.5, 0.5];
    const onMouseMove = (e: MouseEvent) => {
      const rect = canvas.getBoundingClientRect();
      mouse = [
        (e.clientX - rect.left) / rect.width,
        1.0 - (e.clientY - rect.top) / rect.height,
      ];
    };

    window.addEventListener("mousemove", onMouseMove);

    /* ===================== RENDER LOOP ===================== */

    let raf = 0;
    const render = (time: number) => {
      gl.uniform1f(uTime, time * 0.0009); // 🐢 slower global time
      gl.uniform2f(uRes, canvas.width, canvas.height);
      gl.uniform2f(uMouse, mouse[0], mouse[1]);
      gl.drawArrays(gl.TRIANGLES, 0, 6);
      raf = requestAnimationFrame(render);
    };

    raf = requestAnimationFrame(render);

    /* ===================== CLEANUP ===================== */

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("mousemove", onMouseMove);
      window.removeEventListener("resize", resize);
      gl.deleteBuffer(buffer);
      gl.deleteProgram(program);
      gl.deleteShader(vs);
      gl.deleteShader(fs);
    };
  }, []);

  return (
    <canvas
      ref={canvasRef}
      style={{
        position: "fixed",
        inset: 0,
        background: "#000",
        zIndex: -1,
        display: "block",
      }}
    />
  );
};

export default ProceduralFireBackground;

More by ashish rajwani

View all
  • ASMR background

    ASMR background

    685
  • animated pattern cloud

    animated pattern cloud

    616
  • Balloons Pop background

    Balloons Pop background

    362
  • realistic fog background

    realistic fog background

    309
  • 3d animated stars background

    3d animated stars background

    3
  • Matrix background

    Matrix background

    1
  • tab pages

    tab pages

    1
  • sleek text input

    sleek text input

More in Shaders

View all
  • Glass Fold

    Glass Fold

    213
  • Neural Noise

    Neural Noise

    718
  • Water Caustic

    Water Caustic

    44
  • Shader Background

    Shader Background

    2K
  • Wave

    Wave

    688
  • Stripe like gradient-shader

    Stripe like gradient-shader

    265
  • Black Hole

    Black Hole

    23
  • Waves shaders Homlu UI

    Waves shaders Homlu UI

    92