Canvas Fireworks Physics — Free From-Scratch Particle Explosion

Canvas Fireworks Physics · Animations · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

From-scratch physics
Gravity and drag as plain constants — no particle library.
Two particle types
A Rocket constructor and a Particle constructor, each simple.
Exponential drag
Velocity multiplied by DRAG each frame for a natural decel.
Randomized fade rate
Per-particle decay means bursts fade unevenly, not all at once.
Ring-buffer trails
Each rocket keeps its last 6 positions, alpha-ramped.
Motion-blur clear
A translucent fill instead of clearRect leaves soft afterimages.
Click to launch
Rockets fire from the clicked x position.
Self-throttling auto-loop
A timer launches new rockets, capped by total particle count.

About this UI Snippet

Canvas Fireworks Physics — Gravity, Drag, and Fade From Scratch

Screenshot of the Canvas Fireworks Physics snippet rendered live

Most confetti and firework effects on the web reach for a particle library. This snippet is the opposite exercise: rockets, trails, explosions, gravity, air drag, and fade are all implemented directly against the 2D canvas API with two small object constructors and a handful of physics constants — useful both as a working effect and as a compact reference for how particle systems actually work under the hood.

Two kinds of particle

A Rocket starts at the bottom of the canvas with an upward velocity and a randomly chosen target height; a Particle is spawned only at the moment of explosion, with a random angle and speed so it flies outward in every direction from the burst point. Keeping these as two distinct, tiny constructors (rather than one particle type with a mode flag) keeps each one's update logic — arc-and-detonate versus radiate-and-fade — easy to read in isolation.

Real gravity and drag, not a lookup table

Every particle's vertical velocity increases by a constant GRAVITY each frame, exactly like a real projectile under constant acceleration. DRAG (0.985) is multiplied into both velocity components every frame, so particles lose speed exponentially rather than linearly — this is what makes a burst's outer edge decelerate and hang for a moment before gravity visibly takes over, matching how real fireworks debris behaves far more convincingly than a fixed-duration animation would.

Life as a countdown, not a timer

Rather than tracking each particle's remaining time with Date.now() comparisons, every particle has a life value starting at 1 that decrements by a random decay each frame, and ctx.globalAlpha is set directly from that value before drawing. Because decay is randomized per particle, a single burst fades out unevenly — some sparks visibly outlast others — instead of every particle in an explosion vanishing on the same frame.

A trail from a short history array

Each rocket keeps its own trail array of its last six positions, and every frame draws all of them with alpha increasing toward the most recent point. This is the same "ring buffer of recent positions, alpha-ramped" technique used for cursor trails and comet effects — it produces a convincing motion streak with no extra state beyond an array you push to and shift from.

Fading the whole canvas, not clearing it

Instead of ctx.clearRect, each frame paints a translucent rgba(4,5,12,0.22) rectangle over everything. That leaves a faint afterimage of the previous frame rather than a hard cut, giving trails and bursts a soft motion-blur quality for almost no extra code. Compare this from-scratch approach with the library-driven canvas confetti burst, or pair it with a starfield backdrop for a night-sky celebration scene.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to walk through why the DRAG constant is applied multiplicatively every frame rather than subtracted, and how that produces exponential rather than linear deceleration — try changing DRAG from 0.985 to 0.95 and 0.999 and ask it to predict the visual difference before you run it. It's also useful for reasoning about the fade: ask why life and globalAlpha are tied together directly instead of using a CSS transition or a separate opacity tween, and why per-particle randomized decay matters for how a burst reads. For extensions, ask it to add a secondary "crackle" burst that spawns a few seconds after the main explosion at reduced scale, make rocket color match its eventual burst color visibly during the trail, or add a sound-trigger hook that fires on each detonation. It can also help you reason about performance — ask how the rockets.length + particles.length cap interacts with the auto-launch timer to keep the scene from growing unbounded. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "canvas fireworks" effect in plain HTML, CSS, and JavaScript using only the Canvas 2D API — hand-rolled particle physics, no particle or animation library.

