Skip to content

Particle System ​

Fold includes a particle system that runs entirely on the GPU. Configure it with Fold.init(), with HTML data attributes, or both.


Index ​

Modes ↓ ​

  • Global — brownian · gravity · spring · orbit · chain · boid · spiral
  • Element — attract · repel · orbit · vortex · contain · brownian · gravity · boid

Fold.init() API ↓ ​

  • particles — Global config object passed to Fold.init()
    • Simulation — count, modes, friction, simSpeed, forces
    • Bounds — shape, auto-doc sizing, radius, strength, constrain vs wrap
    • Rendering — trails, line thickness, blend mode, depth color
    • Interactivity — cursor and touch influence
    • Attractors — attractor limit, particle budget, base force strength
    • Lifecycle — particleLifetime, emitter respawn
    • Debug — origin sphere and attractor overlays
  • Reference →

Data Attribute API ↓ ​

Reference ↓ ​


Global Configuration ​

Pass a particles object to Fold.init() to configure the particle system. All properties are optional — unspecified values use the defaults shown below.

js
Fold.init({
  particles: {
    // count omitted → auto-computed from page area (set 0 to suppress ambient cloud)
    modes:                 ['brownian'],
    friction:              0.89,
    boundsShape:           'box',
    boundsAutoDoc:         true,
    boundsStrength:        1,
    boundsMode:            'constrain',
    attractorBaseStrength: 0.004,
    trailLength:           7,
    brownianStrength:      0.0002,
    brownianFrequency:     11,
    depthColorStops: [
      { depth: -0.2, color: [0.05, 0.10, 1.00] },
      { depth:  0.0, color: [0.20, 1.00, 0.40] },
      { depth:  0.2, color: [1.00, 0.10, 0.05] },
    ],
  },
});

Simulation ​

count ​

Total number of ambient GPU particles — the global background cloud that moves independently of any DOM element. Each particle is drawn as a short trail. Higher counts increase GPU load but have no CPU cost.

Particles seeded by data-foldpage-particles-emit elements are additive — they are allocated on top of this count, not subtracted from it.

Auto-compute: When count is omitted from the particles config object, Fold measures the page area (window.innerWidth × document.body.scrollHeight) and computes an appropriate count (up to 5 000 at a 1440 × 5000 px reference page). This means minimal configs like Fold.init({ particles: {} }) get a sensible ambient cloud automatically.

count: 0: Explicitly accepted — zero ambient particles. Use this to disable the global cloud entirely and let DOM element emitters be the sole particle source. Note that false, null, or an absent count key are treated as "unset" and trigger auto-compute; only the literal value 0 suppresses ambient particles.

DefaultRange
(auto-computed from page area)0 or any positive integer
js
// Explicit count
Fold.init({ particles: { count: 1200 } });

// Suppress ambient cloud — particles only from data-attr emitters
Fold.init({ particles: { count: 0, friction: 0.89, trailLength: 7 } });

// Omit count — auto-sized to page area
Fold.init({ particles: { friction: 0.89 } });

modes ​

Array of modes active on the particle system. Multiple modes compose additively — all active modes apply their force to every particle each frame.

Default
['brownian']
js
Fold.init({ particles: { modes: ['brownian', 'gravity'] } });

See Modes for all available values and their behavior.


friction ​

Velocity damping factor applied each frame. 1.0 = no damping (particles coast forever). 0.0 = full stop every frame. Values near 0.75–0.90 give natural-feeling motion.

DefaultRange
0.890.0 – 1.0

simSpeed ​

Overall simulation speed — movement and particle lifetimes all scale together. simSpeed: 0.15 runs the simulation at 15% speed with no change to the character of the motion.

DefaultRange
1.00.0+ (values above 1.0 fast-forward)
js
Fold.init({
  particles: {
    simSpeed: 0.15,
  },
});

brownianFrequency ​

How many times per second the Brownian random-walk direction changes. At high values each particle gets a new random impulse nearly every frame — rapid, jittery noise. Lowering this means each kick lasts longer, producing smooth lazy drifts instead of high-frequency chatter.

This controls the temporal character of Brownian motion, independent of its strength.

DefaultRange
11Any positive number
js
// Smooth, slow-drifting Brownian — direction holds for ~0.5 s per kick
Fold.init({
  particles: {
    brownianFrequency: 2,
    brownianStrength:  0.001,
    modes: ['brownian'],
  },
});
ValueCharacter
60+Rapid jitter — new direction nearly every frame
11 (default)Gentle ambient drift — direction shifts roughly 11× per second
4–8Lazy drift — particles meander slowly between direction changes
1–2Long slow glide — each random kick lasts nearly a second

