Close Menu

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    What's Hot

    How to create Animated 404 Page not found using HTML and CSS

    29 July 2025

    How to create Reptile Interactive Cursor using HTML and JS

    27 July 2025

    How to create Cat Loading Animation using HTML and CSS

    23 July 2025
    Facebook X (Twitter) Instagram YouTube Telegram Threads
    Coding StellaCoding Stella
    • Home
    • Blog
    • HTML & CSS
      • Login Form
    • JavaScript
    • Hire us!
    Coding StellaCoding Stella
    Home - JavaScript - How to make On-Scroll Fire Transition effect using HTML CSS & JavaScript
    JavaScript

    How to make On-Scroll Fire Transition effect using HTML CSS & JavaScript

    Coding StellaBy Coding Stella12 July 2024Updated:12 July 2024No Comments8 Mins Read
    Share Facebook Twitter Pinterest LinkedIn Tumblr Reddit Email WhatsApp Copy Link

    Let’s create an On-Scroll Fire Transition effect using HTML, CSS, JavaScript, WebGL, and GSAP’s ScrollTrigger. This project will create a fiery transition effect that activates as the user scrolls down the page, providing an engaging and dynamic user experience.

    We’ll use HTML for the structure, CSS for basic styling, JavaScript to integrate WebGL for the fire effect, and GSAP’s ScrollTrigger to control the animations based on the scroll position.

    Let’s dive into building the On-Scroll Fire Transition effect. Whether you’re a beginner or an experienced developer, this project offers a challenging and rewarding way to enhance your web development skills and create an immersive user experience. Let’s ignite some creativity and make scrolling more exciting!

    HTML :

    This HTML code creates a web page with a dynamic, full-screen canvas that displays a fire transition effect when scrolling. The canvas uses WebGL and fragment shaders to control pixel colors and opacities based on inputs like scroll position, time, and screen dimensions. Perlin Noise is utilized to create smooth, organic patterns for the fire effect. The GSAP ScrollTrigger library is used to manage the scrolling animations. Additionally, links to Anime.js and other resources are provided for further reference. The overall setup involves minimal JavaScript for rendering and doesn’t rely on additional libraries for the core functionality.

    <!DOCTYPE html>
    <html lang="en" >
    <head>
      <meta charset="UTF-8">
      <title>On-Scroll Fire Transition (WebGL + GSAP ScrollTrigger)</title>
      <link rel="stylesheet" href="./style.css">
    
    </head>
    <body>
    <!-- partial:index.partial.html -->
    <div class="page">
        <div class="header">
            The Magic Behind It
        </div>
        <div class="content">
            <p>
                This HTML page you're viewing is enhanced with a full-screen <b>&lt;canvas&gt;</b> element.
                A fragment shader runs on this canvas, determining the color and opacity of each pixel.
                The shader takes several inputs: <b>scroll position (or animation state)</b>, <b>time</b>, and <b>screen dimensions</b>.
            </p>
            <p>
                Gathering <b>time</b> and <b>screen dimensions</b> is straightforward, but for <b>animation state</b> we leverage the <a href="https://animejs.com/documentation/" target="_blank">Anime.js</a> library.
            </p>
            <p>
                With these inputs ready, we feed them into the shader as uniforms.
                The WebGL component of this demo uses a minimal JS setup to render the fragment shader on a single, full-screen plane. No additional libraries are required.
            </p>
            <p>
                The shader utilizes <a href="https://thebookofshaders.com/12/" target="_blank">Perlin Noise</a> to create dynamic visual effects.
            </p>
            <p>
                Initially, we generate a semi-transparent mask to shape a glowing effect. This mask is created with low-scale Perlin Noise and uses the <b>scroll position</b> as a threshold.
                By adjusting the thresholds on the same Perlin Noise, we can
                <br>
                (a) gradually dim parts of the screen, darkening pixels before they become transparent
                <br>
                (b) create a border effect along the edges, which we use to apply a glowing outline
            </p>
            <p>
                The glow itself is produced using two Perlin Noise functions - one for the shape and one for the color. Both functions operate on a larger scale and are animated with the <b>time</b> input instead of the <b>scroll position</b>.
            </p>
    
            <p class="last-line">
                <a href="https://www.linkedin.com/in/johndoe/" target="_blank">LinkedIn</a> | <a href="https://codepen.io/johndoe" target="_blank">CodePen</a> | <a href="https://twitter.com/johndoe" target="_top">Twitter (X)</a>
            </p>
        </div>
    </div>
    
    
    <canvas id="fire-overlay"></canvas>
    <div class="scroll-msg">
        <div>Hello 👋</div>
        <div>scroll me</div>
        <div class="arrow-animated">&darr;</div>
    </div>
    
    <script type="x-shader/x-fragment" id="vertShader">
        precision mediump float;
    
        varying vec2 vUv;
        attribute vec2 a_position;
    
        void main() {
            vUv = a_position;
            gl_Position = vec4(a_position, 0.0, 1.0);
        }
    </script>
    
    
    <script type="x-shader/x-fragment" id="fragShader">
        precision mediump float;
    
        varying vec2 vUv;
        uniform vec2 u_resolution;
        uniform float u_progress;
        uniform float u_time;
    
        float rand(vec2 n) {
            return fract(cos(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);
        }
        float noise(vec2 n) {
            const vec2 d = vec2(0., 1.);
            vec2 b = floor(n), f = smoothstep(vec2(0.0), vec2(1.0), fract(n));
            return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y);
        }
        float fbm(vec2 n) {
            float total = 0.0, amplitude = .4;
            for (int i = 0; i < 4; i++) {
                total += noise(n) * amplitude;
                n += n;
                amplitude *= 0.6;
            }
            return total;
        }
    
        void main() {
            vec2 uv = vUv;
            uv.x *= min(1., u_resolution.x / u_resolution.y);
            uv.y *= min(1., u_resolution.y / u_resolution.x);
    
            float t = u_progress;
            vec3 color = vec3(1., 1., .95);
    
            float main_noise = 1. - fbm(.75 * uv + 10. - vec2(.3, .9 * t));
    
            float paper_darkness = smoothstep(main_noise - .1, main_noise, t);
            color -= vec3(.99, .95, .99) * paper_darkness;
    
            vec3 fire_color = fbm(6. * uv - vec2(0., .005 * u_time)) * vec3(6., 1.4, .0);
            float show_fire = smoothstep(.4, .9, fbm(10. * uv + 2. - vec2(0., .005 * u_time)));
            show_fire += smoothstep(.7, .8, fbm(.5 * uv + 5. - vec2(0., .001 * u_time)));
    
            float fire_border = .02 * show_fire;
            float fire_edge = smoothstep(main_noise - fire_border, main_noise - .5 * fire_border, t);
            fire_edge *= (1. - smoothstep(main_noise - .5 * fire_border, main_noise, t));
            color += fire_color * fire_edge;
    
            float opacity = 1. - smoothstep(main_noise - .0005, main_noise, t);
    
            gl_FragColor = vec4(color, opacity);
        }
    
    </script>
    <!-- partial -->
      <script src='https://unpkg.com/gsap@3/dist/gsap.min.js'></script>
    <script src='https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js'></script><script  src="./script.js"></script>
    
    </body>
    </html>
    

    CSS :

    This CSS styles an HTML page with a full-screen fire effect. The main components include the body, page, scroll message, and canvas. The body and html elements are reset to have no margins or padding, and a sans-serif font is used. Links inherit the default color.

    The .page class centers its content and covers the entire viewport height with a minimum height of 180vh. The header is centered with uppercase text and a large font size. The content section is limited to 800px in width with padding for spacing. Selected text has a custom highlight color.

    The .scroll-msg section is fixed and centered on the screen, showing a scroll prompt. The canvas with ID fire-overlay is fixed, covering the full screen to display the fire effect. The animated arrow below the scroll message bounces up and down continuously.

    body, html {
        margin: 0;
        padding: 0;
        font-family: sans-serif;
        font-size: 20px;
        color: #3d3d3d;
    }
    
    a {
        color: inherit;
    }
    
    .page {
        width: 100%;
        min-height: 180vh;
        display: flex;
        flex-direction: column;
        align-items: center;
        opacity: 0;
    }
    
    .page .header {
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 40px;
        text-transform: uppercase;
        width: 100vw;
        margin-top: 20vh;
        height: 25vh;
    }
    
    .page .content {
        max-width: 800px;
        padding: 10px;
    }
    
    .page .last-line {
        text-align: right;
        padding-top: 1em;
    }
    
    .page ::-moz-selection {
        background: #F7C02D;
    }
    
    .page ::selection {
        background: #F7C02D;
    }
    
    .scroll-msg {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100vh;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        pointer-events: none;
    	 padding-top: 2em;
    }
    
    .scroll-msg > div:nth-child(1) {
        margin-top: -10vh;
        padding-bottom: 1em;
        text-transform: uppercase;
        font-size: 2em;
    }
    
    canvas#fire-overlay {
        position: fixed;
        top: 0;
        left: 0;
        display: block;
        width: 100%;
        pointer-events: none;
    }
    
    .arrow-animated {
        font-size: 1em;
        animation: arrow-float 1s infinite;
    }
    
    @keyframes arrow-float {
        0% {
            transform: translateY(0);
            animation-timing-function: ease-out;
        }
        60% {
            transform: translateY(50%);
            animation-timing-function: ease-in-out;
        }
        100% {
            transform: translateY(0);
            animation-timing-function: ease-out;
        }
    }

    JavaScript:

    This JavaScript code animates a fire effect on a WebGL canvas element based on scrolling using GSAP. It sets up the canvas and shaders, adjusts the fire effect parameters as the user scrolls, and ensures the canvas resizes correctly with the window. The animation is smoothly rendered in real-time by continuously updating the canvas with requestAnimationFrame.

    const canvasEl = document.querySelector("#fire-overlay");
    const scrollMsgEl = document.querySelector(".scroll-msg");
    
    const devicePixelRatio = Math.min(window.devicePixelRatio, 2);
    // const devicePixelRatio = 1;
    
    const params = {
        fireTime: .35,
        fireTimeAddition: 0
    }
    
    let st, uniforms;
    const gl = initShader();
    
    st = gsap.timeline({
        scrollTrigger: {
            trigger: ".page",
            start: "0% 0%",
            end: "100% 100%",
            // markers: true,
            scrub: true,
            onLeaveBack: () => {
                // params.fireTimeAddition = Math.min(params.fireTimeAddition, .3);
                // gsap.to(params, {
                //     duration: .75,
                //     fireTimeAddition: 0
                // })
            },
        },
    })
    	 .to(scrollMsgEl, {
    	   duration: .1,
       	opacity: 0
        }, 0)
        .to(params, {
            fireTime: .63
        }, 0)
    
    
    window.addEventListener("resize", resizeCanvas);
    resizeCanvas();
    
    gsap.set(".page", {
        opacity: 1
    })
    
    
    function initShader() {
        const vsSource = document.getElementById("vertShader").innerHTML;
        const fsSource = document.getElementById("fragShader").innerHTML;
    
        const gl = canvasEl.getContext("webgl") || canvasEl.getContext("experimental-webgl");
    
        if (!gl) {
            alert("WebGL is not supported by your browser.");
        }
    
        function createShader(gl, sourceCode, type) {
            const shader = gl.createShader(type);
            gl.shaderSource(shader, sourceCode);
            gl.compileShader(shader);
    
            if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
                console.error("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
                gl.deleteShader(shader);
                return null;
            }
    
            return shader;
        }
    
        const vertexShader = createShader(gl, vsSource, gl.VERTEX_SHADER);
        const fragmentShader = createShader(gl, fsSource, gl.FRAGMENT_SHADER);
    
        function createShaderProgram(gl, vertexShader, fragmentShader) {
            const program = gl.createProgram();
            gl.attachShader(program, vertexShader);
            gl.attachShader(program, fragmentShader);
            gl.linkProgram(program);
    
            if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
                console.error("Unable to initialize the shader program: " + gl.getProgramInfoLog(program));
                return null;
            }
    
            return program;
        }
    
        const shaderProgram = createShaderProgram(gl, vertexShader, fragmentShader);
        uniforms = getUniforms(shaderProgram);
    
        function getUniforms(program) {
            let uniforms = [];
            let uniformCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
            for (let i = 0; i < uniformCount; i++) {
                let uniformName = gl.getActiveUniform(program, i).name;
                uniforms[uniformName] = gl.getUniformLocation(program, uniformName);
            }
            return uniforms;
        }
    
        const vertices = new Float32Array([-1., -1., 1., -1., -1., 1., 1., 1.]);
    
        const vertexBuffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
        gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
    
        gl.useProgram(shaderProgram);
    
        const positionLocation = gl.getAttribLocation(shaderProgram, "a_position");
        gl.enableVertexAttribArray(positionLocation);
    
        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
        gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
    
        return gl;
    }
    
    function render() {
        const currentTime = performance.now();
        gl.uniform1f(uniforms.u_time, currentTime);
    
        // if (st.scrollTrigger.isActive && st.scrollTrigger.direction === 1) {
        //     params.fireTimeAddition += .001;
        // }
    
        gl.uniform1f(uniforms.u_progress, params.fireTime + params.fireTimeAddition);
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    
        requestAnimationFrame(render);
    }
    
    function resizeCanvas() {
        canvasEl.width = window.innerWidth * devicePixelRatio;
        canvasEl.height = window.innerHeight * devicePixelRatio;
        gl.viewport(0, 0, canvasEl.width, canvasEl.height);
        gl.uniform2f(uniforms.u_resolution, canvasEl.width, canvasEl.height);
        render();
    }

    In conclusion, creating an On-Scroll Fire Transition using HTML, CSS, JavaScript, WebGL, and GSAP’s ScrollTrigger has been a thrilling and educational project. By combining these technologies, we’ve crafted a dynamic and visually striking effect that enhances user engagement as they scroll through the page. This project showcases the power of WebGL and GSAP in creating advanced animations and interactions for modern web designs.

    If your project has problems, don’t worry. Just click to download the source code and face your coding challenges with excitement. Have fun coding!

    Animation
    Share. Copy Link Twitter Facebook LinkedIn Email WhatsApp
    Previous ArticleHow to make Glassy Profile Card using HTML & TAILWIND
    Next Article How to make 3D Dot Preloader using HTML & CSS
    Coding Stella
    • Website

    Related Posts

    HTML & CSS

    How to create Animated 404 Page not found using HTML and CSS

    29 July 2025
    JavaScript

    How to create Reptile Interactive Cursor using HTML and JS

    27 July 2025
    HTML & CSS

    How to create Cat Loading Animation using HTML and CSS

    23 July 2025
    Add A Comment
    Leave A Reply Cancel Reply

    Trending Post

    Master Frontend in 100 Days Ebook

    2 March 202421K Views

    How to make Modern Login Form using HTML & CSS | Glassmorphism

    11 January 202420K Views

    How to make I love you Animation in HTML CSS & JavaScript

    14 February 202417K Views

    How to make Valentine’s Day Card using HTML & CSS

    13 February 202413K Views
    Follow Us
    • Instagram
    • Facebook
    • YouTube
    • Twitter
    ads
    Featured Post

    How to create Password Validator using HTML CSS & JavaScript

    30 October 2024

    How to make Clean Toast Notifications using HTML & CSS

    16 March 2024

    Top 5 Source Code Editors in 2024

    25 January 2024

    How to make Awesome Search Bar 2 using HTML & CSS

    4 April 2025
    Latest Post

    How to create Animated 404 Page not found using HTML and CSS

    29 July 2025

    How to create Reptile Interactive Cursor using HTML and JS

    27 July 2025

    How to create Cat Loading Animation using HTML and CSS

    23 July 2025

    How to create Animated Fanta Website using HTML CSS and JS

    20 July 2025
    Facebook X (Twitter) Instagram YouTube
    • About Us
    • Privacy Policy
    • Return and Refund Policy
    • Terms and Conditions
    • Contact Us
    • Buy me a coffee
    © 2025 Coding Stella. Made with 💙 by @coding.stella

    Type above and press Enter to search. Press Esc to cancel.

    Ad Blocker Enabled!
    Ad Blocker Enabled!
    Looks like you're using an ad blocker. We rely on advertising to help fund our site.
    Okay! I understood