LGiki/react-firework-simulator

★ 0Forks 0TypeScriptGitHub ↗Compare

README

react-fireworks

A beautiful, interactive firework simulator React component. Features 12 shell types, canvas-based particle rendering, sound effects, dynamic sky lighting, and full customization -- all in a single drop-in component.

Ported from the original vanilla JS firework simulator by Caleb Miller.

Installation

npm install react-fireworks
# or
yarn add react-fireworks
# or
pnpm add react-fireworks
# or
bun add react-fireworks

Peer dependencies: react >= 18 and react-dom >= 18 (React 19 also supported).

Quick Start

import { FireworkSimulator } from 'react-fireworks';
import 'react-fireworks/styles';

function App() {
  return <FireworkSimulator style={{ width: '100vw', height: '100vh' }} />;
}

That's it -- a full-screen firework show with auto-launching sequences and interactive canvas (click to launch).

Examples

Minimal full-page background

<FireworkSimulator style={{ position: 'fixed', inset: 0 }} />

Custom configuration

<FireworkSimulator
  config={{
    shell: 'Willow',
    size: '4',
    quality: '3',         // QUALITY_HIGH
    skyLighting: '2',     // SKY_LIGHT_NORMAL
    autoLaunch: true,
    finale: false,
  }}
  style={{ width: '100%', height: 500 }}
/>

Custom background color

<FireworkSimulator
  config={{ backgroundColor: '#1a1a2e' }}
  style={{ width: '100vw', height: '100vh' }}
/>

Note: Works best with dark colors. The renderer uses mix-blend-mode: lighten, so very bright backgrounds will make dimmer particles invisible.

With sound enabled

<FireworkSimulator soundEnabled style={{ width: '100vw', height: '100vh' }} />

Programmatic control via onReady

import { FireworkSimulator, FireworkEngine } from 'react-fireworks';
import 'react-fireworks/styles';
import { useRef } from 'react';

function App() {
  const engineRef = useRef<FireworkEngine | null>(null);

  const launchOne = () => {
    engineRef.current?.launchShellFromConfig();
  };

  return (
    <>
      <FireworkSimulator
        paused
        onReady={(engine) => { engineRef.current = engine; }}
        style={{ width: '100%', height: 400 }}
      />
      <button onClick={launchOne}>Launch Firework</button>
    </>
  );
}

Props

<FireworkSimulator />

Prop Type Default Description
config Partial<EngineConfig> Device-aware defaults Engine configuration (merged with defaults). Changes are synced to the engine at runtime.
className string undefined CSS class name applied to the root container.
style React.CSSProperties undefined Inline styles for the root container. You must provide dimensions (width/height) since the component fills its container.
paused boolean false Pause the simulation.
soundEnabled boolean false Enable sound effects (launch whooshes, burst explosions, crackles).
launchSequence LaunchEntry[] undefined Custom launch sequence. Shells fire in order, cycling after the last entry. Omit for random mode.
onReady (engine: FireworkEngine) => void undefined Callback fired when the engine is initialized. Use this to store a reference for programmatic control.

Engine Configuration

The EngineConfig interface controls the simulation behavior. Pass any subset as config:

interface EngineConfig {
  quality: string;         // '1' (Low), '2' (Normal), '3' (High)
  shell: string;           // Shell type name (see Shell Types below)
  size: string;            // Shell size: '0'-'5' (maps to 3"-16")
  autoLaunch: boolean;     // Auto-fire sequences
  finale: boolean;         // Intense rapid-fire mode (requires autoLaunch)
  skyLighting: string;     // '0' (None), '1' (Dim), '2' (Normal)
  longExposure: boolean;   // Camera long-exposure effect
  scaleFactor: number;     // Zoom level: 0.5 - 2.0
  backgroundColor: string; // Hex color for background (default: '#000000')
}

Launch Sequence

The launchSequence prop accepts an array of LaunchEntry objects. When provided, the engine fires shells in the specified order, cycling back to the beginning after the last entry. Omit or pass undefined to use the default random mode.

