Atmosphere✨ Premium

Fire Effect

2D canvas fire particles — lifetime-driven yellow→orange→red gradient, smoke trail via semi-transparent background fill, 3 spawns per frame from the base.

JSCanvasFire

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Atmosphere has 57 effects, including 10 free. Explore the category →

3 usage examples

Hero / Landing — 'Hot deals' launch page — Fire Effect example 1

① Hero / Landing — 'Hot deals' launch page

WhenLaunching a flash sale, a limited offer, or a promotional countdown where visual urgency must land at first glance.
WhyThe central fire column creates a strong visual anchor that draws the eye to the headline and CTA without a heavy image asset — zero network request.
SettingsDefaults: 3 spawns/frame, ±20 px spread, 3–9 px size. For a denser flame on a large screen, raise spawn to 5–6 by editing for (let n = 0; n < 3; n++).
Gamified result screen — Fitness or learning achievement — Fire Effect example 2

② Gamified result screen — Fitness or learning achievement

WhenDisplaying a final score, personal record, or unlocked milestone in a sport or learning app — fire visually anchors the sense of victory.
WhyFlames rise across the full screen height from the bottom, reinforcing the achievement emotion through the heat / energy / ascent metaphor — readable without a single extra word.
SettingsSpawn 5/frame (for (let n = 0; n < 3; n++)n < 5); spread widened to ±40 px (4080) for a wide sheet covering the screen; decay lowered to 0.01–0.02 (.01 * Math.random() + .01) for flames reaching the top of the canvas.
Flash sale background — Full-page countdown — Fire Effect example 3

③ Flash sale background — Full-page countdown

WhenA dedicated flash-sale or time-limited offer page: fire fills the entire background and turns the whole page into a visual urgency signal.
WhyThe flame is the entire scene, not an accent: it wraps the visitor from the first frame. Urgency works through atmosphere — no extra copy, no static icon needed.
SettingsSpread raised to ±60 px (40120) so fire covers the full page width; spawn 4/frame; decay lowered to 0.008–0.018 (.01 * Math.random() + .008) for very tall flames reaching the top of the screen.

How it works

On every frame, the canvas is not cleared with clearRect: a semi-transparent ctx.fillRect (rgba(10,10,15,0.15)) is applied instead. Previous frames persist at 85% opacity, producing the smoke trail effect with no extra buffer or storage.

3 new particles spawn each frame from the canvas bottom (y = height − 20 px), at a random x position ±20 px around center (t.w/2 + 40*(Math.random()−0.5)). Initial vertical velocity: −2 to −6 px/frame (rising); horizontal drift: ±1 px/frame. Deceleration is minimal: vy *= 0.99 — particles rise at near-constant speed until their lifetime runs out.

The color gradient is driven by remaining life: R = 255, G = floor(200 × life), B = floor(50 × life). Fresh (life = 1) → yellow-orange (255, 200, 50); at mid-life (0.5) → orange (255, 100, 25); near death → pure red fading out. Size and alpha decay in parallel: size *= 0.98 per frame, alpha = 0.8 × life. Each particle lives 33–100 frames (random decay 0.01–0.03).

The effect runs an unconditional requestAnimationFrame loop — no IntersectionObserver, no visibility check. There is no public API and no canvas resize handling after init: setupCanvas reads parent dimensions once at load.

Accessibility

  • prefers-reduced-motion absent: the effect does not check this OS preference. The animation always runs, even when 'Reduce Motion' is active. Handle manually with window.matchMedia('(prefers-reduced-motion:reduce)').matches before starting the RAF.
  • The <canvas> carries no aria-hidden, role, or aria-label in the provided code — screen readers may announce it as an empty interactive element. Add aria-hidden="true" on the canvas and role="img" + aria-label on the wrapper.
  • No keyboard or pointer interaction: the effect is purely decorative. It does not interfere with keyboard navigation elsewhere on the page.
  • The brightest particles (yellow-orange on near-black #0a0a0f) exceed a 7:1 contrast ratio — high visual legibility.
  • Information is encoded by color alone (particle age = color); no non-color alternative is provided. As a purely decorative effect, this is acceptable.

Browser compatibility

Requires Canvas 2D, requestAnimationFrame, and ES6 template literals — all modern browsers since 2016.

Chrome 49+✓ Full
Firefox 45+✓ Full
Safari 10+✓ Full
Edge 14+✓ Full
Mobile iOS✓ Full
Android Chrome✓ Full

Without Canvas 2D, the <code>&lt;canvas&gt;</code> stays empty and silent — no fatal JS error. The <code>.ps-canvas-wrap</code> container still occupies its space; add a CSS fallback background (e.g. an orange-to-red gradient) if visibility is required.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="demo-preview" role="img"
     aria-label="Rising fire particle animation in yellow to red">
  <div class="ps-canvas-wrap">
    <canvas class="ps-canvas" id="psFire"
            aria-hidden="true">
    </canvas>
  </div>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Spawn rate (JS: for loop) 3 per frame for (let n = 0; n < 3; n++) — raise to 5–8 for a dense wide flame, drop to 1–2 for a light flicker.
Horizontal spread (JS: 40) ±20 px from center 40 * (Math.random() − .5) — double to 80 for a wide flame, reduce to 10 for a needle-thin column centered on a button.
Rise speed (JS: vy) -2 to -6 px/frame 4 * -Math.random() - 2 — raise the multiplier (4→6) for faster particles; raise the offset (2→3) for a higher minimum speed.
Initial size (JS: size) 3–9 px 6 * Math.random() + 3 — first term sets variation range, second sets minimum. Large embers: 10 * Math.random() + 6.
Lifetime (JS: decay) 0.01–0.03 per frame .02 * Math.random() + .01 — lower for long-lived particles rising high (tall flame); raise for a short intense burst near the source.
Smoke trail (JS: background fillStyle) rgba(10,10,15,0.15) Semi-transparent overlay replacing clearRect. Lower alpha (0.06–0.10) for a long vaporous trail; raise it (0.30–0.50) to cut the smoke and show only fresh particles.
Color palette (JS: RGB formula) yellow→orange→red Replace rgba(${a},${l},${c},…) — blue-white fire: rgba(${Math.floor(200*i.life)},${Math.floor(200*i.life)},255,…); green fire: rgba(0,255,${Math.floor(200*i.life)},…).

FAQ

The IIFE binds to the fixed id psFire at load — only one instance runs natively. For multiple flames, extract the logic into a parameterized function: function initFire(id){ const t = setupCanvas(id); const e = []; (function n(){…requestAnimationFrame(n)})(); } then call initFire('fire1'), initFire('fire2'), etc. setupCanvas is a reusable global function.
The code provides no RAF handle and no public API. For control: store the RAF return value in an external variable (let rafId = requestAnimationFrame(n)) and cancel with cancelAnimationFrame(rafId). To restart, simply call the tick function again.
setupCanvas reads the parent's getBoundingClientRect() once at init. If the container changes size (window resize, accordion panel), recalibrate manually: canvas.width = wrap.offsetWidth; canvas.height = wrap.offsetHeight; and update t.w and t.h. Add a ResizeObserver on .ps-canvas-wrap to automate this.