Jeu d'Aiguilles (Pin Spin)
Plante des aiguilles dans un disque tournant sans en toucher d'autres : 10 niveaux à vitesse croissante
Qu'est-ce que le Jeu d'Aiguilles ?
Pin Spin est un jeu casual jouable dans le navigateur, dans la même veine que les hits mobiles AA et Pin Out. Au centre de l'écran, un disque blanc tourne à vitesse constante. Tu tires des aiguilles une à une depuis le bas, et chaque aiguille doit se planter dans le disque sans toucher celles qui y sont déjà. Chaque niveau te donne un nombre fixe d'aiguilles : place-les toutes pour passer au suivant, et un seul choc met fin à la manche immédiatement. Les règles sont assez simples pour être comprises en quelques secondes, mais la difficulté grimpe vite et le jeu devient un vrai exercice de timing et de sang-froid. L'outil tourne entièrement dans le navigateur, sans inscription ni téléchargement : clic souris sur ordinateur, tap à l'écran sur mobile.
Comment utiliser
Étapes
- À l'ouverture de la page, le jeu démarre automatiquement au niveau 1, le disque tournant déjà
- Clique dans la zone de jeu (ou appuie sur Espace ou Entrée) pour tirer une aiguille
- L'aiguille file vers le centre et se plante dans le disque ; elle ne doit toucher aucune aiguille déjà en place
- Place toutes les aiguilles du niveau sans choc pour passer au niveau suivant
- Tout choc met fin à la manche : choisis « Recommencer le niveau » ou « Tout recommencer » pour continuer (note : appuyer sur Espace / Entrée sur l'écran de fin de partie déclenche « Tout recommencer » et te renvoie au niveau 1, pas « Recommencer le niveau » — utilise le bouton si tu veux rejouer le même niveau)
Astuces pour passer les niveaux
- Le disque tourne à vitesse constante : le rythme compte plus que la vitesse de clic ; vise le milieu du créneau libre.
- Tire juste après le passage d'une aiguille voisine ; l'aiguille met environ 120 ms à voler, donc anticipe légèrement.
- Jusqu'à 3 tirs sont mis en file et partent l'un après l'autre ; les clics au-delà de cette limite sont rejetés, pas mis en file, donc rien ne sert de marteler le clic.
- Les niveaux avancés laissent des espaces très étroits et tournent bien plus vite : sauter un tir risqué vaut mieux qu'une collision gourmande.
Difficulté par niveau
- Niveaux 1-3 : beaucoup d'aiguilles, larges espaces ; idéal pour prendre le rythme.
- Niveaux 4-7 : davantage d'aiguilles déjà sur le disque, il faut anticiper la rotation.
- Niveaux 8-10 : le disque est presque plein, chaque tir doit être minuté précisément.
Cas d'usage
Principe technique
La boucle principale est pilotée par requestAnimationFrame. À chaque image, on incrémente une rotation interne d'une valeur fixe speed (degrés par image), puis on parcourt toutes les aiguilles plantées pour leur attribuer transform: rotate(baseAngle + rotation). Comme transform est une propriété de couche de composition, le navigateur délègue la rotation au thread compositeur GPU sans déclencher de layout ni de paint, si bien que le dernier niveau, avec ses 15 aiguilles en rotation, tient sans peine 60 fps. La détection de collision utilise une approche « différence d'angle convertie en longueur de corde ». Chaque aiguille plantée est représentée par son baseAngle dans le système de coordonnées du disque (un décalage fixe par rapport au disque, indépendant de la rotation). Quand une aiguille tirée atteint le centre, son angle en coordonnées monde vaut TARGET_ANGLE = 0, ce qui se traduit en coordonnées du disque par normalizeAngle(0 - rotation). On compare alors le nouveau baseAngle à chaque baseAngle existant : la différence angulaire Δθ est convertie en longueur de corde 2R·sin(Δθ/2) entre les centres des points de pointe d'aiguille (R = 111px du centre de chaque point de pointe au centre du disque). Si elle passe sous le seuil de 24px, le tir est en collision et la manche s'arrête. Le tir est encadré par une petite machine à états. shootLocked marque une aiguille comme « en vol » et empêche que l'événement transitionend et le setTimeout de secours de 180 ms ne valident deux fois le même tir. La transition CSS dure 120ms ; le setTimeout de 180ms sert de filet de sécurité dans le cas rare où transitionend ne se déclenche pas (par exemple, lorsque le DOM est modifié avant la fin de la transition). pendingShots met en mémoire tampon les clics demandés pendant qu'une aiguille est encore en l'air, plafonné à 3 — les clics au-delà de ce plafond sont rejetés (pas mis en file), ce qui conserve la sensation de tir rapide tout en empêchant que plusieurs aiguilles convergent simultanément vers le centre. Échec et réussite passent gameOver à true : la boucle continue, mais cesse de mettre à jour rotation, gelant la scène. Pour la mise à l'échelle, le jeu garde une taille de design fixe en 420×720 et l'enveloppe externe calcule un facteur d'échelle à partir du viewport et l'applique via transform: scale. L'implémentation actuelle plafonne l'échelle à 1, donc les petits viewports sont réduits proportionnellement tandis que les viewports de bureau gardent la taille d'origine — ils ne s'agrandissent pas. Ainsi la détection de collision opère toujours dans le système de coordonnées de design avec un seul jeu de seuils en pixels, et le verdict est identique sur tous les écrans.
- Boucle principale : requestAnimationFrame, rotation += speed à chaque image, toutes les aiguilles reçoivent transform: rotate(baseAngle + rotation).
- Accélération GPU : la rotation utilise transform plutôt que left/top, le compositeur du navigateur s'en occupe, sans layout ni repaint.
- Détection d'impact : le 0° en coordonnées monde est ramené en coordonnées du disque via normalizeAngle(0 - rotation) ; la longueur de corde 2R·sin(Δθ/2) est comparée à un seuil de 24px.
- Machine à états : shootLocked empêche le double règlement, pendingShots est plafonné à 3 avec rejet du surplus (pas de mise en file), gameOver fige les mises à jour de rotation.
- Validation du tir : vol CSS de 120ms, transitionend en règlement principal + setTimeout de 180ms en filet de sécurité pour le cas rare où transitionend ne se déclenche pas.
- Mise à l'échelle : système de design fixe 420×720 ; l'enveloppe applique transform: scale selon la taille du viewport, plafonné à 1 pour que le bureau garde la taille d'origine plutôt que de s'agrandir.
- Stockage : seul le meilleur niveau franchi est sauvegardé dans localStorage. Rien n'est envoyé, pas de compte, pas de classement.
Exemples
Vitesse et nombre d'aiguilles sur les 10 niveaux
Niveau Départ Tirs Vitesse (deg/frame)
1 2 8 1.35
2 3 9 1.50
3 3 10 1.65
4 4 11 1.80
5 4 12 2.00
6 5 13 2.20
7 5 14 2.45
8 6 15 2.70
9 7 15 3.00
10 8 15 3.35Une partie typique : échec au niveau 5
Niveau 5 : 4 aiguilles de départ, 12 tirs à effectuer.
Tirs 1-7 : se plantent proprement, il en reste 5.
Tir 8 : déclenché avant que la précédente ne se pose, trop tôt.
Verdict : nouveau baseAngle à 8° du tir 6, sous DOT_HIT_DISTANCE.
Résultat : collision, fin de manche avec « Partie perdue ! ».
Clique sur « Recommencer le niveau » pour reprendre au niveau 5.Bilan d'une partie complète
Temps total : environ 2 min 40 s
Tirs cumulés : 117
Échecs : 3
Meilleur niveau : 10
Message : « Tous les niveaux terminés ! » ; le bouton affiche « Rejouer ».
Le meilleur niveau est écrit dans localStorage et persiste après rechargement de la page.Questions fréquentes
J'ai cliqué sur un espace vide, pourquoi ai-je quand même perdu ?
L'aiguille n'est pas instantanée : entre le clic et le contact réel avec le disque, il s'écoule environ 120 ms, et le disque continue de tourner pendant ce temps. Si l'espace était assez large au moment du clic mais qu'une autre aiguille est arrivée sur la ligne cible pendant le vol, la nouvelle aiguille atterrit dessus. La parade simple : tirer juste après qu'un espace a franchi la ligne, pour laisser de la marge à la rotation.
Puis-je rester appuyé sur l'écran pour tirer en rafale ?
On peut cliquer rapidement, mais le tir est limité : aucun ne s'élance tant que le précédent est encore en l'air. Jusqu'à 3 clics pendant le vol sont mis en file et partent automatiquement à la suite ; tout ce qui dépasse est rejeté, pas mis en file. Dans les niveaux avancés, mieux vaut ralentir et laisser chaque aiguille se poser plutôt que de marteler le clic.
Comment évolue la difficulté ?
Chaque niveau a sa propre vitesse de rotation, son nombre d'aiguilles de départ et son nombre de tirs requis. La vitesse passe de 1,35 deg/frame au niveau 1 à 3,35 deg/frame au niveau 10 — environ 2,5×, sans atteindre le triple. Les aiguilles de départ passent de 2 à 8, donc les espaces survivants se réduisent. Sur la fin, la patience compte plus que la vitesse pure.
Après un échec, on repart de zéro ou on rejoue le même niveau ?
Les deux options sont possibles, mais le déclencheur est important. « Recommencer le niveau » relance le niveau où tu as échoué, avec les mêmes nombres d'aiguilles et la même vitesse ; « Tout recommencer » — ainsi qu'un appui sur Espace / Entrée sur l'écran de fin de partie — te ramène au niveau 1. Il n'existe aucun raccourci clavier pour « Recommencer le niveau » : si tu appuies par réflexe sur Espace pour continuer, tu retomberas au niveau 1. Ton meilleur niveau franchi reste toujours dans localStorage.
Pourquoi la zone de jeu paraît-elle si étroite ?
La taille de design est 420×720, calquée sur un mobile en mode portrait. Sur téléphone, c'est naturellement plein écran ; sur ordinateur, l'enveloppe la réduit proportionnellement, mais l'échelle est plafonnée à 1, donc une grande fenêtre de bureau ne fera pas grossir le jeu au-delà de 420×720. La logique du jeu ne change pas avec la taille de la fenêtre.
Des données sont-elles envoyées à un serveur ?
Non. Tout le jeu tourne en local dans le navigateur. La seule chose stockée est un nombre — ton meilleur niveau franchi — dans localStorage. Effacer les données du site ou changer de navigateur l'efface. Il n'y a pas de classement et nous n'enregistrons aucun score ni interaction de notre côté.
À quoi faire attention sur mobile ?
Active le mode plein écran de ton navigateur pour que le geste de tirer vers le bas ne déclenche pas de rafraîchissement. Le mode paysage fonctionne, mais le portrait original colle mieux à la mécanique. Sur des téléphones modestes avec quelques baisses d'images, ferme les autres onglets ou animations lourdes ; le jeu lui-même ne fait que quelques appels transform par image, sa charge sur l'appareil reste minimale.