interface LaunchEntry {
  shell: ShellName;  // Shell type to launch (required)
  size?: number;     // Shell size, same scale as EngineConfig.size (default: config.size)
  x?: number;        // Horizontal position, 0-1 (default: random)
  y?: number;        // Launch height, 0-1 (default: random)
  delay?: number;    // Delay in ms before next launch (default: based on shell star life)
}

Example -- a choreographed show:

<FireworkSimulator
  launchSequence={[
    { shell: 'Crysanthemum', x: 0.5, size: 4 },
    { shell: 'Ring', x: 0.3, delay: 1500 },
    { shell: 'Ring', x: 0.7, delay: 1500 },
    { shell: 'Palm', x: 0.5, size: 3 },
    { shell: 'Willow', x: 0.5, delay: 3000 },
  ]}
  style={{ width: '100vw', height: '100vh' }}
/>

Example -- only Crysanthemum and Palm, alternating:

<FireworkSimulator
  launchSequence={[
    { shell: 'Crysanthemum' },
    { shell: 'Palm' },
  ]}
  style={{ width: '100vw', height: '100vh' }}
/>

The launch sequence can also be set programmatically via the engine:

engine.setLaunchSequence([
  { shell: 'Ring', x: 0.5, delay: 1000 },
  { shell: 'Strobe', x: 0.5, delay: 1000 },
]);

// Clear the sequence to return to random mode
engine.setLaunchSequence([]);

Shell Types

The following shell types are available (pass as shell in config):

Shell Description
Random Randomly selects from all types (default)
Crysanthemum Classic spherical burst with optional pistil and streamers
Crackle Sparkling crackle effect
Crossette Stars that split into smaller bursts on death
Falling Leaves Slow-drifting leaf-like particles
Floral Flower-pattern burst
Ghost Invisible launch trail, colored burst
Horse Tail Heavy trailing sparks that fall like a horse's tail
Palm Palm-tree shaped burst
Ring Ring-shaped burst pattern
Strobe Blinking strobe effect
Willow Long-trailing willow effect

Access the full list programmatically:

import { shellNames } from 'react-fireworks';
// ['Random', 'Crackle', 'Crossette', 'Crysanthemum', ...]

Engine API

The FireworkEngine class provides programmatic control over the simulation. Obtain a reference via the onReady prop.

Methods

Method Signature Description
setConfig (config: Partial<EngineConfig>) => void Update engine configuration at runtime.
setPaused (paused: boolean) => void Pause or resume the simulation.
setSoundEnabled (enabled: boolean) => void Enable or disable sound effects.
setLaunchSequence (sequence: LaunchEntry[]) => void Set a custom launch sequence. Pass an empty array to return to random mode.
handleResize () => void Recalculate canvas dimensions. Called automatically on window resize.
launchShellFromConfig (event?: { x: number; y: number }) => void Launch a single firework. Pass canvas-relative coordinates to target a position, or omit for a random location.
destroy () => void Clean up all resources (animation frames, event listeners, particle pools). Called automatically on component unmount.

Exported Constants

Convenience constants for building config objects:

import {
  // Quality levels
  QUALITY_LOW,        // 1
  QUALITY_NORMAL,     // 2
  QUALITY_HIGH,       // 3

  // Sky lighting levels
  SKY_LIGHT_NONE,     // 0
  SKY_LIGHT_DIM,      // 1
  SKY_LIGHT_NORMAL,   // 2

  // Device detection flags
  IS_MOBILE,
  IS_DESKTOP,
  IS_HEADER,
  IS_HIGH_END_DEVICE,

  // Helpers
  getDefaultScaleFactor,
  shellNames,
} from 'react-fireworks';

State Management Hook

For advanced integrations (e.g., building a custom settings UI), the useFireworkStore hook provides state management:

import { useFireworkStore } from 'react-fireworks';

const {
  state,             // Full AppState object
  setPaused,         // (paused: boolean) => void
  setSoundEnabled,   // (enabled: boolean) => void
  setConfig,         // (config: Partial<EngineConfig>) => void
} = useFireworkStore(/* optional config overrides */);

