ambient-effects
v0.3.0
Published
Ambient canvas effects for Next.js pages — fireflies, snow, bubbles, confetti, and more.
Maintainers
Readme
ambient-effects
A lightweight library of ambient canvas effects for Next.js pages. Drop it in and pick an effect — fireflies, snow, bubbles, confetti, constellation, stardust, embers, leaves, rain, flow, spooky, neon, wind, or sunlight. Every effect reacts to the pointer, fires on click, and bends with scroll, matching the reference demo exactly.
Built for Next.js 16 + React 19 + TypeScript. Runtime dependencies beyond React: clsx and tailwind-merge only (used by the cn() class helper).
Current release: v0.2.0 — full interaction layer (pointer, click, scroll, arrow keys), faithful port of all 10 effects including Rain puddles and Flow trails, src/index.ts barrel exported correctly, shadcn-style docs at /about. See CHANGELOG.md for the complete list.
Quick start
npm install ambient-effectsThen use the component in any client-rendered page:
import { AmbientSurface } from "ambient-effects";
export default function Page() {
return (
<main style={{ height: "100vh", overflow: "hidden" }}>
<AmbientSurface defaultEffect="fireflies" />
</main>
);
}Install
npm install ambient-effectsOr install from source:
git clone https://github.com/Muhammad-waqar-uit/ambient-effects
cd ambient-effects
npm install
npm run devOpen http://localhost:3000 — / is a verbatim replica of the reference "10 Ambient Page Effects" page (fixed canvas, glass dock, name label, hint), and /about is the docs landing page.
Usage
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| defaultEffect | EffectId | "fireflies" | Initial effect to display (uncontrolled). |
| effect | EffectId | — | Controlled effect. If provided, component is controlled. |
| onEffectChange | (effect: EffectId) => void | — | Callback when effect changes. |
| showControls | boolean | true | Whether to show the built-in effect selector. |
| fullscreen | boolean | false | When true, the canvas fills the viewport (fixed inset-0) instead of the parent container. |
| dark | boolean | true | Dark-first theme like the reference; toggling re-inits the current effect. |
| reduceMotion | boolean | undefined | Explicit override for the user's motion preference. When undefined, respects prefers-reduced-motion. When active, particle density is halved (animation continues, matching the reference). |
| className | string | — | CSS class applied to the canvas wrapper. |
| style | React.CSSProperties | — | Inline styles applied to the canvas wrapper. |
Effects
The built-in effects are:
| Effect | Description |
|--------|-------------|
| fireflies | Drifting glowing particles that seek the pointer; additive glow in dark mode. |
| snow | Layered snowfall with cursor repulsion, wind drift and scroll gusts. |
| bubbles | Rising translucent bubbles; click near one to pop it into a burst. |
| confetti | Falling colored rectangles with rotation; scatters away from the pointer. |
| constellation | Stars linked by lines, with extra connections reaching to the cursor. |
| stardust | Sparkling dust whose emitters track fast pointer movement. |
| embers | Rising glowing embers with additive blending and bottom-fade intensity. |
| leaves | Falling leaves with rotation, sway and wind-driven drift. |
| rain | Slanted rain streaks that pool into expanding ripple puddles; click for a large splash. |
| flow | A tint-trail flow field that swirls around the cursor. |
| spooky | Halloween lantern: cursor-carved low light, drifting ghosts, blinking red eye-pairs, sudden lightning flashes with apparitions (from "Morgan Hollow"). Click to strike. |
| neon | Glowing neon circuit strokes with realistic on/off flicker that intensify near the cursor. |
| wind | Gusts of wind streaks that swirl around the pointer and bend with scroll wind. |
| sunlight | Warm god-rays from a sun origin, floating dust motes, and a scroll-slanted haze. |
Interactions
| Gesture | Behavior | |---------| | Move | Cursor position and velocity are smoothed into particle physics; the hint fades after the first move. | | Click | Sparks burst at the pointer, nearby particles get shoved away (within 340px); bubbles pop and rain splits large puddles. | | Scroll | Scroll velocity slants particles; wind follows a sine of scroll progress. | | Arrow keys | ArrowRight/ArrowLeft cycle the ten effects. |
The reference page is 600vh tall so scroll interactions have room; give your page similar height when you use scroll-reactive behavior.
Theme
dark defaults to true (the reference starts dark and never reads prefers-color-scheme). Set dark={false} to start light; toggling the attribute re-initializes the current effect so lightness dimming applies immediately.
Fullscreen mode
<AmbientSurface defaultEffect="constellation" fullscreen />In fullscreen mode the wrapper gets fixed inset-0 z-50 and the canvas tracks window.innerWidth/innerHeight. Use this for a background layer behind your page content.
Respecting motion preferences
The component automatically reads prefers-reduced-motion when reduceMotion is left undefined. You can force a behavior:
<AmbientSurface reduceMotion={true} />When reduced motion is active, particle density is halved and animation continues — matching the reference's cnt() scaling rather than freezing to a static frame.
Developer experience
Scripts
| Command | Description |
|---------|-------------|
| npm run dev | Start the Next.js dev server (Turbopack). |
| npm run build | Production build. |
| npm run start | Start the production server. |
| npm run lint | Run ESLint. |
Quality checks
This project enforces the following gates before any change lands:
- TypeScript strict mode (
npx tsc --noEmit) - ESLint with
eslint-config-next(TypeScript + core web vitals rules) - Next.js production build (
npm run build)
Run all three locally before pushing:
npx tsc --noEmit && npm run lint && npm run buildPath alias
Source imports use the @/* alias mapped to ./src/*. For example:
import { AmbientSurface } from "@/lib/ambient/ambient-surface";This is configured in tsconfig.json and works in both the dev server and production build.
Package structure
ambient-effects/
├── src/
│ ├── index.ts # Public barrel export
│ ├── lib/
│ │ ├── utils.ts # cn() helper (clsx + tailwind-merge)
│ │ └── ambient/
│ │ ├── types.ts # EffectId, EffectProps, EffectDefinition, AmbientSurfaceProps
│ │ ├── ambient-surface.tsx # Main "use client" component
│ │ └── effects.ts # All 10 effect implementations
│ └── app/ # Next.js app (demo + entrypoint)
│ ├── page.tsx
│ ├── layout.tsx
│ └── globals.css
├── public/ # Static assets (favicon, SVGs)
├── next.config.ts
├── tsconfig.json
├── eslint.config.mjs
├── package.json
└── README.mdPublic API surface
The package exports from src/index.ts:
export { AmbientSurface, effects, getEffect } from "./ambient/ambient-surface";
export type {
EffectId,
EffectProps,
EffectFn,
EffectDefinition,
AmbientSurfaceProps,
} from "./ambient/types";AmbientSurface— the React component.effects— the array of allEffectDefinitionentries (useful for custom UI).getEffect(id)— look up an effect by id, with a safe fallback to fireflies.
Browser support
- Modern evergreen browsers (Chrome, Firefox, Safari, Edge — latest two versions).
- Requires
CanvasRenderingContext2DandrequestAnimationFrame. - Respects
prefers-reduced-motionautomatically.
Accessibility
- The canvas has
role="img"and anaria-label. - The effect selector is a real
<select>witharia-label. - Reduced-motion users get static frames automatically.
License
MIT — see LICENSE. Free to use, modify, and distribute, including in commercial projects.
Contributing
Contributions are welcome. Please read the guidelines below before opening a PR.
Adding a new effect
- Add the effect id to the
EffectIdunion insrc/lib/ambient/types.ts. - Implement the draw function in
src/lib/ambient/effects.ts. Follow the existing pattern:- Define a particle interface.
- Create a
createX()factory that returns initial particle state. - Implement
drawX(props: EffectProps & { particles: X[] }). - Register it with
withParticles("effectId", drawX)in theeffectsarray.
- Add the effect to the
effectsexport array. - Add it to the README effects table.
- Test it in the dev server — switch to it via the dropdown and verify it runs smoothly.
Reporting a bug
Open an issue at https://github.com/Muhammad-waqar-uit/ambient-effects/issues with:
- The effect name.
- Browser + OS.
- A minimal repro or a description of the visual glitch.
- Whether it happens in dev or production build.
Pull requests
- Keep PRs focused — one effect, one fix, one feature per PR.
- Run the quality checks (TypeScript + ESLint + build) before submitting.
- Update the README if you add or change public API surface.
- Add a changelog entry in
CHANGELOG.mdunder[Unreleased].
Changelog
See CHANGELOG.md.
Acknowledgments
- Built on Next.js and React.
- Styling via Tailwind CSS v4.
- Class merging via
clsxandtailwind-merge.
