WebGL 2 fireworks with 15 distinct shell types, adaptive performance, multi-line text that shimmers and dissolves, and a choreographed Super Grand Finale. Zero dependencies. Tiny footprint.
Some libraries just draw circles and call them fireworks. Grand Fireworks renders genuine pyrotechnic geometry — crossettes that split mid-air, weeping willows that trail gravity-realistic embers, galactic spirals, and chrysanthemum shells that bloom. Built for landing pages, hero sections, celebrations, and anyone who refuses to ship a half-baked effect.
View source on GitHub · Download source ZIP · Open the GitHub Pages site · What changed in 1.6.2
WebGL vs Canvas
| Render path | Why it matters | Verdict |
|---|---|---|
| WebGL 2 | Handles denser particle clouds, brighter blending, and richer trails without making the show feel like it is dragging a refrigerator uphill. | The main stage. This is the path that keeps the fireworks feeling alive. |
| Canvas 2D | Works as a fallback, but it is still a 2D paint loop from another era. | Fine for compatibility. Not the path we want when the show needs to breathe. |
| Old-school DOM tricks | Easy to start, brittle fast, and nowhere near the particle budget needed for the good stuff. | Leave them in the museum. |
The engine is built to prefer WebGL 2 first, because that is where the real headroom lives. Canvas 2D is the emergency parachute, not the headline act.
Quick start
<script src="GrandFireworks.js"></script>
<script>
const fireworks = new GrandFireworks();
fireworks.start();
</script>The zero-argument constructor creates a transparent fullscreen overlay, uses the medium adaptive preset, does not start automatically, and runs indefinitely once started.
CDN
Load directly from GitHub via jsDelivr — no build step, no bundler required.
<script src="https://cdn.jsdelivr.net/gh/travisjmac/grand-fireworks-js@latest/dist/GrandFireworks-v1.6.2.min.js"></script>Experiences
Two focused ways to explore Grand Fireworks: a cinematic scene, and a hands-on feature lab.
Build it into anything
Grand Fireworks is designed to be dropped into an existing page, experience, or game without adopting a framework or rebuilding your UI. Create one instance, choose a container, and trigger shells, text, finales, or your own gameplay events from ordinary JavaScript. The engine handles the spectacle; your project keeps its own rules, controls, and personality.
Configuration
Build a reusable setup, preview it, save it locally, and load it back whenever you need it.
Default options
const fireworks = new GrandFireworks({
container: document.body,
mode: 'fullscreen', // 'fullscreen' | 'contained'
placement: 'overlay', // 'overlay' | 'background'
clip: true,
zIndex: 9999,
autoStart: false,
baseStyle: 'medium', // 'classic' | 'oldSchool' | 'thin' | 'medium' | 'bold' | 'spectacle'
duration: 0, // 0 = run indefinitely
durationMode: 'graceful',
maxFinishTime: 5000,
background: false,
transition: {
fadeIn: 500,
fadeOut: 400,
easing: 'ease-out',
clearOnHide: true
},
renderer: {
preferred: 'auto', // WebGL fullscreen; Canvas 2D contained
fallback: 'canvas2d',
preserveDrawingBuffer: 'auto' // retained when trails require it
},
sound: {
enabled: false,
volume: 0.3
},
visuals: {
trails: true,
trailFade: 0.115, // lower = longer trails
bloom: 1.25,
rocketExhaust: true,
explosionFlashes: true,
starChance: 0.08,
groupedSalvos: true,
secondaryCrackle: true,
zoom: 1 // 0.1–4; smaller = wider field of view
},
performance: {
preset: 'high', // low | medium | high | ultra
adaptive: true,
pauseWhenHidden: true,
pauseWhenOffscreen: true,
respectReducedMotion: true
},
show: {
intensity: 1,
openingSalvo: 6,
maxRockets: 6,
maxParticles: 1100,
launchInterval: 850,
launchSpread: 0.55, // starting area across the container
angleRange: 14, // random degrees left/right
angleStrength: 1, // multiplier for sideways motion
textRocketAngle: 0, // text rockets stay vertical
launchHorizon: 1, // launch area, measured in screen widths
zAngleRange: 25, // 0–45° depth drift for each shell
zAngleStrength: 0.8, // 0–3 multiplier for that drift
enabledTypes: 'all',
palettes: 'default'
},
finale: {
enabled: false,
type: 'super-grand-finale',
triggers: ['stop', 'duration'],
trails: 10, // secondary comet trails
trailFlight: 1100, // milliseconds before detonation
burstScale: 1,
maxWaitBeforeLaunch: 3000,
particleScale: 1,
finishDelay: 800,
maxDuration: 9000
},
textFirework: {
enabled: true,
renderMode: 'hybrid',
maxCharacters: 72,
maxCharactersPerLine: 24,
maxLines: 3,
overflow: 'ellipsis',
maxWidth: 0.82,
verticalPosition: 0.42,
lineHeight: 1.15,
fontFamily: 'system-ui, sans-serif',
fontWeight: 800,
fontSize: 92,
particleSpacing: 4,
particleSize: 2.5,
colors: ['#FFFFFF', '#FFD700', '#FF69B4'],
revealDuration: 250,
holdDuration: 1400,
dissolveDuration: 900,
dissolveStyle: 'sparkle',
fallDuration: 3200,
gravity: 0.025,
shimmer: true,
synchronizeExplosions: true,
exclusive: true
}
});Performance presets
| Preset | FPS | Particles | Rockets | Interval | DPR |
|---|---|---|---|---|---|
| Low | 30 | 1500 | 4 | 1000 ms | 1× |
| Medium | 60 | 3000 | 6 | 750 ms | 1.25× |
| High | 60 | 5000 | 10 | 550 ms | 1.5× |
| Ultra | 60 | 8000 | 14 | 350 ms | 2× |
Explicit show and performance values override preset values. maxRockets is always clamped to the agreed safety maximum of 25.
Ordinary rockets launch within the middle 55% of the display and travel at a random angle of up to 14° left or right. angleStrength controls how pronounced the fan is. Text rockets remain vertical by default, while the Super Grand Finale automatically uses a wider 18° fan.
renderer.preferred: 'auto' uses WebGL for fullscreen shows and reliable Canvas 2D rendering for contained shows. Explicitly use preferred: 'webgl2' with preserveDrawingBuffer: true only when you have tested WebGL in your particular nested layout.
Graceful stopping
await fireworks.stop();
// Stops new launches, finishes active fireworks,
// plays the finale when configured, fades out, then resolves.
fireworks.stop({ immediate: true }); // Emergency stop
fireworks.stop({ finale: false }); // Skip one configured finale
fireworks.finalize(); // Graceful stop with finaleDepth-staged horizon
visuals.zoom changes the visible field of view: values below 1 pull back to reveal more of the launch horizon, while values above 1 move closer. show.launchHorizon defines the total launch area in screen widths. Each shell is staged as near, middle, or far, so distance affects its apparent size, flight speed, burst height, stereo position, loudness, and the small sound delay on distant booms. Use zAngleRange and zAngleStrength to tune its depth drift. For a live reference, open Feature Demos.
Text fireworks
fireworks.launchText('I love you very much. You are the best.', {
renderMode: 'hybrid',
maxLines: 3,
colors: ['#FFFFFF', '#FF69B4', '#FFD700'],
holdDuration: 2000,
dissolveDuration: 1000
});
await fireworks.launchTextSequence([
'WISH BIG',
{ text: 'SHINE BRIGHT', overrides: { colors: ['#00BFFF', '#FFFFFF'] } },
'CELEBRATE!'
], { gap: 250, clearBetween: true });
fireworks.cancelTextSequence();Text wraps at word boundaries. Each line receives its own rocket; launch times are coordinated so the lines explode into one centered message. Set synchronizeExplosions: false for staggered line arrivals. Sequences dispatch start, item, end, and cancel events.
Methods
launch(), launchText(), launchTextSequence(), and launchFinale() work even when the regular show is not running. They temporarily wake the renderer, play only the requested effect, and fade away when it finishes—without restarting automatic launches.
| Method | Purpose |
|---|---|
start(options?) | Start the show. A run-specific duration may be supplied. |
stop(options?) | Gracefully finish and return a Promise. |
pause() / resume() | Pause or resume without launching a new show. |
launch(options?) | Launch one regular firework, including while stopped. |
launchText(text, options?) | Launch synchronized or staggered text rockets. |
launchTextSequence(messages, options?) | Show cancellable text messages one at a time and return a completion result. |
cancelTextSequence(options?) | Cancel the active message queue and optionally clear its current text. |
launchFinale() | Launch the standalone staged Super Grand Finale. |
finalize() | Gracefully end with the finale. |
clear() | Remove active fireworks. |
setOptions(partial) | Merge settings and apply live opacity and DPR changes. |
getOptions() | Return resolved configuration. |
getStats() | Return renderer, FPS, particles, rockets, state, and timer. |
destroy() | Remove layers, listeners, audio, observers, and renderer resources. |
Events
| Event | Dispatched when |
|---|---|
start | The show begins or resumes its main loop. |
pause | The show is manually paused. |
resume | The show is manually resumed after a pause. |
stop | The show has fully stopped and the canvas is hidden. |
textlaunch | A text firework is launched. |
textsequencestart | A text sequence begins. |
textsequenceitem | Each individual text message in a sequence fires. |
textsequenceend | The full text sequence completes. |
textsequencecancel | An active text sequence is cancelled. |
finalestage | A stage of the Super Grand Finale begins (carrier, trails, secondary-burst). |
rendererchange | The renderer falls back from WebGL to Canvas 2D. |
Fireworks Command v0.3
Starlight Intercept