brownianStrength ​

Magnitude of the random impulse applied each frame when brownian mode is active. Larger values create more chaotic, energetic jitter. This is the primary "noise" knob for ambient motion.

Default
0.0002

gravity ​

World-space acceleration vector applied each frame when gravity mode is active. The default points slightly downward. Units are world-space per-frame² — values are typically very small.

Default
[0, -0.00004, 0]
js
Fold.init({ particles: { modes: ['gravity'], gravity: [0, -0.0001, 0] } });

springStrength ​

Spring coefficient pulling particles toward springTarget when spring or orbit mode is active. Used as the inward component in orbit mode too (strength × 0.5).

Default
0.00001

springTarget ​

World-space target point for spring and orbit modes. Defaults to the particle group's local origin [0, 0, 0].

Default
[0, 0, 0]

Bounds ​

The bounds volume keeps particles inside the visible page area. The default shape is a box automatically sized to the document dimensions — particles stay inside the live page width and height rather than spilling outside it.

boundsShape ​

Selects the bounds geometry.

ValueBehavior
'box' (default)An axis-aligned box sized to the document. Each axis is constrained or wrapped independently.
'sphere'A sphere centered at the particle group origin, sized by boundsRadius.
js
Fold.init({ particles: { boundsShape: 'sphere', boundsRadius: 0.6 } });

boundsAutoDoc ​

When true (the default), the box bounds are automatically sized to window.innerWidth × document.body.scrollHeight and re-applied whenever the document resizes. The box center is automatically placed at the vertical midpoint of the document in particle local space.

Set to false to use boundsBox and boundsCenter for fully manual control.

Default
true

boundsBox ​

Explicit half-extents [x, y, z] in world units for the box bounds. Overrides boundsAutoDoc when provided with non-zero values.

The relationship to CSS pixels is: worldUnits = cssPixels × 0.0007. So a 800 px half-width ≈ 0.56.

Default
(auto-computed from document)
js
Fold.init({
  particles: {
    boundsShape:   'box',
    boundsAutoDoc: false,
    boundsBox:     [0.56, 1.4, 0.45],  // [halfWidth, halfHeight, halfDepth]
  },
});

boundsCenter ​

Explicit center offset [x, y, z] of the box in particle local space. Overrides the auto-computed document center when provided with non-zero values.

Default
(auto-computed: document vertical midpoint)

boundsRadius ​

For boundsShape: 'sphere': radius of the containing sphere in world units.

For boundsShape: 'box': used as the Z-axis half-extent (depth of the particle slab) when boundsAutoDoc is computing the other dimensions.

The relationship to CSS pixels is: boundsRadius / 0.0007 ≈ pixels. So 0.45 ≈ 643 px radius.

Default
0.45

boundsStrength ​

How hard the boundary pushes back in constrain mode. For box bounds, this applies per-axis proportional to how far a particle has overshot the face. Higher values create a harder wall; lower values allow particles to drift further outside before returning.

Default
1

boundsMode ​

How particles behave when they reach the bounds volume.

ValueBehavior
'constrain' (default)A spring force pushes the particle back inward.
'wrap'The particle teleports to the opposite side of the volume. For box bounds each axis wraps independently; velocity is optionally damped by wrapDamping.
js
Fold.init({ particles: { boundsMode: 'wrap', wrapDamping: 0.5 } });

wrapDamping ​

Velocity multiplier applied once when a particle wraps. 0 = particles stop dead on wrap; 1 = velocity is fully preserved. Only used when boundsMode: 'wrap'.

Default
0

Rendering ​

trailLength ​

Number of frames back used to pick the trail endpoint. Higher values create longer trails. The trail fades from near-transparent at the tail to fully opaque at the head.

Default
7

lineThickness ​

Particle line thickness in screen-space pixels. The line expands perpendicular to its direction in clip space, so it always reads as a consistent thickness regardless of depth.

Default
1.0

blendMode ​

How particle colors are composited over the scene behind them.

ValueEffect
'normal' (default)Standard alpha blending.
'additive'Colors add together — bright on dark backgrounds, good for glowing effects.
'multiply'Colors multiply — darkens the scene.
'subtractive'Colors subtract from the scene.
js
Fold.init({ particles: { blendMode: 'additive' } });

depthColorStops ​