Requirements:
- A full-bleed canvas over a dark night-sky background. Clicking anywhere on the page launches a rocket from that horizontal position; also auto-launch a rocket immediately on load and every roughly 1.4 seconds afterward via a timer, but only if the total number of active rockets plus particles is below a cap (e.g. 260) so the effect self-throttles instead of growing unbounded.
- A Rocket object: starts at the bottom of the canvas, has an upward initial velocity and a randomly chosen target height, keeps a short trailing array (its last ~6 positions) drawn each frame with alpha increasing toward the most recent point, and detonates (spawning particles, then being removed) once its upward velocity crosses zero or it reaches its target height.
- A Particle object created only at detonation: given a random angle and speed so it radiates outward from the burst point in every direction, with its own random decay rate controlling how fast its life value (starting at 1) counts down each frame.
- Real physics on every particle each frame: add a constant GRAVITY to vertical velocity (so particles arc and fall), multiply both velocity components by a DRAG constant just under 1 (so speed decays exponentially, not linearly), and set the particle's rendered alpha directly from its remaining life value so particles fade out, with particles removed from the array once life reaches zero.
- Instead of ctx.clearRect each frame, fill the canvas with a low-alpha dark rectangle (e.g. rgba(4,5,12,0.22)) so previous frames leave a fading afterimage rather than a hard cut, giving trails and bursts a soft motion-blur quality.
- Pick each rocket/burst's color randomly from a small palette array, spawn 60-100 particles per explosion, and keep the whole thing running smoothly via a single requestAnimationFrame loop that updates and draws both the rockets array and the particles array every frame.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA rocket auto-launches on load and the sky starts filling.
  2. 2
    Click anywhereA new rocket launches from that x position and climbs upward.
  3. 3
    Watch it detonateAt its random target height it explodes into 60-100 particles.
  4. 4
    Observe the fadeParticles lose speed to drag, fall to gravity, and fade at random rates.
  5. 5
    Let it auto-loopA timer launches new rockets every ~1.4s, capped by particle count.
  6. 6
    Tune the showChange GRAVITY, DRAG, COLORS, or the particle count per burst.

Real-world uses

Common Use Cases

Celebration and success states
A bigger moment than canvas confetti burst.
New Year / holiday landing pages
A full-screen night-sky celebration hero.
Product launch pages
Auto-loop fireworks behind a launch-day headline.
Event countdown finales
Trigger explode() manually when a countdown hits zero.
Physics teaching demos
A compact, readable gravity-and-drag reference example.
Night-sky scenes
Layer over a starfield backdrop.

Got questions?

Frequently Asked Questions

Everything here — rocket launch, arc, detonation, particle spawning, gravity, drag, and fade — is written directly against the 2D canvas API with two small constructors and a few numeric constants, with no external particle engine involved. The canvas confetti burst snippet wraps the canvas-confetti library instead; this one is the from-scratch version, useful when you want full control over the physics or want to see exactly how a particle system works.

Every frame, each particle's vx and vy are multiplied by DRAG (0.985), so velocity decays exponentially rather than dropping by a fixed amount. That produces a burst whose outer ring visibly slows and seems to hang for a moment before gravity pulls it down — much closer to how real firework debris moves through air resistance than a burst with no drag, which would keep expanding outward at a constant rate.

Each particle is given its own random decay value when it is created, and its life counter (starting at 1) drops by that amount every frame, directly driving ctx.globalAlpha. Because decay varies per particle, a single explosion does not vanish as one uniform block — some sparks linger visibly longer than others, the same way real embers burn out at different rates.

clearRect would wipe every trace of the previous frame instantly, leaving hard-edged dots with no sense of motion. Filling the canvas each frame with a low-alpha rgba(4,5,12,0.22) rectangle instead partially erases the previous frame, leaving a fading afterimage behind every moving trail and particle — a cheap way to get a motion-blur look without a shader or an offscreen buffer.

The auto-launch timer checks rockets.length + particles.length against a cap (260) before firing a new rocket, so the total particle count self-limits rather than growing without bound. Combined with particles being removed from the array once their life reaches zero, the scene reaches a steady-state particle count instead of accumulating indefinitely, which keeps frame time roughly constant during continuous play.