Skip to content

Particle Constellation

Show drifting, connected particles that gather around the pointer.

import { ParticleConstellation, SignalOptions } from '@youj/motion/backgrounds'

Experimental: what this label means

Installation & usage

npm i @youj/motion

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use it where the idea is connection: service graphs, networks, integrations, collaboration. It reads as a living diagram, and it's the one background that answers the pointer directly. Don't use it behind reading-length text, since the hairlines cross everything, and keep it to one per page.

The constellation fills its nearest positioned ancestor and paints behind your content, so make the container relative and isolate.

import { ParticleConstellation } from '@youj/motion/backgrounds'

export function GraphHero() {
  return (
    <section className="relative isolate min-h-[560px]">
      <ParticleConstellation />
      <p className="relative text-5xl font-semibold">See every dependency before it breaks</p>
    </section>
  )
}

Each particle drifts at its own slow velocity and wraps around the edges. Two particles closer than linkDistance are joined by a hairline that fades with distance. The number of particles follows the area: at density={1}, one particle per 9,000 square pixels, capped at 900. A phone and a wide display look equally full.

Reference

Propscolors, intensity, speed… (9)

Props

ParticleConstellation props
PropType
colors[particles: string, links: string, highlight: string]

[particles, links, highlight] CSS colors. Default: --color-fg-muted, --color-fg-subtle, --color-accent.

–
intensitynumber

Opacity of particles and links, 0–1.5.

default 1

1
speednumber

Drift speed multiplier.

default 1

1
densitynumber

Particles per area, relative. 1 ≈ one particle per 9 000 CSS px².

default 1

1
linkDistancenumber

Max link length in CSS px.

default 130

130
pointer'attract' | 'repel'

How particles respond to the pointer.

default 'attract'

'attract'
interactiveboolean

Pointer reactivity.

default true

true
signalsboolean | SignalOptions

Pulses of light that travel along the links from particle to particle, sometimes triggering the next one, like neurons firing. true uses the default rainbow (mint, sky, violet, orange; brighter on a dark canvas). Off by default; none under reduced motion.

default false

false
classNamestring–
PerformanceIt's drawn with Canvas 2D, not WebGL

Performance

It's drawn with Canvas 2D, not WebGL. A spatial grid sized to the link distance means each particle only checks its neighboring cells, and links are batched into a handful of strokes per frame by opacity. The canvas renders at a device-pixel ratio capped at 2. The loop pauses when the constellation is offscreen or the tab is hidden, and it stops and cleans up on unmount. If the browser can't create a 2D context, it falls back to a static CSS wash of the highlight color.

MotionDrift, pointer pull, pointer strength · reduced motion

Motion

Motion tokens
MomentTokens
Driftper-particle velocity, wrapping at the edges
no token

speed ÷ time scale

speed ÷ time scale
Pointer pullattract or repel within reach, then relax
no token

per-frame physics

per-frame physics
Pointer strengtheases in and out with the pointer
no token

smoothed per frame

smoothed per frame

Reduced motion

Draws one fixed, seeded constellation: the same composition every time. There's no drift and no pointer response, and it redraws only when the size, the props or the theme change.

Reduced motion

The constellation shows a fixed composition from a seeded layout, so it's the same on every visit and in every screenshot. The particles and links keep their colors and density. Only the drift and the pointer response are gone. Switch on Reduced motion in any preview above to compare.

AccessibilityARIA

Accessibility

  • It's decorative: the element is aria-hidden and ignores pointer events.
  • Hairlines cross whatever sits above them. Keep a quiet pool of canvas behind the copy, as the hero preview does, or lower intensity, and check your text's contrast there.
  • The drift is slow and stops offscreen. Under reduced motion the field doesn't move at all.