An array of { depth, color } stops defining a gradient mapped to each particle's Z position in world space. The color at each particle's current depth is interpolated between the two nearest stops.

depth is a world-space Z value (negative = farther from camera). color is a normalized RGB array [r, g, b] where each channel is 0.0–1.0.

js
Fold.init({
  particles: {
    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
    ],
  },
});

Default (5 stops):

depthcolor (RGB)approx.
-0.2[0.05, 0.10, 1.00]deep blue
-0.1[0.00, 0.80, 1.00]sky blue
0.0[0.20, 1.00, 0.40]green
0.1[1.00, 0.80, 0.00]amber
0.2[1.00, 0.10, 0.05]red

Interactivity ​

Cursor and touch position are mapped to a world-space point and used as a live attractor. The attraction is always active when interactiveStrength > 0 — no additional setup is required.

interactiveStrength ​

Force magnitude of the cursor influence. Set to 0 to disable cursor attraction entirely.

Default
0.002

interactiveDistance ​

The world-space radius around the cursor within which particles are fully influenced. Falls off as inverse-square beyond this distance.

Default
0.05

interactiveStaleDecay ​

When the cursor stops moving, the interactive force decays by this factor each frame. 0.99 = slow decay (force lingers for ~100 frames). 0.9 = fast decay (~10 frames).

Default
0.99
js
Fold.init({
  particles: {
    interactiveStrength:   0.003,  // stronger pull
    interactiveDistance:   0.08,   // wider reach
    interactiveStaleDecay: 0.95,   // force fades quickly when pointer is still
  },
});

DOM Attractors Config ​

maxAttractors ​

Maximum number of DOM element attractor slots active simultaneously. Each mode token in a space/comma-separated list on one element consumes one slot — data-foldpage-particles="vortex gravity" uses two slots. The maximum is 40.

DefaultHard cap
4040
js
Fold.init({
  particles: {
    maxAttractors: 20,  // lower to reduce GPU cost
  },
});

maxParticles ​

Cap on the total particle count — count (ambient) plus all data-foldpage-particles-emit values. If the total exceeds the cap, emitters lowest on the page get fewer particles until it fits.

Default
10000

10000 is light for any WebGL-capable GPU. Raise it for denser scenes, lower it for low-power devices.

js
Fold.init({
  particles: {
    maxParticles: 10000,  // total particle budget: ambient + all emitters combined
    count:        500,    // 500 ambient + up to 9500 emitter particles within budget
  },
});

attractorBaseStrength ​

Base world-space force magnitude for DOM element attractors at strength=1.0. Each element's data-foldpage-particles-strength value (0–1) is multiplied by this to get the final force.

Default
0.004

Lifecycle ​

particleLifetime ​

Global particle lifetime in seconds. When set to 0 (the default) particles are immortal and never respawn. When set to a positive value, each particle has a normalised age that advances by delta / lifetime per frame. When age reaches 1.0 the particle dies and immediately respawns:

  • Emitter-assigned particles (those seeded by a data-foldpage-particles-emit element) respawn near their assigned emitter's current DOM position. If the emitter also has data-foldpage-particles-lifetime, that value overrides this global setting for those particles only.
  • Free particles (no emitter assignment) respawn at a random point inside the bounds volume, creating a continuously refreshing ambient cloud.

Initial ages are randomised at startup to prevent mass-respawn on the first frame. On respawn each particle also receives a small random phase offset (up to 50% of its lifetime), so particles respawning near the same element do so in a rolling stagger rather than a synchronised burst — this keeps emitters producing a continuous flow even after many lifetime cycles. The particle's trail is additionally suppressed for trailLength frames while the history buffer fills with the new trajectory, preventing a ghost line connecting the spawn point to the previous death position.

js
Fold.init({
  particles: {
    count:            800,
    particleLifetime: 6,   // each ambient particle lives for 6 seconds then respawns
    modes:            ['brownian'],
  },
});

Pair with data-foldpage-particles-emit to keep particles flowing from specific elements:

html
<!-- 300 particles stream continuously from this headline -->
<h1 data-foldpage-particles="attract"
    data-foldpage-particles-strength="0.7"
    data-foldpage-particles-radius="180"
    data-foldpage-particles-emit="300">
  Hero Headline
</h1>
DefaultRange
0 (immortal)Any positive number (seconds)

Debug ​

debug ​

When true, renders diagnostic overlays in the 3D scene:

  • A red sphere at the particle group's local origin.
  • Wireframe spheres at each active DOM attractor position, scaled to the attractor's influence radius and color-coded by mode: green = attract, red = repel, blue = orbit, yellow = vortex.

