Glass Caustics (WebGL)
Des caustiques de piscine calculées en temps réel par un shader WebGL — cinq itérations par pixel, netteté doublée sur écran Retina — vues à travers une vitre floutée à 7 px et saturée à 160 % ; dégradé de repli si WebGL manque, image fixe si l'utilisateur réduit les animations.
Mis à jour le
Cet effet fait partie d'Effect.Labs — 840 effets vanilla, une partie gratuite, une partie premium. Glassmorphism comprend 47 effets, dont 6 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Une scène en 16/10 (.gc-scene, coins de 14 px, fond #04101c) avec deux couches : un <canvas class="gc-canvas"> plein cadre, et la vitre .gc-glass — min(72%, 330px) de large, coins de 22 px, dégradé blanc de 16 % à 5 %, liseré à 28 %, backdrop-filter: blur(var(--gc-blur, 7px)) saturate(160%), texte centré à ombres portées. Le flou de 7 px est volontairement léger : les lignes de lumière restent devinables sous la vitre, comme à travers un verre épais.
Le script crée un contexte WebGL par canvas, compile un shader de sommets qui couvre l'écran d'un seul triangle, et un shader de fragments qui calcule chaque pixel : il ramène la position dans une tuile de 2π, puis fait cinq itérations où la position est déformée par des sinus et cosinus du temps ; l'intensité cumulée, passée à la puissance 8, donne des filaments lumineux très fins, ajoutés à un bleu de piscine (0.04, 0.32, 0.52). Le temps avance à 0,4 × les secondes. La taille du canvas suit la scène à la densité de l'écran, plafonnée à 2 (mesuré : 784 × 490 px de canvas pour une scène de 784 px à densité 1), recalculée à chaque redimensionnement de fenêtre. Chaque canvas a sa boucle : plusieurs vitres par page fonctionnent. Mesuré dans Firefox et dans Chromium avec la carte graphique : filaments nets, ondulation continue.
Trois replis sont prévus dans le script. Si l'utilisateur demande moins de transparence, ou si le navigateur ne connaît pas backdrop-filter, la scène reçoit .gc-solid ; le préfixe -webkit- couvre Safari 17 et antérieurs. Sans WebGL, elle reçoit .gc-nogl : mesuré en retirant WebGL au navigateur, le canvas est masqué et un dégradé radial bleu prend sa place. En mouvement réduit, une seule image est dessinée — et redessinée à chaque redimensionnement, puisque changer la taille du canvas vide le tampon WebGL : mesuré dans Firefox, l'image reste en place après un passage de 900 à 700 px de large. Une variante plus ancienne de ces caustiques existe aussi dans la catégorie.
Accessibilité
- Mouvement réduit : sous
prefers-reduced-motion, le script dessine une seule image (temps 8) et ne lance pas la boucle — mesuré, l'image ne change plus. Les caustiques restent visibles, figées, même après un redimensionnement de la fenêtre :resize()redessine alors l'image fixe. - Transparence réduite et absence de flou : le script pose
.gc-solidquandprefers-reduced-transparency: reducerépond ou quand nibackdrop-filterni sa forme préfixée ne sont connus. Mesuré sous émulation dans Chromium : vitre pleine#08182a, caustiques baissées à 30 %. Firefox 153 et WebKit 26 ne connaissent pas encore ce réglage de transparence. - Contraste : c'est le point faible. Les filaments sont presque blancs et passent sous le texte. Sans les ombres portées du code, mesuré sur la vitre à six instants : 3,54:1 en valeur médiane, 1,31:1 quand un filament traverse. Les ombres (
text-shadownoir à 45 et 60 %) sauvent la lecture d'un titre court ; pour un texte long ou petit, montez le voile de la vitre (premier arrêt du dégradé à 30 % de noir) ou augmentez--gc-blurà 14 px pour noyer les filaments. - Sémantique : le canvas est décoratif — ajoutez
aria-hidden="true". La lumière qui ondule plein cadre peut gêner les personnes sensibles au mouvement même si elle est lente : le respect deprefers-reduced-motionest déjà dans le script.
Compatibilité navigateur
WebGL 1 + CSS (backdrop-filter) + 3,3 Ko de JavaScript, aucune dépendance. WebGL 1 existe dans tous les navigateurs actuels, y compris Safari 8 et plus. Mesuré dans Firefox 153 et dans Chromium 151 avec la carte graphique (Metal) : caustiques complètes. Les machines sans accélération reçoivent un rendu logiciel ; mesuré en forçant le moteur logiciel ancien de Chromium dans un outil de capture, le canvas sortait parfois blanc ou vide d'une prise à l'autre — c'est ce moteur de test, pas un navigateur d'acheteur, mais c'est la raison du repli. Le shader demande la précision haute (highp) : un appareil qui ne l'a pas fait échouer la compilation, et le script bascule sur le dégradé. La vitre floute dans les trois moteurs (capture d'écran réelle pour Firefox et WebKit, dont les captures automatiques n'affichent jamais backdrop-filter).
Sans WebGL ou si le shader ne compile pas, la scène reçoit .gc-nogl : canvas masqué, dégradé radial du bleu #0a6f8c au bleu nuit, vitre et texte intacts. La seconde couche de ce fond, des rayons en dégradé conique, est déclarée sous le dégradé radial opaque et ne se voit pas : placez-la en premier si vous voulez les rayons. Sans backdrop-filter, .gc-solid rend la vitre pleine. Sans JavaScript, le canvas reste vide sur le fond #04101c.
Le code
Structure HTML à coller dans votre page (le CSS + JS sont inclus avec Premium) :
<div class="demo-preview">
<div class="gc-scene">
<canvas class="gc-canvas" data-caustics></canvas>
<div class="gc-glass">
<h3>…</h3>
<p>…</p>
</div>
</div>
</div>
Le code complet HTML + CSS + JS de Glass Caustics (WebGL), prêt à coller — et les 840 effets. Ou 299 € TTC une fois, accès à vie.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Vitesse (JS — var speed = 1) |
1 (le shader avance à 0,4 × les secondes) | 0,5 pour une eau calme de bassin, 2 pour une eau agitée. Le temps de l'image fixe sous mouvement réduit est 8 (render(8.0)). |
Couleur de l'eau (shader — vec3(0.04, 0.32, 0.52)) |
bleu piscine | Rouge, vert, bleu de 0 à 1 : vec3(0.02, 0.35, 0.30) pour un lagon vert, vec3(0.02, 0.08, 0.20) pour une eau de nuit. |
Finesse des filaments (shader — pow(abs(c), 8.0)) |
8 | 4 donne des nappes de lumière larges, 12 des fils très fins et plus rares. |
Taille des cellules (shader — uv * 6.28318) |
une tuile de caustiques par scène, étirée au format de la scène | Multipliez par 2 (uv * 12.566) pour des cellules deux fois plus petites. Pour des cellules rondes quel que soit le format, divisez par la hauteur seule : gl_FragCoord.xy / u_res.y. |
Flou de la vitre (CSS — --gc-blur) |
7px | 14 px noie les filaments derrière le texte (meilleur contraste), 3 px les laisse presque nets. |
Fond de repli (CSS — .gc-scene.gc-nogl) |
dégradé radial bleu (rayons coniques masqués dessous) | Mettez une image de piscine en url(…) center / cover : c'est elle que verront les appareils sans WebGL. |
Netteté (JS — Math.min(window.devicePixelRatio || 1, 2)) |
densité de l'écran, plafonnée à 2 | Plafonnez à 1 sur une grande scène pour diviser par quatre le nombre de pixels calculés sur écran Retina ; le flou de la vitre masque la perte de netteté. |
FAQ
IntersectionObserver quand la scène sort de l'écran.getContext('webgl') puis experimental-webgl ; si aucun ne répond, ou si un des deux shaders ne compile pas, ou si le programme ne se lie pas, il pose .gc-nogl sur la scène et s'arrête. Mesuré en retirant WebGL au navigateur : canvas masqué, dégradé radial bleu à la place, vitre et texte intacts, aucune erreur. Le contexte perdu en cours de route (onglet en veille longue sur mobile) n'est pas géré : le canvas reste figé sur sa dernière image..gc-canvas et crée un contexte WebGL par canvas, chacun avec sa boucle. Les navigateurs limitent le nombre de contextes WebGL actifs (autour de seize) : au-delà, les plus anciens sont perdus. Pour une grille de cartes, préférez un seul canvas derrière toute la grille.