Fold.init() — global particle config

Fold.init({
  particles: {
    count:                 1200,    // total GPU particles
    modes:                 ['brownian', 'orbit'],
    boundsRadius:          0.85,    // world units (≈ 1215 CSS px)
    boundsStrength:        0.25,
    friction:              0.84,
    brownianStrength:      0.0004,
    attractorBaseStrength: 0.006,  // scaled by per-element strength attr
    depthColorStops: [
      { depth: -0.25, color: [0.05, 0.15, 0.90] },  // deep blue
      { depth:  0.00, color: [0.30, 0.85, 1.00] },  // cyan
      { depth:  0.25, color: [0.90, 0.45, 1.00] },  // violet
    ],
  },
});
Attract

Pull

A spring force draws nearby particles toward the element's center. Strength and radius are tunable per element.

attract zone!
<div
  data-foldpage-particles="attract"
  data-foldpage-particles-strength="0.9"
  data-foldpage-particles-radius="200">
</div>
Repel

Push

An inverse spring force pushes particles away — useful for hollow regions or interactive obstacle effects.

repel zone
<div
  data-foldpage-particles="repel"
  data-foldpage-particles-strength="0.8"
  data-foldpage-particles-radius="180">
</div>
Orbit

Spin

A tangential force in the XZ plane circles particles around the element's Y axis with a gentle inward spring to maintain distance.

orbit zone
<div
  data-foldpage-particles="orbit"
  data-foldpage-particles-strength="0.7"
  data-foldpage-particles-radius="210">
</div>
Vortex

Drain

XZ tangential spin combined with vertical pull toward the element — creates a funnel or drain effect that pulls particles into a column.

vortex zone
<div
  data-foldpage-particles="vortex"
  data-foldpage-particles-strength="0.75"
  data-foldpage-particles-radius="190">
</div>

contain

Stuck

Particles move freely inside the element's bounds. Any particle that crosses the boundary receives a pushback force proportional to how far it has overshot — fading to zero at radius pixels outside.

contain zone
<div
  data-foldpage-particles="contain"
  data-foldpage-particles-strength="1.5"
  data-foldpage-particles-radius="80"
  data-foldpage-particles-emit="200">
</div>
Brownian

Turbulence

A localized noise field — particles within the influence radius receive randomized impulses. Strength and frequency are tunable per element, independent of the global brownian mode.

brownian zone
<div
  data-foldpage-particles="brownian"
  data-foldpage-particles-radius="200"
  data-foldpage-particles-brownian-strength="0.0012"
  data-foldpage-particles-brownian-frequency="20">
</div>
Gravity

Field

A directional gravity field within the influence radius. The vector sets direction and magnitude — particles outside the radius are unaffected. Force falls off quadratically toward the edge.

gravity zone
<div
  data-foldpage-particles="gravity"
  data-foldpage-particles-radius="210"
  data-foldpage-particles-gravity="0,-0.0004,0">
</div>

<!-- combine with other modes -->
<div
  data-foldpage-particles="attract gravity"
  data-foldpage-particles-gravity="0,-0.0003,0">
</div>
Chain

Ribbon

Each particle is spring-linked to the next in the simulation texture, forming flowing ribbons and streamer effects. A longer trailLength makes the ribbon structure more visible. Global simulation mode — set via Fold.init.

global simulation mode
Fold.init({
  particles: {
    modes:         ['chain'],
    chainStrength: 0.002,  // spring between adjacent particles
    trailLength:   12,     // longer trails show ribbon structure
    friction:      0.92,
  },
});
Boid

Flock

Flocking simulation — particles attract nearby neighbours (cohesion), align velocities (alignment), and avoid crowding (separation). Composing with brownian breaks up lock-step synchronisation. Global simulation mode — set via Fold.init.

global simulation mode
Fold.init({
  particles: {
    modes:             ['brownian', 'boid'],
    boidRadius:        0.05,   // cohesion + alignment radius
    boidSepRadius:     0.03,   // separation avoidance radius
    boidCohesionStr:   0.008,
    boidAlignmentStr:  0.005,
    boidSeparationStr: 0.003,
  },
});
Spiral