Useful for verifying that attractor elements are positioned correctly relative to 3D content.

Default
false
js
Fold.init({ particles: { debug: true } });

Modes ​

Modes define how forces act on particles. Apply a mode globally so it affects every particle in the scene, or attach it to a specific element so it only influences particles within that element's radius. Forces from all active modes combine each frame.

Global scope — set via Fold.init({ particles: { modes: [...] } }) or data-foldpage-particles-target="body" in HTML.

Element scope — set via data-foldpage-particles="..." on any HTML element.

ModeGlobalElementDescription
brownian✓✓Random impulse each frame — ambient noise and jitter. Global: affects every particle uniformly. Element: localized turbulence, fading at the element edge.
gravity✓✓Directional acceleration. Global: constant pull everywhere. Element: directional field within the element's radius, falling off at the edge.
orbit✓✓Tangential spin force. Global: all particles orbit a fixed springTarget point. Element: nearby particles orbit that element's position.
boid✓✓Flocking — cohesion, alignment, and separation. Global: full-scene flocking. Element: localized flocking zone; intensity scales with global flocking parameters.
attract—✓Spring pull toward the element's center.
repel—✓Spring push away from the element's center.
vortex—✓XZ funnel spin with vertical pull and centripetal spring.
contain—✓Particles bounce off the element's CSS bounding box.
spring✓—Pulls every particle toward springTarget.
chain✓—Spring-links particles in sequence — ribbon or streamer effect.
spiral✓—Helical tangential XZ force with sinusoidal vertical oscillation.

The global modes array controls which global-scope modes are active. Modes compose additively — ['brownian', 'orbit'] applies both forces simultaneously to every particle.

ModeDescription
brownianRandom impulse each frame — ambient noise and jitter. The brownianStrength property controls the magnitude.
gravityConstant acceleration in the gravity vector direction. Good for falling particles or directional drift.
springSpring force pulling every particle toward springTarget. Particles cluster around the origin and oscillate.
orbitTangential force in the XZ plane circling particles around springTarget's Y axis, combined with a gentle inward spring. Creates circular cloud motion.
chainEach particle is spring-linked to the next one in sequence. Creates flowing ribbon or streamer effects. Configure chain tension via Fold.init.
boidFlocking simulation — particles attract nearby neighbors (cohesion), align velocities (alignment), and avoid crowding (separation). Configure search radius, cohesion, alignment, and separation strength via Fold.init.
spiralTangential XZ force combined with a sinusoidal vertical oscillation, creating helical or DNA-strand paths. Configure tangential force and vertical amplitude via Fold.init.

Mode parameters ​

ParameterDefaultUsed by
brownianStrength0.0002brownian
brownianFrequency11brownian
gravity[0, -0.00004, 0]gravity
springStrength0.00001spring, orbit, spiral
springTarget[0, 0, 0]spring, orbit, spiral
chainStrength0.002chain
boidRadius0.05boid (cohesion + alignment search radius)
boidSepRadius0.05boid (separation search radius)
boidCohesionStr0.008boid
boidAlignmentStr0.005boid
boidSeparationStr0.003boid
spiralTangentStr0.0001spiral
spiralVertStr0.0spiral (vertical oscillation amplitude)

Element Modes ​

Any HTML element inside a Fold.page target wrapper can influence the particle system by carrying data-foldpage-particles attributes. Element modes only affect particles within the element's radius, with forces falling off quadratically toward the edge.

NOTE Up to 40 elements can have active particle mode simultaneously; elements beyond the limit are ignored. Each mode token in a space/comma-separated list counts as one slot — "vortex gravity" on one element uses two slots.

data-foldpage-particles ​

Marks an element as a particle influencer and sets its mode(s). Accepts a space- or comma-separated list — each mode is registered as an independent slot on the same element, so the forces compose simultaneously.

