Generative Flow Field
Bruit Perlin 2D en champ de vecteurs — 5000 particules lumineuses en blending additif, traînées persistantes, rendu nébuleuse ou aurore à 60fps.
Vous aurez accès à la démo interactive avec un compte gratuit.
Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Atmosphère comprend 57 effets, dont 10 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Un tableau de permutation Perlin classique (256 valeurs entières mélangées aléatoirement, dupliquées à 512 entrées) sert de base à perlin2(x, y). La fonction calcule une interpolation quintique 6t⁵ − 15t⁴ + 10t³ (fade), un barycentre bilinéaire (lerp) et projette sur un gradient 2D parmi 4 orientations (±x ± y). La sortie est dans [−1, 1] ; multipliée par 2π elle donne un angle de flux en tout point du plan.
À chaque frame, l'angle de chaque particule vaut perlin2(x × 0,0028, y × 0,0028 + timeOffset) × 2π. Le facteur d'échelle 0,0028 contrôle le zoom du champ (tourbillons larges), et timeOffset s'incrémente de 0,0008 × speed par frame — le champ tout entier dérive lentement dans le temps sans jamais se répéter. La particule se déplace de cos(angle) × speed × 1,3 en x et sin(angle) × speed × 1,3 en y ; les bords sont toroïdaux (wrap-around). Chaque particule porte un age et une life (100–300 frames) : à sa mort elle renaît à une position aléatoire et reçoit une nouvelle couleur, ce qui assure une densité uniforme sans zones mortes.
Avant de dessiner les particules, chaque frame dépose un voile rgba(10, 10, 20, trailAlpha) sur tout le canvas (trailAlpha = 0,05 par défaut, soit ~20 frames de trace visible). Les particules sont ensuite peintes en globalCompositeOperation: 'lighter' (blending additif) : là où plusieurs flux convergent, les canaux RGB s'additionnent jusqu'au blanc, créant les filaments lumineux caractéristiques des aurores ou nébuleuses. La taille du pixel varie de 1,6 à 2,4 px selon lifeRatio (1 − |age/life − 0,5| × 2), ce qui donne un fondu d'entrée et de sortie naturel.
Un IntersectionObserver (seuil 0,01) suspend la boucle requestAnimationFrame dès que le canvas quitte le viewport — zéro CPU inutile hors-écran. Le canvas est redimensionné en tenant compte du devicePixelRatio (plafonné à 2) via syncSize() ; un écouteur resize réinitialise la matrice de transformation (ctx.setTransform(1,0,0,1,0,0)) puis rappelle syncSize() pour recalibrer sans artefact.
Accessibilité
- prefers-reduced-motion : présent dans le code — le script détecte
prefers-reduced-motion: reduceet retourne immédiatement. Le canvas reste vide, aucunrequestAnimationFramen'est lancé. Il n'existe pas de rendu statique de remplacement intégré : prévoir un dégradé CSS de fond comme repli visuel. - Le
<canvas>ne porte pasaria-hidden="true"dans le HTML livré — à ajouter pour que les lecteurs d'écran l'ignorent (le canvas ne contient pas de contenu textuel). - Pas de
role="img"ni d'aria-labelsur le conteneur — à ajouter sur.ps-canvas-wrappour décrire la scène aux technologies assistives. - Aucun élément interactif dans l'effet : le canvas est purement visuel, la navigation clavier n'est pas impactée par l'animation.
- Le contraste du texte overlay est à la charge de l'intégrateur — le canvas ne fournit pas de scrim ; ajouter un voile
rgba(0,0,0,0.45)sous tout texte positionné par-dessus l'effet.
Compatibilité navigateur
Nécessite Canvas 2D Context et requestAnimationFrame — supportés dans tous les navigateurs modernes. Utilise la déstructuration ES6 ([a, b] = [b, a]) pour le mélange de permutation.
Si <code>canvas</code> n'est pas supporté ou si <code>getContext('2d')</code> retourne <code>null</code>, le script s'arrête silencieusement (guard <code>if (!ctx) return</code>). Le conteneur <code>.ps-canvas-wrap</code> s'affiche avec son fond CSS — aucune erreur fatale, la page reste utilisable.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="demo-preview">
<div class="ps-canvas-wrap" role="img" aria-label="Champ vectoriel animé — particules lumineuses suivant des courants Perlin">
<canvas class="ps-canvas" id="psFlowField" aria-hidden="true"></canvas>
</div>
</div>
HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
| settings.palette | 'aurora' | Palette de couleurs active : 'aurora' (vert/cyan/violet), 'ocean' (bleus/cyans), 'fire' (oranges/rouges/jaunes), 'mono' (blancs semi-transparents). Modifier en tête du script dans l'objet settings. |
| settings.count | 5000 | Nombre total de particules instanciées. 2000–3000 pour mobile ou composant small ; 8000 pour fond de page grand format. Modifie l'appel spawnParticles(settings.count). |
| settings.speed | 1.0 | Multiplicateur global : pilote à la fois le déplacement des particules (× 1,3) et la dérive temporelle du champ (timeOffset += 0,0008 × speed). 0,5 = courants lents et contemplatifs, 1,5 = tourbillons énergiques. |
| settings.trailAlpha | 0.05 | Opacité du voile de fondu appliqué au canvas à chaque frame. 0,02 → traînées très longues (effet nébuleuse), 0,12 → traînées courtes (particules nettes). Plus la valeur est faible, plus les filaments s'accumulent. |
| PALETTES (objet JS) | 5 couleurs par palette | Tableaux d'hex des couleurs par palette, définis en tête du script. Remplacer les valeurs hex d'une palette existante ou ajouter une clé 'brand' pour une palette personnalisée, puis pointer settings.palette = 'brand'. |
| Constante scale (0.0028) | 0.0028 | Zoom du bruit Perlin dans l'appel perlin2(p.x × scale, p.y × scale + timeOffset). 0.0014 → tourbillons deux fois plus grands ; 0.005 → granularité fine, champ chaotique. |
| Incrément timeOffset (0.0008) | 0.0008 | Vitesse de dérive temporelle du champ, dans la ligne timeOffset += 0.0008 × settings.speed. 0.0003 → champ quasi figé (variations très lentes), 0.002 → champ qui se transforme rapidement. |
FAQ
fillRect minimaliste). Sur Android milieu de gamme, réduire settings.count à 2000–3000 suffit. L'IntersectionObserver coupe le requestAnimationFrame dès que la section est hors du viewport, ce qui annule tout coût CPU quand l'effet n'est pas visible.getBoundingClientRect(). Donnez une taille CSS explicite au .ps-canvas-wrap (par ex. width: 600px; height: 400px) et le champ s'y adapte automatiquement — les particules restent dans ces limites grâce au wrap-around bord-à-bord. Pour une zone plus petite, réduire settings.count proportionnellement (3000 pour 600×300 px).prefers-reduced-motion: reduce est détecté, le script quitte immédiatement sans rien dessiner ; le canvas reste transparent et le fond CSS du conteneur s'affiche seul. Il n'y a pas de rendu statique intégré. Recommandation : ajouter une règle CSS @media (prefers-reduced-motion: reduce) { .ps-canvas-wrap { background: linear-gradient(135deg, #22ffaa22, #7c3aff22); } } comme repli visuel.