Helix

Tangential XZ force combined with sinusoidal vertical oscillation — particles trace helical paths around springTarget. Raise spiralVertStr for tighter vertical coils. Global simulation mode — set via Fold.init.

global simulation mode
Fold.init({
  particles: {
    modes:            ['spiral'],
    spiralTangentStr: 0.0001,  // XZ tangential force
    spiralVertStr:    0.02,    // vertical oscillation amplitude
    springTarget:     [0, 0, 0],
    springStrength:   0.00005,
  },
});
Per-Mode Tuning

Compose

When an element lists multiple modes, each parameter attribute accepts a space-separated list aligned positionally to the mode tokens — so attract and brownian can have different strengths, radii, and friction values.

attract + brownian, tuned
<div
  data-foldpage-particles="attract brownian"
  data-foldpage-particles-strength="1.0 0.3"
  data-foldpage-particles-radius="180 360"
  data-foldpage-particles-emit="120"
  data-foldpage-particles-brownian-strength="0 0.0006"
  data-foldpage-particles-friction="0.96 0.88">
</div>

HTML-only config

data-foldpage-particles-target="body" promotes an element from a local attractor to a global config source. The primary attribute sets simulation modes; other attrs override global physics. No Fold.init() call is needed.

The hidden element below sets orbit + brownian simulation modes, friction 0.93, and a mild upward gravity — identical to the equivalent Fold.init call shown on the right.

<!-- target="body": element becomes a global config source.
     data-foldpage-particles sets simulation modes here,
     not attractor behaviors. No Fold.init() required. -->
<div
  data-foldpage-particles="orbit brownian"
  data-foldpage-particles-target="body"
  data-foldpage-particles-friction="0.93"
  data-foldpage-particles-brownian-strength="0.00015"
  data-foldpage-particles-gravity="0,0.00003,0">
</div>

<!-- Equivalent Fold.init() call: -->
Fold.init({
  particles: {
    modes:            ['orbit', 'brownian'],
    friction:         0.93,
    brownianStrength: 0.00015,
    gravity:          [0, 0.00003, 0],
  },
});

Gravity Drain

vortex spins particles around the element's Y‑axis. gravity applies a separate downward pull across a wider radius. The two forces compete — particles orbit but lose altitude, spiraling downward like water draining into a vortex. Each mode has its own tuned radius, strength and friction so the spin and the fall operate at independent scales.

vortex + gravity drain
<!-- vortex spins tight (r=140); gravity field is wider (r=340)
     and pulls harder — creating a downward drain spiral.
     Per-mode gravity uses semicolons to separate vectors. -->
<div
  data-foldpage-particles="vortex gravity"

  <!-- vortex: strong, tight   gravity: softer, wide -->
  data-foldpage-particles-strength="1.8 0.55"
  data-foldpage-particles-radius="140 340"

  <!-- emit/lifetime apply to the element, not per-mode -->
  data-foldpage-particles-emit="220"
  data-foldpage-particles-lifetime="7"

  <!-- per-mode gravity vectors (semicolon = mode separator):
       vortex slot: gentle downward nudge
       gravity slot: stronger pull that dominates at distance -->
  data-foldpage-particles-gravity="0,-0.0006,0; 0,-0.0028,0"

  <!-- vortex particles keep momentum; gravity drains it faster -->
  data-foldpage-particles-friction="0.945 0.905">
</div>

Turbulent Cage

contain creates an invisible boundary at the element's edges, bouncing particles back with a short, sharp falloff. brownian fills a wider zone with chaotic turbulence to agitate the trapped swarm. The result is a snow-globe effect — particles scramble frantically inside the cage but can't escape. Contain gets tight friction so collisions feel elastic; brownian gets looser friction so jitter accumulates.

contain + brownian cage
<!-- contain walls particles in with a 28 px falloff edge.
     brownian fills a huge radius (380 px) with turbulence.
     The cage traps; the turbulence keeps them pinballing. -->