ValueBehavior
attractSpring force pulls nearby particles toward the element's center.
repelSpring force pushes nearby particles away from the element's center.
orbit3D tangential force orbits particles around the element. The orbit plane adapts to each particle's angular momentum — particles can arc over the top or underneath as well as around the sides. A gentle inward spring maintains orbital distance.
vortexXZ tangential spin combined with a vertical pull and an inward centripetal spring — funnel effect that actively retains particles within the zone.
containParticles move freely inside the element's CSS bounding box. Any particle that crosses the boundary receives a per-axis pushback force proportional to how far it has overshot, fading to zero at radius pixels outside.
brownianLocalized turbulence field within the radius, falling off quadratically toward the edge. Configure with data-foldpage-particles-brownian-strength and data-foldpage-particles-brownian-frequency.
gravityDirectional gravity field within the radius. Applies a constant acceleration vector, also falling off quadratically. Configure with data-foldpage-particles-gravity.
boidLocalized flocking zone — particles near the element flock together using cohesion, alignment, and separation forces. Force fades at the element edge. Configure flocking intensity via Fold.init boid parameters.
html
<!-- Pull particles toward this card's center -->
<div data-foldpage-particles="attract">
  Feature Card
</div>

<!-- Push particles away — hollow region effect -->
<nav data-foldpage-particles="repel">
  Navigation
</nav>

<!-- Keep emitted particles within the element's own bounds -->
<div data-foldpage-particles="contain"
     data-foldpage-particles-strength="1.5"
     data-foldpage-particles-radius="80"
     data-foldpage-particles-emit="200">
  Contained particle field
</div>

<!-- Combine modes: vortex spin with an additional orbital force -->
<div data-foldpage-particles="vortex orbit"
     data-foldpage-particles-strength="0.8"
     data-foldpage-particles-radius="200">
  Vortex + orbit zone
</div>

Each mode in the list occupies one attractor slot toward the maxAttractors limit. Unknown or misspelled mode tokens are silently ignored.

contain and radius: for the contain mode, data-foldpage-particles-radius is the falloff distance in CSS pixels — how far outside the element boundary the pushback force reaches before dropping to zero. A smaller radius (e.g. 40) creates a harder wall; a larger value (e.g. 150) produces a softer gradient that guides particles back from further away.


Per-Mode Arrays ​

When an element lists multiple modes you can supply space-separated values for strength, radius, friction, brownian-strength, brownian-frequency, and gravity. Values align positionally with the mode tokens in data-foldpage-particles. (emit and lifetime are element-level scalars and do not accept per-mode lists — see below.)

html
<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>

Here attract gets strength=1.0, radius=180; brownian gets strength=0.3, radius=360, brownian-strength=0.0006, friction=0.88. Both share the element's single emit=120 pool.

Padding rule: if fewer values are provided than there are modes, the last value repeats for all remaining modes.

Single value: a single value applies to all modes for strength, radius, friction, brownian-strength, and brownian-frequency.

emit and lifetime are not per-mode. They describe the element's particle pool, not individual force behaviors. Particles from that pool are affected by all modes whose radius covers them. Passing a list for these attributes has no additional effect — only the single scalar is used.

Gravity: because gravity already uses commas to separate x,y,z components, per-mode gravity vectors are separated by semicolons:

html
data-foldpage-particles-gravity="0,-0.001,0; 0,-0.003,0"
<!-- attract → (0,-0.001,0)   brownian → (0,-0.003,0) -->

A single vector without a semicolon still works as before and applies to all modes.


data-foldpage-particles-strength ​

Per-element strength multiplier in the range 0–1. Multiplied by the global base force strength (see attractorBaseStrength in Fold.init) to produce the final force magnitude.

Auto-scaling: When this attribute is omitted, the base value of 0.8 is automatically scaled by element size (see Size-aware auto-scaling). Larger elements get proportionally stronger pull/push without any configuration. Set the attribute explicitly to pin strength independent of element size.

Default (omitted)
auto (scales with element size)
html
<!-- Explicit strength -->
<div data-foldpage-particles="attract"
     data-foldpage-particles-strength="0.9">
  Strong attractor
</div>

<!-- Omit — strength auto-scales to element area -->
<div data-foldpage-particles="attract">
  Auto-scaled attractor
</div>

data-foldpage-particles-radius ​

Influence radius in CSS pixels. Particles outside this radius are unaffected. Force falls off quadratically from full strength at the element's center to zero at the radius edge.

Auto-scaling: When this attribute is omitted, the base value of 200 px is automatically scaled by element size. A small 100 × 60 px element gets a radius of ~63 px; a large 500 × 300 px element gets ~316 px. Set the attribute explicitly to lock the radius.

Default (omitted)
auto (scales with element size)
html
<!-- Explicit radius -->
<div data-foldpage-particles="orbit"
     data-foldpage-particles-radius="250">
  Wide orbit zone
</div>

<!-- Omit — radius auto-scales to element area -->
<div data-foldpage-particles="attract">
  Auto-scaled radius
</div>

data-foldpage-particles-emit ​