AppState

interface AppState {
  paused: boolean;          // Is the simulation paused?
  soundEnabled: boolean;    // Are sound effects on?
  config: EngineConfig;     // Current engine configuration
}

Interaction

Mouse / Touch

  • Click or tap anywhere on the canvas to launch a firework at that position.
  • Drag along the bottom edge of the canvas to adjust simulation speed (0% - 100%).

Styling

The component ships with its own scoped CSS. All internal class names are prefixed with fw- to avoid conflicts with your app's styles.

Importing Styles

// Required -- import alongside the component
import 'react-fireworks/styles';

Container Sizing

The component fills its container. You must provide dimensions via style or className:

// Via inline style
<FireworkSimulator style={{ width: '100%', height: '100vh' }} />

// Via CSS class
<FireworkSimulator className="my-firework-wrapper" />
.my-firework-wrapper {
  width: 100%;
  height: 500px;
}

Overriding Internal Styles

All internal elements use the fw- prefix. Override them with higher-specificity selectors:

/* Remove stage border */
.my-app .fw-stage-container {
  border: none;
}

To change the background color, use config.backgroundColor rather than CSS overrides -- this ensures the sky lighting effect uses the correct base color.

CSS Class Reference

Class Element
fw-container Root wrapper
fw-stage-container Canvas sizing wrapper
fw-canvas-container Canvas element wrapper

Sound

Sound effects (launch whooshes, burst explosions, crackles) are loaded from a CDN on demand. They feature randomized pitch and volume for natural variation. Sound is off by default and can be enabled via the soundEnabled prop or programmatically:

// Via prop
<FireworkSimulator soundEnabled style={{ width: '100vw', height: '100vh' }} />

// Via engine reference
engine.setSoundEnabled(true);

Performance Tips

  • Object pooling: All particles (stars, sparks, burst flashes) use object pools, eliminating garbage collection pressure during animation.
  • Dual-canvas rendering: Trails render on a separate canvas from the foreground for efficient compositing with mix-blend-mode: lighten.
  • Device-aware defaults: Quality and scale automatically adjust based on screen size and navigator.hardwareConcurrency.
  • For constrained environments (low-end devices, small containers), set quality: '1' and a smaller scaleFactor.
  • The finale mode launches fireworks rapidly and may cause lag on lower-end hardware.

Browser Support

Works in all modern browsers supporting:

  • <canvas> 2D rendering context
  • requestAnimationFrame
  • ES2023 syntax (or transpiled by your bundler)
  • Web Audio API (optional -- for sound effects; degrades gracefully if unavailable)

Development

# Install dependencies
bun install

# Start dev server (demo app at localhost:5173)
bun dev

# Build the library (outputs to dist/)
bun run build

# Type-check
bun run lint

The src/main.tsx and src/App.tsx files serve as a development playground and are excluded from the library build.

Architecture

The library separates the imperative canvas engine from the React UI layer:

src/
  index.ts                    # Library entry point (public exports)
  firework.css                # Scoped component styles (fw- prefix)
  components/
    FireworkSimulator.tsx      # Main component: wires React props to engine
  engine/
    FireworkEngine.ts          # Main orchestrator: animation loop, physics, rendering
    Stage.ts                   # Canvas wrapper (sizing, DPR, pointer events, rAF)
    particles.ts               # Object-pooled particle systems (Star, Spark, BurstFlash)
    shells.ts                  # 12 shell type factories + Shell class
    sound.ts                   # SoundManager (preload, playback, throttling)
    constants.ts               # Colors, quality levels, device detection
    math.ts                    # Utility math functions
  hooks/
    useFireworkStore.ts        # State management hook for building custom UIs

Key design decisions:

  • The component renders only the canvas -- all UI (controls, settings) is left to the consumer.
  • The engine runs outside React's render cycle. React only manages the canvas container.
  • State flows one-way: React props -> useEffect -> engine setter methods. The engine never calls back into React.

License

MIT

Credits

Original firework simulation by Caleb Miller.

Contributors

LGiki

Issues