A single campaign image becomes a 3D museum poster with soft paper folds, a slow specular light sweep across the surface, and a film-grade animated grain. Slow camera drift and pointer parallax keep it breathing.
import ExhibitionPoster from '@crazygl/hero-exhibition-poster';
export default function Page() {
return (
<ExhibitionPoster
posterImage="/your-campaign-image.jpg"
posterTilt={-13}
foldStrength={0.1}
lightSweepStrength={0.55}
/>
);
}posterImage (any AVIF/WebP/JPG/PNG; the plane rebuilds to the image's native aspect), posterScale, posterTilt.foldStrength, foldDensity, foldSpeed (0 freezes the folds).lightSweepSpeed, lightSweepStrength (the moving gallery-spotlight highlight).grainAmount (filmic noise; 0.015–0.03 is the sweet spot).cameraDriftStrength, pointerParallax (±2° yaw / ±1.5° pitch on the poster).bgTop, bgBottom (gallery-wall gradient), transparentBackground.npm install @crazygl/hero-exhibition-posterThe component takes the same props you see in the live customizer on the right — every default ships poster-quality.
import ExhibitionPosterHero from '@crazygl/hero-exhibition-poster';
export default function Landing() {
return (
<ExhibitionPosterHero />
);
}The wrapper renders static HTML on the server and only initialises the canvas after hydration, so search engines see your copy.
// app/page.tsx — works in SSR-first frameworks (Next, Remix, Astro, etc.)
'use client';
import ExhibitionPosterHero from '@crazygl/hero-exhibition-poster';
export default function Page() {
return (
<section>
<ExhibitionPosterHero
heading="Say hi."
subheading="Your new hero."
/>
<article>
<h2>Welcome</h2>
<p>Your content keeps its own voice below the hero.</p>
</article>
</section>
);
}