Seeds N additional particles near this element's position at mount time — on top of the global count set in Fold.init(). The element also acts as an attractor (using its data-foldpage-particles mode) to keep those particles in the neighbourhood after initialization.

Each emitter element contributes its own independent particle slice. Multiple emitters each add their own count rather than splitting a shared budget.

Auto-emit: When the attribute is absent entirely, the emit count is automatically computed from the element's rendered area:

autoEmit = clamp(round(200 × sizeFactor), 1, 500)

At the 300 × 200 px baseline this produces 200 particles. Smaller elements emit fewer; larger elements emit more, up to the 500-particle per-element cap.

Explicit 0: Setting emit="0" suppresses auto-emit and seeds no particles for this element. Use this when you want the element to act as a force field only, with no dedicated particle cloud.

ValueBehaviour
(absent)Auto-computed from element area, capped at 500
0No particles seeded — attractor-only
> 0Exactly N particles seeded near this element
html
<!-- Auto-emit — count derived from element size -->
<div data-foldpage-particles="attract">
  200 particles at 300×200 px; scales with area
</div>

<!-- Explicit count -->
<h1 data-foldpage-particles="attract"
    data-foldpage-particles-strength="0.6"
    data-foldpage-particles-radius="180"
    data-foldpage-particles-emit="200">
  Hero Headline
</h1>

<!-- Force field only — no particles emitted -->
<nav data-foldpage-particles="repel"
     data-foldpage-particles-emit="0">
  Navigation
</nav>

When particleLifetime > 0 (or when data-foldpage-particles-lifetime is set), emitter particles respawn near their assigned element's live DOM position rather than at a random point, creating a continuous stream from the element.


data-foldpage-particles-lifetime ​

Particle lifetime in seconds for this element's influence zone. Behavior depends on whether the element also carries data-foldpage-particles-emit:

With emit — per-emitter lifetime. Overrides the global particleLifetime for the particles seeded by this element only. Other emitters and free particles are unaffected.

Without emit — global lifetime override. Sets the lifetime for all free (non-emitter-assigned) particles on the page. This is the DOM-level equivalent of Fold.init({ particles: { particleLifetime: N } }) and only takes effect when no global particleLifetime is configured. Use this to make the ambient particle field cycle without needing emitters.

ValueBehavior
0 (default)Falls back to the global particleLifetime. If that is also 0, particles are immortal.
> 0Particles live for this many seconds, then respawn. Emitter particles respawn near the element; free particles respawn at a random point in the bounds volume.
html
<!-- All ambient particles cycle every 2 seconds (no emit needed) -->
<div data-foldpage-particles="repel"
     data-foldpage-particles-radius="180"
     data-foldpage-particles-lifetime="2">
  Repel zone — particles near here die and refresh
</div>
html
<!-- These 150 particles respawn every 4 seconds near this element -->
<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">
  Streaming emitter
</div>

Mix immortal ambient particles with short-lived emitter bursts:

html
<!-- All free particles cycle every 2 seconds -->
<div data-foldpage-particles="repel"
     data-foldpage-particles-lifetime="2">
  Repel zone
</div>

<!-- Emitter particles have their own independent 5-second cycle -->
<section data-foldpage-particles="attract"
         data-foldpage-particles-emit="200"
         data-foldpage-particles-lifetime="5">
  Hero section
</section>

data-foldpage-particles-brownian-strength ​

Turbulence magnitude for the brownian element mode. The per-element default (0.001) is intentionally stronger than the global ambient brownianStrength (0.0002) — localized turbulence fields are expected to be more energetic than the scene-wide drift.

Default (omitted)
0.001
html
<div data-foldpage-particles="brownian"
     data-foldpage-particles-radius="220"
     data-foldpage-particles-brownian-strength="0.0008"
     data-foldpage-particles-brownian-frequency="30">
</div>

data-foldpage-particles-brownian-frequency ​

Step rate of the per-element brownian noise. Higher values produce faster, more erratic jitter; lower values give slow, rolling turbulence. The per-element default (20) is higher than the global ambient frequency (11) for more perceptibly active localized zones.

Default (omitted)ValueEffect
2060+Rapid frame-rate jitter
20 (default)Brisk turbulence — direction shifts ~20× per second
10–15Smooth lazy drifts
4–8Slow rolling noise

data-foldpage-particles-gravity ​

Directional gravity vector for the gravity element mode, as three comma- or space-separated floats "x,y,z". The force is applied within the influence radius and falls off quadratically toward the edge. Defaults to the global gravity from Fold.init({ particles: { gravity } }).