<div
  data-foldpage-particles="contain brownian"

  <!-- contain: very strong wall   brownian: light nudges -->
  data-foldpage-particles-strength="2.2 0.15"

  <!-- contain: tight 28 px edge   brownian: wide 380 px field -->
  data-foldpage-particles-radius="28 380"

  <!-- emit/lifetime apply to the element, not per-mode -->
  data-foldpage-particles-emit="260"

  <!-- brownian params apply to brownian slot only -->
  data-foldpage-particles-brownian-strength="0 0.0018"
  data-foldpage-particles-brownian-frequency="0 4.5"

  <!-- contain bounces elastically; brownian lets energy build -->
  data-foldpage-particles-friction="0.82 0.94">
</div>

Spawn at element

Adding data-foldpage-particles-emit="N" initialises a dedicated slice of N particles near the element's position at mount time. The element also acts as an attractor to keep its particles in the neighbourhood. Multiple emitters split the particle count between them.

emitter A
emitter B
emitter C
<!-- Each element seeds N additional particles -->
<div
  data-foldpage-particles="attract"
  data-foldpage-particles-strength="0.6"
  data-foldpage-particles-radius="120"
  data-foldpage-particles-emit="150"
  data-foldpage-particles-lifetime="4">
  emitter A
</div>

<!-- lifetime overrides particleLifetime for this emitter's
     particles. 0 or omitted = immortal / use global setting. -->

Mouse attraction

When interactiveStrength is set, particles are attracted toward the cursor position in real time. Move the mouse across the scene to pull the field — no DOM element required. interactiveDistance controls how close (in world units) the cursor must be, and interactiveStaleDecay fades the force when the pointer is idle.

Fold.init({
  particles: {
    // attract toward cursor / touch
    interactiveStrength:   0.002,  // force magnitude
    interactiveDistance:   0.05,   // world-unit radius
    interactiveStaleDecay: 0.99,   // fade when pointer idle
  },
});

Full FoldParticlesConfig reference

Fold.init({
  particles: {
    // Simulation
    count:                  3000,     // GPU particle count
    modes:                  ['brownian', 'orbit'],  // global simulation modes
    friction:               0.78,     // velocity damping per frame
    brownianStrength:       0.0005,   // random impulse magnitude
    gravity:                [0, -0.00004, 0],
    springStrength:         0.00001,
    springTarget:           [0, 0, 0],

    // Bounds
    boundsShape:            'box',    // 'box' (default) | 'sphere'
    boundsAutoDoc:          true,    // auto-size box to document size; updates on resize
    boundsBox:              [0.56, 1.4, 0.45],  // explicit half-extents [x,y,z]; overrides boundsAutoDoc
    boundsCenter:           [0, -0.7, 0],        // explicit center; overrides boundsAutoDoc
    boundsRadius:           0.45,     // sphere radius, or Z depth half-extent for box
    boundsStrength:         1,
    boundsMode:             'constrain',  // 'constrain' | 'wrap'
    wrapDamping:            0,

    // Interactivity (cursor / touch)
    interactiveStrength:    0.002,
    interactiveDistance:    0.05,
    interactiveStaleDecay:  0.99,

    // DOM element attractors
    maxAttractors:          16,       // max simultaneous elements (hard cap: 16)
    attractorBaseStrength:  0.004,    // scaled by data-attr strength value

    // Rendering
    trailLength:            4,        // frames back for trail endpoint
    lineThickness:          1.0,      // pixels (screen-space)
    blendMode:              'normal', // 'normal' | 'additive' | 'multiply' | 'subtractive'
    depthColorStops: [
      { depth: -0.2, color: [0.05, 0.1, 1.0] },
      { depth:  0.0, color: [0.2,  1.0, 0.4] },
      { depth:  0.2, color: [1.0,  0.1, 0.05] },
    ],

    // Advanced modes
    chainStrength:          0.002,
    boidRadius:             0.05,
    spiralTangentStr:       0.0001,
  },
});

Data attribute reference

