darkwatch/client/src/components/ParticleOverlay.tsx
Aaron Wood e2ce57527f feat: replace particle fire with Three.js FBM shader overlay
Switch fire effect from tsParticles to a full-screen Three.js WebGL
shader using layered FBM noise for volumetric-style flames rising from
the bottom of the screen. Also fix rain/embers canvas banding by
switching them to tsParticles fullScreen mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 22:17:36 -04:00

51 lines
1.3 KiB
TypeScript

import { useEffect, useState } from "react";
import Particles, { initParticlesEngine } from "@tsparticles/react";
import { loadSlim } from "@tsparticles/slim";
import type { AtmosphereState } from "../lib/atmosphereTypes";
import { getRainConfig, getEmbersConfig } from "../lib/particleConfigs";
// Module-level singleton so the engine is only initialised once
let enginePromise: Promise<void> | null = null;
function ensureEngine(): Promise<void> {
if (!enginePromise) {
enginePromise = initParticlesEngine(async (engine) => {
await loadSlim(engine);
});
}
return enginePromise;
}
interface ParticleOverlayProps {
atmosphere: AtmosphereState;
}
export default function ParticleOverlay({ atmosphere }: ParticleOverlayProps) {
const [ready, setReady] = useState(false);
useEffect(() => {
ensureEngine().then(() => setReady(true));
}, []);
if (!ready) return null;
const { rain, embers } = atmosphere;
return (
<>
{rain.active && (
<Particles
key={`rain-${rain.intensity}`}
id="particles-rain"
options={getRainConfig(rain.intensity)}
/>
)}
{embers.active && (
<Particles
key={`embers-${embers.intensity}`}
id="particles-embers"
options={getEmbersConfig(embers.intensity)}
/>
)}
</>
);
}