Values are in world units per frame²; useful values are roughly 0.0001–0.001.

html
<!-- Downward pull within 250px of this element -->
<div data-foldpage-particles="gravity"
     data-foldpage-particles-radius="250"
     data-foldpage-particles-gravity="0,-0.0003,0">
</div>

<!-- Combine with attract — pull in AND push down -->
<div data-foldpage-particles="attract gravity"
     data-foldpage-particles-radius="200"
     data-foldpage-particles-gravity="0,-0.0002,0">
</div>

data-foldpage-particles-friction ​

Overrides the global friction value for particles that fall within this element's influence radius. Accepts a float in the range 0–1, where 0 stops particles instantly and 1 means no damping. The effective friction blends between the override and the global value, weighted by the quadratic falloff — particles at the centre of the element see the full override; particles at the edge of the radius blend back toward global friction.

Auto-scaling: When this attribute is omitted, friction is automatically adjusted by element size:

autoFriction = clamp(1 − (1 − globalFriction) / sizeFactor, 0.70, 0.97)

At the baseline element size the result equals the global friction exactly (no change). Smaller elements get higher friction (more damping, tighter clusters); larger elements get lower friction (less damping, more momentum). Set the attribute explicitly to bypass this scaling.

Unlike brownian and gravity, this attribute works with any mode and does not require a specific value in data-foldpage-particles.

html
<!-- Sticky zone — particles decelerate and gather near this element -->
<div data-foldpage-particles="attract"
     data-foldpage-particles-radius="200"
     data-foldpage-particles-friction="0.95">
</div>

<!-- Slippery zone — particles accelerate and coast near this element -->
<div data-foldpage-particles="orbit"
     data-foldpage-particles-radius="180"
     data-foldpage-particles-friction="0.55">
</div>

Size-aware auto-scaling ​

When strength, radius, emit, and friction attributes are all omitted, the particle system measures the element's rendered area at mount time and derives proportional values automatically. This means a single data-foldpage-particles="attract" attribute is all you need — Fold handles the rest.


Combining attributes ​

All attributes can be combined on the same element, including multiple modes:

html
<!-- Single mode with all attributes -->
<div
  data-foldpage-particles="orbit"
  data-foldpage-particles-strength="0.7"
  data-foldpage-particles-radius="220"
  data-foldpage-particles-emit="150"
  data-foldpage-particles-lifetime="5">
  Featured content — particles orbit and refresh every 5 s
</div>

<!-- Multiple modes on one element -->
<div
  data-foldpage-particles="vortex orbit"
  data-foldpage-particles-strength="0.75"
  data-foldpage-particles-radius="200"
  data-foldpage-particles-emit="150">
  Vortex spin + orbital pull simultaneously
</div>

Global scope via data attributes ​

Setting data-foldpage-particles-target="body" on any element promotes it from a local attractor to a global config source. The element does not create an attractor slot. Instead its attributes are merged into the global FoldParticlesConfig — equivalent to the values you would pass to Fold.init({ particles: { ... } }).

In this context the primary data-foldpage-particles attribute is interpreted as simulation modes (brownian, gravity, spring, orbit, chain, boid, spiral), not attractor behavior modes.

html
<!-- Pure-HTML global config — no Fold.init() call required -->
<div
  data-foldpage-particles="brownian gravity"
  data-foldpage-particles-target="body"
  data-foldpage-particles-friction="0.92"
  data-foldpage-particles-brownian-strength="0.0003"
  data-foldpage-particles-gravity="0,-0.00006,0">
</div>

This is equivalent to:

js
Fold.init({ particles: {
  modes:            ['brownian', 'gravity'],
  friction:         0.92,
  brownianStrength: 0.0003,
  gravity:          [0, -0.00006, 0],
} });

Supported attributes in target="body" context:

AttributeMaps to
data-foldpage-particlesmodes — simulation mode list
data-foldpage-particles-frictionfriction
data-foldpage-particles-brownian-strengthbrownianStrength
data-foldpage-particles-brownian-frequencybrownianFrequency
data-foldpage-particles-gravitygravity — single "x,y,z" vector

Attractor-only attributes (strength, radius, emit, lifetime) are ignored on target="body" elements.

Priority: target="body" config sits below JS Fold.init() / Fold.set() — if both set the same property, the JS value wins. Multiple target="body" elements on the same page merge first-wins per property, in DOM order.

