Cursor Text Scramble
Chaque caractère devient un span ; au mousemove, ceux dont le centre est à moins de 60 px du pointeur reçoivent un caractère au hasard parmi 69 et la couleur #a78bfa, puis reviennent un à un 200 à 500 ms plus tard — aucune animation CSS, uniquement des setTimeout. Le HTML vendu livre la phrase en texte brut : le script produit 44 spans, un par caractère.
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. Curseurs comprend 26 effets, dont 1 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
Au chargement, pour chaque [data-text-scramble], le script prend le premier .scramble-text, lit son textContent, le vide et recrée un span.char par caractère, avec l'original mémorisé dans data-original. Le CSS des lettres : display: inline-block (pour que chacune ait une boîte mesurable), transition: color .1s, et .char.scrambling { color: #a78bfa }. Le texte lui-même est en 1,3 rem gras, letter-spacing: 2px, #e2e8f0, user-select: none, centré dans une zone en flex de padding: 30px.
À chaque mousemove sur la zone, le script parcourt toutes les lettres, mesure chacune (getBoundingClientRect) et calcule la distance de son centre au pointeur. Si elle est inférieure à 60 px et que l'original n'est pas une espace, la lettre reçoit la classe scrambling et un caractère tiré dans une chaîne de 69 signes (A–Z, a–z, 0–9, !@#$%&*). Son minuteur de retour est annulé puis reposé : setTimeout(…, 200 + 300 × Math.random()) — la lettre retrouve data-original et perd la classe 200 à 500 ms après le dernier passage du pointeur. Tant que la souris bouge à portée, chaque événement retire un nouveau caractère : c'est le scintillement.
Mesuré dans Chromium : après un balayage du texte, le dernier caractère revient 497 ms après le dernier mouvement et la phrase est restaurée à l'identique. Il n'y a ni requestAnimationFrame ni image clé : la seule animation est la transition de couleur de 100 ms. Le coût est celui de la mesure : une lecture de géométrie par lettre et par événement — négligeable pour 44 lettres, à surveiller sur un paragraphe. Le pointeur natif reste affiché (cursor: default).
Le HTML vendu livre la phrase en texte brut dans .scramble-text : textContent contient exactement les 44 caractères et le script crée 44 spans, un par lettre et par espace. C'est la condition de fonctionnement, car le script relit textContent et redécoupe tout, et la condition n'épargne que l'espace " ". Un HTML indenté — la phrase déjà découpée en 44 span.char, chacun sur sa ligne, comme dans une ancienne extraction — lui faisait compter 45 retours à la ligne et 272 espaces d'indentation, soit 357 spans au lieu de 44 (317 de largeur nulle) ; à portée du pointeur, les retours à la ligne recevaient un caractère visible — mesuré sur ce HTML : 7 brouillés en même temps, des lettres parasites entre les mots (« C U R S O kI D vL hM… ») et une ligne qui s'élargissait de 637 à 684 px.
Accessibilité
- prefers-reduced-motion absent du code : le scintillement des lettres n'est pas une animation CSS, aucune media query ne l'arrête. Conditionnez la pose de l'écouteur
mousemoveà!matchMedia('(prefers-reduced-motion: reduce)').matches— le texte reste alors un texte, lisible et stable, ce qui est le repli attendu. - L'effet est exclusivement souris ; au tactile, au plus un événement par toucher, sans conséquence. Le texte ne porte aucune information supplémentaire pendant le brouillage — il en perd, temporairement : ne l'appliquez pas à une consigne qu'il faut lire au moment où la souris passe (menu, libellé de bouton).
user-select: noneempêche par ailleurs de sélectionner et copier la phrase : retirez-le si c'est du contenu. - Contraste :
#e2e8f0sur#0a0a0f= 16:1 et la couleur de brouillage#a78bfa= 7,3:1, AAA. Sur une page claire, ces valeurs s'effondrent (1,2:1 et 2,7:1 sur blanc) : passez le texte à#1e293b(14,6:1) et le brouillage à#6d28d9(7,1:1). - Lecteurs d'écran : la phrase est découpée en dizaines de
spaneninline-block, et son contenu change des dizaines de fois par seconde au survol. Certains lecteurs épellent les inline-blocks un à un ; tous liront des caractères aléatoires si l'utilisateur passe la souris en même temps. Donnez à.scramble-textunaria-labelportant la phrase (ou une copie masquée visuellement) etaria-hidden="true"aux spans, pour que la version lue soit toujours la vraie. - Intégration : le HTML doit fournir la phrase en texte brut dans
.scramble-text, comme le code vendu — des spans déjà découpés et indentés produiraient des spans blancs parasites. Le découpage a lieu une fois au chargement : un texte modifié ensuite (traduction, CMS) n'est pas repris, relancez le script après. La zone a besoin d'une hauteur —height: 100%dans le conteneur vendu donne 93 px, la bande du texte plus 30 px de marge — et le pointeur doit passer à moins de 60 px du centre des lettres.
Compatibilité navigateur
Code ES5 (var, function) : il requiert getBoundingClientRect, classList, setTimeout et data-*. Aucune animation CSS hormis une transition de couleur. Zéro dépendance.
Sans JavaScript, la phrase s'affiche telle quelle, en texte brut ou dans ses spans : rien ne manque, rien ne bouge. Sans transition CSS, la couleur violette apparaît et disparaît d'un coup.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="scramble-zone" data-text-scramble>
<div class="scramble-text">CURSOR ALCHEMY TRANSFORMS DIGITAL EXPERIENCE</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 |
|---|---|---|
Rayon d'action (JS — < 60) |
60 px | Distance du pointeur au centre d'une lettre en dessous de laquelle elle se brouille. 30 touche une ou deux lettres ; 120 brouille un mot entier. À grande taille de police, augmentez-le proportionnellement. |
| Jeu de caractères (JS — chaîne de 69 signes) | A–Z a–z 0–9 !@#$%&* | Caractères tirés au hasard. Attention : l'indice est Math.floor(69 × Math.random()) — si vous changez la chaîne, mettez sa longueur à la place de 69, sinon des indices hors chaîne renvoient undefined, affiché tel quel. |
Délai de retour (JS — 200 + 300 × Math.random()) |
200 à 500 ms | Temps entre le dernier passage du pointeur et le retour de la lettre. Le minuteur est réinitialisé à chaque événement : la lettre ne revient qu'une fois le pointeur parti. 80 + 120 × … = retour vif ; 600 + 900 × … = traînée longue. |
Couleur de brouillage (CSS — .char.scrambling) |
#a78bfa | Couleur des lettres pendant le brouillage. Le passage se fait en transition: color .1s. Vérifiez 4,5:1 sur votre fond (7,3:1 ici sur #0a0a0f). |
Espaces (JS — " " !== data-original) |
seules les espaces sont épargnées | Remplacez la comparaison par !/\s/.test(original) pour épargner aussi retours à la ligne et tabulations — indispensable si votre HTML est indenté ou contient plusieurs lignes. Épargnez aussi la ponctuation en l'ajoutant au test. |
Typographie (CSS — .scramble-text) |
1,3 rem, 700, letter-spacing 2 px | Plus la police est grande, moins de lettres tiennent dans le rayon de 60 px : ajustez le rayon avec la taille. Une police à chasse fixe évite que la ligne bouge quand une lettre étroite (i, l) devient large (W, @). |
Cible (HTML — .scramble-text, texte brut) |
premier .scramble-text de la zone | Le script ne traite que le premier .scramble-text de chaque zone [data-text-scramble]. Fournissez la phrase en texte brut, sans spans ni indentation ; pour plusieurs titres, plusieurs zones. |
FAQ
textContent de .scramble-text et crée un span par caractère, retours à la ligne et indentation compris — et que la condition n'épargne que l'espace " ". Le code vendu livre la phrase en texte brut d'un seul tenant (<div class="scramble-text">CURSOR ALCHEMY …</div>, 44 spans). Si vous l'avez réécrite sur plusieurs lignes indentées, ou si un outil a figé le DOM en 44 span.char joliment mis en forme, chaque retour à la ligne "\n" devient un span qui se brouille comme une lettre et affiche un caractère visible là où il n'y avait rien — mesuré sur un tel HTML : 357 spans au lieu de 44, des signes entre les mots et une ligne passant de 637 à 684 px. Deux remèdes, au choix : garder la phrase en texte brut sur une seule ligne, ou remplacer la comparaison par !/\s/.test(original).mousemove retire un nouveau caractère pour toute lettre à portée et réinitialise son minuteur de retour (clearTimeout puis setTimeout). Tant que le pointeur reste à moins de 60 px, la lettre scintille et son retour est repoussé ; dès qu'il s'éloigne, le dernier minuteur posé s'écoule — 200 à 500 ms, tiré au hasard par lettre, d'où le retour désordonné. Mesuré : 497 ms entre le dernier mouvement et la dernière lettre restaurée. Pour un retour synchronisé, remplacez le terme aléatoire par une constante.[data-text-scramble] par titre, chacune avec son .scramble-text — le script ne regarde que le premier de chaque zone. Traduction ou contenu chargé après coup : le découpage en spans se fait une seule fois au chargement, sur le texte présent à ce moment ; si vous remplacez le texte ensuite (i18n côté client, CMS), les spans mémorisés par le script n'existent plus et le survol ne fait rien. Relancez la fonction d'initialisation après avoir écrit le texte, ou écrivez la phrase définitive côté serveur.