<!-- Behavior mode — marks element as a particle influencer -->
data-foldpage-particles="attract"   // spring pull toward element centre
data-foldpage-particles="repel"    // spring push away from element centre
data-foldpage-particles="orbit"    // 3D tangential spin (adapts to angular momentum)
data-foldpage-particles="vortex"   // XZ spin + vertical pull (funnel/drain)
data-foldpage-particles="contain"  // keep particles inside element bounds
data-foldpage-particles="brownian" // localized turbulence field
data-foldpage-particles="gravity"  // localized directional gravity field

<!-- Multiple modes: space- or comma-separated (each uses one attractor slot) -->
data-foldpage-particles="attract brownian"
data-foldpage-particles="vortex,gravity"

<!-- Per-element tuning -->
data-foldpage-particles-strength="0.5"             // 0–1, scaled by attractorBaseStrength
data-foldpage-particles-radius="150"               // influence radius in CSS px (all modes)
data-foldpage-particles-emit="100"                 // seed N particles near this element
data-foldpage-particles-lifetime="3.5"             // emitter lifetime in seconds (0 = immortal)

<!-- Brownian mode tuning (default: global brownianStrength / brownianFrequency) -->
data-foldpage-particles-brownian-strength="0.001"   // turbulence magnitude
data-foldpage-particles-brownian-frequency="20"     // step rate (higher = faster jitter)

<!-- Gravity mode tuning (default: global gravity vector) -->
data-foldpage-particles-gravity="0,-0.0003,0"      // x,y,z direction + magnitude

<!-- Namespace follows Fold.init({ namespace: '...' }) -->
<!-- e.g. namespace:'myapp' → data-myapp-particles="attract" -->

All modes — small vs large

Each pair carries only the mode attribute. Strength, radius, emit, and friction all derive from the element's rendered area via sizeFactor = clamp(√(area ÷ 60 000 px²), 0.1, 3.0).

Attract
sm

small

lg

large

<div data-foldpage-particles="attract"></div>
Repel
sm

small

lg

large

<div data-foldpage-particles="repel"></div>
Orbit
sm

small

lg

large

<div data-foldpage-particles="orbit"></div>
Vortex
sm

small

lg

large

<div data-foldpage-particles="vortex"></div>
Contain
sm

small

lg

large

<div data-foldpage-particles="contain"></div>
Brownian
sm

small

lg

large

<div data-foldpage-particles="brownian"></div>
Gravity
sm

small

lg

large

<div data-foldpage-particles="gravity"
     data-foldpage-particles-gravity="0,-0.001,0"></div>

Element-driven particles

Set count: 0 in Fold.init to suppress the global ambient cloud, then mark DOM elements with a mode attribute. Without an explicit emit attribute each element auto-seeds particles proportional to its area. No particle counts to manage — elements bring their own.

card A
card B
card C
Fold.init({
  particles: {
    count:      0,     // no ambient cloud
    friction:   0.89,
    trailLength: 7,
  },
});

<!-- No emit attr → auto-seeds per element area -->
<div data-foldpage-particles="attract">card A</div>
<div data-foldpage-particles="orbit">card B</div>
<div data-foldpage-particles="brownian">card C</div>

<!-- auto-emit = round(200 × sizeFactor) per element -->
<!-- use emit="0" to suppress a specific element    -->

Targeted particle groups — particles: [{…}]

Pass particles as an array to create independent particle systems. Each entry is scoped to a DOM element via target (CSS selector or HTMLElement) — bounds, attractor scanning, and world positioning are all isolated per group. An entry without target remains the global full-page system.

Fold.init({
  particles: [
    { /* global ambient system — no target */ },
    {
      target: '#particles-zone-a',  // CSS selector or HTMLElement
      count:  200,
      modes:  ['orbit', 'brownian'],
    },
    {
      target: '#particles-zone-b',
      count:  200,
      modes:  ['boid', 'brownian'],
    },
  ],
});

Zone A · #particles-zone-a

Orbit + Brownian

Three attract nodes inside the target element. The orbit global mode spins all particles around the zone centre; each node also draws nearby particles inward.

node 1
node 2
node 3

Zone B · #particles-zone-b

Boid + Brownian

A central repel hollows out the flock; attract nodes at either side anchor it. The boid global mode drives cohesion, alignment, and separation between all particles in the zone.

attract
repel
attract