Mixed usage: target="body" elements and regular attractor elements coexist freely on the same page. The global config drives the simulation modes; the attractor elements add local force zones on top.


Full Config Reference ​

Fold.init() config ​

PropertyDefaultDescription
count(auto from page area)Ambient GPU particles. 0 = no ambient cloud. Emitter particles are additive.
modes['brownian']Active global mode(s) — see Modes
friction0.89Velocity damping per frame (0 = stop, 1 = no damping)
simSpeed1.0Simulation speed. 0.15 = 15% speed
brownianStrength0.0002Random impulse magnitude (brownian mode)
brownianFrequency11Direction-change rate in Hz for Brownian mode. Lower = smoother lazy drifts
gravity[0, 0, 0]Global acceleration vector (gravity mode). Auto-enables the mode when non-zero.
springStrength0.00001Spring coefficient toward springTarget
springTarget[0, 0, 0]Spring / orbit / spiral anchor point
boundsShape'box''box' document-fitted rectangle | 'sphere' centered sphere
boundsAutoDoctrueAuto-size box bounds to innerWidth × scrollHeight; updates on resize
boundsBox(auto)Explicit box half-extents [x, y, z] in world units; overrides boundsAutoDoc
boundsCenter(auto)Explicit box center [x, y, z] in particle local space; overrides boundsAutoDoc
boundsRadius0.45Sphere radius in world units, or Z half-extent for box mode (÷ 0.0007 → CSS px)
boundsStrength1Boundary spring strength (constrain mode)
boundsMode'constrain''constrain' push back | 'wrap' teleport through
wrapDamping0Velocity multiplier on wrap (wrap mode only)
interactiveStrength0.002Cursor/touch force magnitude. 0 = disabled.
interactiveDistance0.05World-space radius of cursor influence
interactiveStaleDecay0.99Force decay rate when cursor is stationary
chainStrength0.002Spring strength linking adjacent particles (chain mode)
boidRadius0.05Cohesion + alignment search radius (boid mode)
boidSepRadius0.05Separation search radius (boid mode)
boidCohesionStr0.008Cohesion force strength (boid mode)
boidAlignmentStr0.005Velocity alignment strength (boid mode)
boidSeparationStr0.003Separation force strength (boid mode)
spiralTangentStr0.0001XZ tangential force strength (spiral mode)
spiralVertStr0.0Vertical oscillation amplitude (spiral mode)
trailLength7Frames back for trail endpoint
lineThickness1.0Particle line thickness in screen pixels
blendMode'normal''normal' | 'additive' | 'multiply' | 'subtractive'
depthColorStops(5-stop gradient){ depth, color } stops mapping Z depth to particle color
maxAttractors40Max simultaneous DOM element attractor slots (hard cap: 40). Each mode token on an element uses one slot.
maxParticles10000Cap on total particles (count + all emit values). If exceeded, emitters lowest on the page get fewer particles.
attractorBaseStrength0.004Base force for DOM attractors at strength=1.0
particleLifetime0 (immortal)Global seconds before a particle dies and respawns. 0 = never.
debugfalseRender origin axes gizmo and wireframe attractor overlays

Data attribute reference ​

All attributes use the foldpage prefix.

AttributeDefault (when omitted)Description
data-foldpage-particles—One or more space/comma-separated modes: attract | repel | orbit | vortex | contain | brownian | gravity | boid. Each token uses one attractor slot (max 40).
data-foldpage-particles-strengthauto (scales with element size)Force multiplier 0–1. Multiplied by global base force strength. Auto-scales by element area when omitted.
data-foldpage-particles-radiusauto (scales with element size)Influence radius in CSS px. Auto-scales by element area when omitted.
data-foldpage-particles-emitauto (scales with element size)Particles seeded near this element. Absent = auto from area; 0 = none (attractor only).
data-foldpage-particles-lifetime0Lifetime in seconds. With emit: per-emitter override. Without emit: sets global lifetime for all free particles. 0 = immortal.
data-foldpage-particles-brownian-strength0.001Turbulence magnitude for brownian mode on this element. Intentionally stronger than global brownian strength.
data-foldpage-particles-brownian-frequency20Noise step rate (Hz) for brownian mode. Higher = faster jitter.
data-foldpage-particles-gravityglobal gravityGravity vector "x,y,z" for gravity mode on this element.
data-foldpage-particles-frictionauto (scales inversely with element size)Friction override (0–1) within the element's radius. When absent, auto-scales so smaller elements damp more. Blends with global by falloff weight.

Fold.page — 3D UI for the web.