FireworksHdr
GPU fireworks background with a deterministic, DOM-free physics core (rockets, peony/willow/ring/glyph shells, sparks, embers, smoke) rendered by a WebGPU engine into a wide-gamut, extended-tone-mapping canvas so bursts and comet trails burn brighter than SDR white on HDR displays, degrading silently through a WebGL2 fallback and a soft-knee SDR path everywhere else
Preview
<script lang="ts">
import { FireworksHdr, type FireworksHandle } from "fancy-ui-svelte";
let handle = $state<FireworksHandle | null>(null);
function launch() {
// Fire a shell toward a random point in the upper half of the frame.
handle?.launch({ apex: { x: 0.3 + Math.random() * 0.4, y: 0.24 + Math.random() * 0.2 } });
}
</script>
<div class="relative h-80 w-full overflow-hidden rounded-lg border bg-black">
<FireworksHdr onReady={(h) => (handle = h)} />
<div class="pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-3">
<p class="text-sm text-white/60 select-none">Tap the sky, or send one up</p>
<button
type="button"
onclick={launch}
class="pointer-events-auto rounded-full border border-white/20 bg-white/10 px-4 py-1.5 text-sm font-medium text-white backdrop-blur transition hover:bg-white/20"
>
Launch a shell
</button>
</div>
</div>Installation
Usage
<script lang="ts">
import { FireworksHdr } from 'fancy-ui-svelte';
</script>
<FireworksHdr />Examples
Basic Usage
Ambient shells run on their own; tap the sky or use the handle to launch one. Brightest on an HDR display in a WebGPU browser, and safe (soft-knee SDR) everywhere else.
<script lang="ts">
import { FireworksHdr, type FireworksHandle } from "$lib/fancy-ui/fireworks-hdr";
let handle = $state<FireworksHandle | null>(null);
function launch() {
// Fire a shell toward a random point in the upper half of the frame.
handle?.launch({ apex: { x: 0.3 + Math.random() * 0.4, y: 0.24 + Math.random() * 0.2 } });
}
</script>
<div class="relative h-80 w-full overflow-hidden rounded-lg border bg-black">
<FireworksHdr onReady={(h) => (handle = h)} />
<div class="pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-3">
<p class="text-sm text-white/60 select-none">Tap the sky, or send one up</p>
<button
type="button"
onclick={launch}
class="pointer-events-auto rounded-full border border-white/20 bg-white/10 px-4 py-1.5 text-sm font-medium text-white backdrop-blur transition hover:bg-white/20"
>
Launch a shell
</button>
</div>
</div>Pattern shells
Shells that break into a figure: the built-in heart and star, or any closed outline you pass as points. The burst is cut from the figure, so it draws itself in the sky and then droops.
<script lang="ts">
import { FireworksHdr, type FireworksHandle, type ShellKind } from "$lib/fancy-ui/fireworks-hdr";
let handle = $state<FireworksHandle | null>(null);
// Any closed figure works: points are y-up around the origin, scale is free.
// This one is a lightning bolt drawn by hand.
const BOLT = [
{ x: 0.1, y: 1 },
{ x: -0.55, y: 0.05 },
{ x: -0.1, y: 0.05 },
{ x: -0.3, y: -1 },
{ x: 0.5, y: 0.1 },
{ x: 0.05, y: 0.1 },
];
function fire(shell: ShellKind, shapePoints?: { x: number; y: number }[]) {
handle?.launch({
apex: { x: 0.3 + Math.random() * 0.4, y: 0.3 },
shell,
shapePoints,
});
}
</script>
<div class="relative h-80 w-full overflow-hidden rounded-lg border bg-black">
<FireworksHdr ambientShells={["heart", "star", "peony"]} onReady={(h) => (handle = h)} />
<div class="pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-3">
<p class="text-sm text-white/60 select-none">Pattern shells</p>
<div class="pointer-events-auto flex gap-2">
{#each [["heart", "Heart"], ["star", "Star"]] as const as [kind, label] (kind)}
<button
type="button"
onclick={() => fire(kind)}
class="rounded-full border border-white/20 bg-white/10 px-4 py-1.5 text-sm font-medium text-white backdrop-blur transition hover:bg-white/20"
>
{label}
</button>
{/each}
<button
type="button"
onclick={() => fire("shape", BOLT)}
class="rounded-full border border-white/20 bg-white/10 px-4 py-1.5 text-sm font-medium text-white backdrop-blur transition hover:bg-white/20"
>
Custom
</button>
</div>
</div>
</div>Props
| Prop | Type | Default | Description |
|---|---|---|---|
palette | string[] | ["#ff2fd6","#a142ff","#3d5bff","#42cfff"] | Brand hues (hex); order-independent, sorted cool→warm for the shell sweep |
hdr | boolean | true | Opt into the GPU engine (WebGPU HDR first, then a WebGL2 fallback); when false, no engine boots |
exposure | number | 2.2 | Display exposure multiplier, clamped to [1, 4] |
ambient | boolean | true | Run the ambient auto-scheduler (Poisson-timed background shells) |
ambientIntensity | number | 0.35 | Ambient energy [0,1] — scales shell size |
interactive | boolean | true | Launch a shell toward the pointer on pointerdown |
quality | "auto" | "high" | "mid" | "low" | "auto" | Particle budget; auto picks from the render level and device pixel ratio |
respectReducedMotion | boolean | true | Force ambient off under prefers-reduced-motion (explicit launches still work) |
class | string | "" | Extra classes on the canvas wrapper |
ambientShells | ShellKind[] | - | Restrict the ambient scheduler to these shells, picked uniformly — including the pattern shells 'heart' and 'star'; defaults to a weighted peony/willow/ring mix ('glyph' and 'shape' are ignored, they need caller-supplied points) |
onReady | (handle: FireworksHandle) => void | - | Fired when the engine is live with an imperative handle, and again with a fresh handle after a recovered GPU context loss; never fired when no GPU renderer comes up |
onLost | () => void | - | Fired once when a GPU context loss could not be recovered; the component has torn itself down and any handle it gave out is inert |
Links
Related components
AnimatedBeam
Animated SVG beams connecting elements with smooth gradients
BorderBeam
Gradient beam that races around a container's border using a CSS offset-path animation, masked so only the border ring is painted, with configurable size, speed, anchor position, and gradient colors
Confetti
Confetti celebration effect powered by canvas-confetti with button trigger support