Étude de cas – Création du doodle Google Stanisław Lem

Marcin Wichary
Marcin Wichary

Bonjour à tous, ce monde étrange

La page d'accueil Google offre un environnement fascinant pour coder. Elle s'accompagne de nombreuses restrictions complexes: une attention particulière portée à la vitesse et à la latence, la nécessité de s'adapter à toutes sortes de navigateurs et à fonctionner dans diverses circonstances, et... oui, la surprise et la satisfaction.

Je parle des doodles Google, des illustrations spéciales qui remplacent parfois notre logo. Bien que ma relation avec les stylos et les brosses ait depuis longtemps ce caractère distinctif d'une ordonnance de restriction, je contribue souvent aux éléments interactifs.

Tous les doodles interactifs que j'ai codés (Pac-Man, Jules Verne, Foire mondiale), et beaucoup d'entre eux, étaient à la fois futuristes et anachronistes: c'était d'innombrables possibilités d'application des fonctionnalités Web de pointe... et la pragmatisme rigoureux de la compatibilité entre navigateurs.

Nous apprenons beaucoup de chaque doodle interactif, et le mini-jeu Stanisław Lem récent ne fait pas exception, avec ses 17 000 lignes de code JavaScript qui essaient beaucoup de choses pour la première fois dans l'histoire du doodle. Aujourd'hui, j'aimerais partager ce code avec vous. Vous trouverez peut-être quelque chose d'intéressant là-bas ou vous indiquerez mes erreurs, et vous en parlerez un peu.

Afficher le code du doodle Stanisław Lem »

Gardez à l'esprit que la page d'accueil de Google n'est pas un espace dédié aux démonstrations technologiques. Avec nos doodles, nous voulons célébrer des personnes et des événements spécifiques, et nous voulons le faire en utilisant le meilleur art et les meilleures technologies que nous pouvons faire intervenir, mais jamais célébrer la technologie pour le plaisir de la technologie. Cela implique d'examiner attentivement chaque partie du langage HTML5 largement compris qui est disponible et de déterminer si elle nous permet d'améliorer le doodle, sans pour autant en détourner l'attention ou l'éclipser.

Examinons quelques technologies Web modernes qui ont trouvé leur place dans le doodle Stanisław Lem, et d'autres qui ne l'ont pas été.

Graphiques via DOM et canevas

Canvas est un outil puissant qui a été spécialement créé pour ce que nous voulions faire dans ce doodle. Cependant, certains des anciens navigateurs qui nous intéressaient ne l'étaient pas. Même si je partage littéralement un bureau avec la personne qui a mis en place une excellente excanvas, j'ai décidé de choisir une autre méthode.

J'ai mis au point un moteur graphique qui élimine les primitives graphiques appelées "rectangles", puis les affiche à l'aide de canevas, ou du DOM si le canevas n'est pas disponible.

Cette approche s'accompagne de défis intéressants. Par exemple, le déplacement ou la modification d'un objet dans le DOM a des conséquences immédiates, tandis que pour le canevas, il existe un moment spécifique où tout est dessiné en même temps. (J'ai décidé de n'utiliser qu'un seul canevas, de le vider et de dessiner à partir de zéro pour chaque cadre. Trop de pièces mobiles d'une part, et de l'autre, trop de complexité pour justifier de les diviser en plusieurs canevas qui se chevauchent et de les mettre à jour de manière sélective.)

Malheureusement, passer au canevas n'est pas aussi simple que de simplement dupliquer les arrière-plans CSS avec drawImage(): vous perdez un certain nombre d'éléments sans frais lors de l'assemblage via DOM (plus important encore, en superposant des z-index et des événements de souris).

J'ai déjà supprimé le z-index avec un concept appelé "plans". Le doodle définissait un certain nombre de plans, du ciel lointain jusqu'au pointeur de la souris devant tout, et chaque acteur dans le doodle devait décider à quel plan il appartenait (de petites corrections plus/moins dans un plan étaient possibles en utilisant planeCorrection).

Lors du rendu via DOM, les plans sont simplement traduits en z-index. Toutefois, si nous effectuons le rendu via le canevas, nous devons trier les rectangles en fonction de leurs plans avant de les dessiner. Comme cette opération est coûteuse à chaque fois, l'ordre n'est recalculé que lorsqu'un acteur est ajouté ou lorsqu'il change d'avion.

Pour les événements de souris, j'en ai fait abstraction aussi... en quelque sorte. Pour le DOM et le canevas, j'ai utilisé d'autres éléments DOM flottants entièrement transparents avec un z-index élevé, dont la fonction consiste uniquement à réagir aux survols, aux clics et aux appuis.

L'une des choses que nous voulions essayer avec ce doodle était de briser le quatrième mur. Le moteur ci-dessus nous a permis de combiner des acteurs basés sur le canevas avec des acteurs basés sur DOM. Par exemple, les explosions se produisent à la fois dans la toile des objets de l'univers et dans le DOM pour le reste de la page d'accueil Google. Comme n'importe quel autre acteur, l'oiseau vole normalement et est accroché à notre masque dentelé comme n'importe quel autre acteur. Il décide donc d'éviter les ennuis pendant le niveau de fusillade et est posé sur le bouton "J'ai de la chance". Pour ce faire, l'oiseau quitte la toile pour devenir un élément DOM (et inversement). J'espérais qu'il soit totalement transparent pour nos visiteurs.

La fréquence d'images

Connaître la fréquence d'images actuelle et réagir lorsqu'elle est trop lente (et trop rapide) était un élément important de notre moteur. Étant donné que les navigateurs ne renvoient pas la fréquence d'images, nous devons la calculer nous-mêmes.

J'ai commencé par utiliser requestAnimationFrame, en revenant à l'ancienne version de setTimeout si l'ancienne n'était pas disponible. requestAnimationFrame permet d'économiser le processeur de manière intelligente dans certaines situations (même si nous le faisons nous-mêmes, comme expliqué ci-dessous), mais il nous permet également d'obtenir une fréquence d'images plus élevée que setTimeout.

Le calcul de la fréquence d'images actuelle est simple, mais il est sujet à des modifications importantes. Par exemple, elle peut chuter rapidement lorsqu'une autre application encombre l'ordinateur pendant un certain temps. Par conséquent, nous calculons une fréquence d'images "glissante" (moyenne) uniquement sur 100 ticks physiques et prenons des décisions en fonction de cette fréquence.

Quel genre de décisions ?

  • Si la fréquence d'images est supérieure à 60 FPS, nous la limitons. Actuellement, sur certaines versions de Firefox, requestAnimationFrame n'a pas de limite supérieure pour la fréquence d'images, et il n'y a aucun intérêt à gaspiller le processeur. Notez que nous limitons en fait à 65 FPS, en raison des erreurs d'arrondi qui font que la fréquence d'images est légèrement supérieure à 60 FPS sur les autres navigateurs. Nous ne souhaitons pas limiter cette limitation par erreur.

  • Si la fréquence d'images est inférieure à 10 FPS, nous ralentissons simplement le moteur au lieu d'abandonner des images. C'est une possibilité de perdre ou de perdre, mais j'ai trouvé qu'ignorer des images de manière excessive serait plus déroutant que de simplement avoir un jeu plus lent (mais tout de même cohérent). Il y a un autre effet secondaire intéressant : si le système est lent temporairement, l'utilisateur ne connaîtra pas un bond étrange, car le moteur rattrape désespérément le retard. (Je l'ai fait un peu différemment pour Pac-Man, mais la fréquence d'images minimale est une meilleure approche.)

  • Enfin, nous pouvons envisager de simplifier les graphiques lorsque la fréquence d'images est dangereusement faible. Nous ne faisons pas cela pour le doodle Lem, à l'exception du pointeur de la souris (plus d'informations à ce sujet ci-dessous), mais nous pourrions peut-être perdre certaines animations superflues afin que le doodle soit fluide, même sur les ordinateurs lents.

Nous avons également un concept de tick physique et logique. Le premier provient de requestAnimationFrame/setTimeout. Dans le jeu normal, le ratio est de 1:1, mais pour une avance rapide, nous ajoutons simplement des graduations logiques par tick physique (jusqu'à 1:5). Cela nous permet d'effectuer tous les calculs nécessaires pour chaque tick logique, mais de ne désigner que le dernier élément qui met à jour les éléments à l'écran.

Analyse comparative

Il est possible d'supposer (et en effet, très tôt) que le canevas sera plus rapide que le DOM lorsqu'il sera disponible. Ce n'est pas toujours vrai. Lors des tests, nous avons découvert qu'Opera 10.0–10.1 sur Mac et Firefox sur Linux sont en fait plus rapides lors du déplacement d'éléments DOM.

Dans le monde parfait, le doodle comparerait discrètement différentes techniques graphiques : les éléments DOM ont été déplacés à l'aide de style.left et style.top, des dessins sur le canevas, et peut-être même des éléments DOM déplacés à l'aide de transformations CSS3.

puis sélectionnez celle qui offre la fréquence d'images la plus élevée. J'ai commencé à écrire du code pour cela, mais j'ai constaté qu'au moins ma méthode d'analyse comparative n'était pas fiable et prenait beaucoup de temps. La vitesse est au cœur de l'interface. Nous souhaitons que le doodle s'affiche instantanément et que la partie commence dès que vous cliquez ou appuyez.

En fin de compte, le développement Web se résume parfois à ce que vous devez faire. J'ai regardé derrière mon épaule pour m'assurer que personne ne regardait, puis j'ai codé en dur Opera 10 et Firefox hors du cadre. Dans la vie suivante, je reviendrai en tant que tag <marquee>.

Économie du processeur

Vous savez qu'un ami qui vient chez vous pour regarder le dernier épisode de la saison de Breaking Bad le gâche pour vous et le supprime de votre enregistreur numérique vidéo ? Tu ne veux pas être celui-là, n'est-ce pas ?

C'est la pire analogie qui soit. Mais nous ne voulons pas que notre doodle soit cet homme : le fait d'être autorisé à accéder à l'onglet du navigateur de quelqu'un est un privilège, et l'accumulation des cycles de processeur ou la distraction de l'utilisateur font de nous un invité désagréable. Par conséquent, si personne ne joue avec le doodle (pas d'appui, de clic de souris, de mouvement de la souris ou d'appui sur une touche), nous voulons qu'il finisse par se mettre en veille.

Quand ?

  • après 18 secondes sur la page d'accueil (dans les jeux d'arcade, il s'agit du mode d'accroche).
  • après 180 secondes si l'onglet est sélectionné
  • après 30 secondes si l'onglet n'est pas sélectionné (par exemple, si l'utilisateur est passé à une autre fenêtre, mais qu'il regarde peut-être encore le doodle dans un onglet inactif)
  • immédiatement si l'onglet devient invisible (par exemple, si l'utilisateur est passé à un autre onglet de la même fenêtre). Inutile de gaspiller des cycles si nous ne pouvons pas le voir.

Comment savoir si l'onglet est actuellement actif ? Nous nous associons à window.focus et window.blur. Comment savoir si l'onglet est visible ? Nous utilisons la nouvelle API Page Visibility et nous réagissons à l'événement approprié.

Les délais ci-dessus sont plus conciliants que d'habitude pour nous. Je les ai adaptés à ce doodle particulier, qui comporte de nombreuses animations d'ambiance (principalement le ciel et l'oiseau). Idéalement, les délais d'inactivité devraient être limités à l'interaction dans le jeu. Par exemple, juste après l'atterrissage, l'oiseau pourrait signaler au doodle qu'il peut se mettre en veille maintenant, mais je n'ai pas mis en œuvre cela à la fin.

Le ciel est toujours en mouvement. Lors de l'endormissement et du réveil, le doodle ne se contente pas de s'arrêter ou de démarrer : il ralentit avant d'être mis en pause, et inversement pour reprendre, augmenter ou diminuer le nombre de tics logiques par tic-tac si nécessaire.

Transitions, transformations et événements

L'un des avantages du langage HTML a toujours été le fait que vous pouvez l'améliorer vous-même: si un élément n'est pas assez performant dans le portefeuille standard de code HTML et CSS, vous pouvez superposer JavaScript pour l'étendre. Malheureusement, cela signifie souvent de devoir repartir de zéro. Les transitions CSS3 sont très utiles, mais vous ne pouvez pas ajouter de type de transition ni utiliser des transitions pour autre chose que styliser les éléments. Autre exemple: les transformations CSS3 sont idéales pour DOM, mais lorsque vous passez au canevas, vous êtes soudainement seul.

Ces problèmes, et bien d'autres, expliquent pourquoi le doodle Lem dispose de son propre moteur de transition et de transformation. Oui, je sais, on appelle ça les années 2000, etc. Les fonctionnalités que j'ai intégrées sont loin d'être aussi puissantes que CSS3, mais quel que soit le moteur, il le fait de manière cohérente et nous donne beaucoup plus de contrôle.

J'ai commencé par un système d'action (d'événements) simple, une chronologie qui déclenche des événements à venir sans utiliser setTimeout, car à tout moment, le doodle peut être divorcé du temps physique à mesure qu'il s'accélère (avance rapide), plus lent (faible fréquence d'images ou mise en veille pour économiser le processeur) ou s'arrête complètement (en attendant la fin du chargement des images).

Les transitions ne sont qu’un autre type d’actions. En plus des mouvements et rotations de base, nous acceptons également les mouvements relatifs (par exemple, déplacer un élément de 10 pixels vers la droite), des éléments personnalisés comme les frissons, ainsi que des animations d'images clés.

Comme mentionné précédemment, les rotations sont effectuées manuellement: nous disposons de sprites pour différents angles pour les objets concernés. La raison principale était que les rotations CSS3 et du canevas introduisaient des artefacts visuels que nous avions jugés inacceptables. Par ailleurs, ces artefacts variaient selon les plates-formes.

Étant donné que certains objets qui pivotent sont liés à d'autres objets en rotation (par exemple, la main d'un robot connectée à l'avant-bras, qui est lui-même attachée à un bras supérieur rotatif), j'ai également dû créer l'origine de transformation d'un homme pauvre sous la forme de pivots.

Tout cela représente une grosse quantité de travail qui, au final, couvre déjà le terrain, déjà pris en charge par HTML5. Mais parfois, la compatibilité native n'est pas suffisante et c'est le moment de réinventer la roue.

Gérer les images et les lutins

Un moteur ne sert pas seulement à faire fonctionner le doodle, c'est aussi à travailler dessus. J'ai partagé certains paramètres de débogage ci-dessus. Vous trouverez les autres dans engine.readDebugParams.

Le spriting est une technique bien connue que nous utilisons également pour les doodles. Il permet d'économiser des octets et de réduire les temps de chargement, et facilite le préchargement. Cependant, cela complique également le développement : chaque modification des images nécessiterait un nouveau respouting (principalement automatisé, mais toujours fastidieux). Par conséquent, le moteur est compatible avec l'exécution sur des images brutes à des fins de développement ainsi que des sprites pour la production via engine.useSprites, qui sont tous deux inclus dans le code source.

Doodle Pac-Man
Sprites utilisés dans le doodle Pac-Man

Nous acceptons également le préchargement des images au fur et à mesure, et l'arrêt du doodle si les images ne se sont pas chargées à temps, avec une fausse barre de progression. (Faux, car malheureusement, même HTML5 ne peut pas indiquer la portion d'un fichier image déjà chargée.)

Capture d&#39;écran du chargement d&#39;un graphique avec la barre de progression rigide.
Capture d'écran du chargement de l'image avec la barre de progression rigide.

Pour certaines scènes, nous utilisons plusieurs lutins non pas autant pour accélérer le chargement en utilisant des connexions parallèles, mais simplement en raison d'une limite de pixels de 3 à 5 millions pour les images sur iOS.

Quelle est la place du HTML5 ? Il n'y a pas grand-chose à voir ci-dessus, mais l'outil que j'ai écrit pour créer/recadrer était une toute nouvelle technologie Web: canevas, blobs et a[download]. L'un des aspects intéressants du langage HTML est qu'il subsume lentement les tâches qui devaient auparavant être effectuées en dehors du navigateur. La seule partie dont nous avions besoin était l'optimisation des fichiers PNG.

Enregistrement de l'état entre les jeux

Les mondes de Lem lui semblaient toujours grands, vivants et réalistes. Ses histoires ont débuté sans beaucoup d'explications. Il s'agissait de la première page, qui a commencé dans la résolution, où le lecteur devait trouver son chemin.

Comme la Cyberiad ne faisait pas exception, nous voulions reproduire ce sentiment pour le doodle. Nous commençons par essayer de ne pas trop expliquer l'histoire. Une autre grande partie est la randomisation, qui, selon nous, était adaptée à la nature mécanique de l'univers du livre. Nous disposons d'un certain nombre de fonctions d'assistance traitant du caractère aléatoire que nous utilisons dans de nombreux endroits.

Nous voulions également améliorer la rejouabilité par d'autres moyens. Pour cela, nous devions savoir combien de fois le doodle était terminé auparavant. La solution technologique historiquement correcte pour résoudre ce problème est le cookie, mais cela ne fonctionne pas pour la page d'accueil Google. Chaque cookie augmente la charge utile de chaque page, et nous nous soucions beaucoup de la vitesse et de la latence.

Heureusement, HTML5 nous offre un stockage Web, facile à utiliser, qui nous permet d'enregistrer et de rappeler le nombre de lectures générales et la dernière scène jouée par l'utilisateur, avec beaucoup plus de grâce que les cookies ne le permettent.

Que faisons-nous de ces informations ?

  • nous affichons un bouton "Avance rapide", qui permet de parcourir les cinématiques que l'utilisateur a déjà vues
  • nous affichons différents N éléments
  • nous augmentons légèrement la difficulté du niveau de tir
  • Dans la troisième et les suivantes, nous affichons un petit dragon représentant les probabilités de l'easter egg.

Un certain nombre de paramètres de débogage contrôlent cela:

  • ?doodle-debug&doodle-first-run : prétendez qu'il s'agit d'une première course
  • ?doodle-debug&doodle-second-run : prétendez qu'il s'agit d'une deuxième course
  • ?doodle-debug&doodle-old-run : faites semblant qu'il s'agit d'une ancienne course

Appareil tactile

Nous voulions que le doodle se sente à l'aise sur les appareils tactiles : les plus modernes sont suffisamment puissants pour qu'il fonctionne parfaitement, et jouer en appuyant sur l'écran devient beaucoup plus amusant qu'en cliquant.

Des modifications initiales devaient être apportées à l’expérience utilisateur. À l'origine, le pointeur de notre souris était le seul moyen de communiquer une vidéo cinématographique/non interactive. Nous avons ensuite ajouté un petit indicateur en bas à droite pour ne pas avoir à nous appuyer uniquement sur le pointeur de la souris (étant donné que ceux-ci n'existent pas sur les appareils tactiles).

Normal Animé Cliquables Déjà consulté
En cours
Pointeur normal du travail en cours
Pointeur &quot;Travail en cours&quot; occupé
Pointeur cliquable en cours de développement
Pointeur sur lequel l&#39;utilisateur a cliqué
Version finale
Pointeur normal finalv
Dernier pointeur occupé
Pointeur cliquable final
Pointeur sur le dernier clic
Pointeurs de la souris pendant le développement et équivalents finaux.

La plupart des solutions ont été prêtes à l'emploi. Toutefois, des tests rapides improvisés d'utilisabilité de notre expérience tactile ont révélé deux problèmes: certaines cibles étaient trop difficiles à appuyer, et les appuis rapides ont été ignorés, car nous ne faisions que écraser les événements de clic de souris.

La présence d'éléments DOM transparents distincts m'a été d'une aide précieuse, car je pouvais les redimensionner indépendamment des visuels. J'ai ajouté une marge intérieure supplémentaire de 15 pixels pour les appareils tactiles et je l'ai utilisée chaque fois que des éléments cliquables étaient créés. (J'ai également ajouté une marge intérieure de 5 pixels pour les environnements de souris, juste pour faire plaisir à M. Fitts.)

Pour l'autre problème, je me suis assuré de joindre et de tester les gestionnaires tactiles de début et de fin appropriés, au lieu de compter sur le clic de la souris.

Nous utilisons également des propriétés de style plus modernes pour supprimer certaines fonctionnalités tactiles ajoutées par défaut par les navigateurs WebKit (appuyer sur mise en surbrillance, appuyer sur l'accroche).

Et comment pouvons-nous détecter si un appareil donné exécutant le doodle prend en charge l'écran tactile ? paresseuse. Au lieu de le déterminer a priori, nous avons simplement utilisé nos QI combinés pour déduire que l'appareil est compatible avec le toucher... après avoir reçu le premier événement de démarrage tactile.

Personnaliser le pointeur de la souris

Mais tout n'est pas tactile. L'un de nos principes directeurs était de placer autant de choses que possible dans l'univers du doodle. La petite interface utilisateur de la barre latérale (avance rapide, point d'interrogation), l'info-bulle et même le pointeur de la souris.

Comment personnaliser le pointeur de la souris ? Certains navigateurs permettent de modifier le curseur de la souris en l'associant à un fichier image personnalisé. Cependant, cette approche n'est pas bien prise en charge et est assez restrictive.

Si ce n'est pas le cas, que se passe-t-il ? Pourquoi ne pas utiliser le pointeur de la souris comme un autre acteur du doodle ? Cela fonctionne, mais s'accompagne d'un certain nombre de mises en gardes, principalement:

  • vous devez pouvoir supprimer le pointeur de la souris natif
  • le pointeur de la souris doit être synchronisé avec le "vrai"

La première est délicate. CSS3 autorise cursor: none, mais il n'est pas non plus compatible avec certains navigateurs. Nous devions recourir à quelques techniques de gymnastique: utiliser un fichier .cur vide en remplacement, spécifier un comportement concret pour certains navigateurs, et même coder en dur d'autres utilisateurs hors de l'expérience.

L'autre est relativement simple sur son visage, mais comme le pointeur de la souris n'est qu'une autre partie de l'univers du doodle, il héritera également de tous ses problèmes. Le plus important ? Si la fréquence d'images du doodle est faible, la fréquence d'images du pointeur de la souris sera elle aussi faible. Cela a des conséquences désastreuses, car le pointeur de la souris, qui est une extension naturelle de votre main, doit être réactif dans tous les cas. (Les personnes qui utilisaient le Commodore Amiga par le passé hochent désormais la tête.)

Une solution assez complexe à ce problème consiste à découpler le pointeur de souris de la boucle de mise à jour standard. C'est exactement ce que nous avons fait, dans un autre univers où je n'ai pas besoin de dormir. Une solution plus simple ? Il suffit de revenir au pointeur de la souris natif si la fréquence d'images glissante descend sous 20 FPS. (C'est là que la fréquence de roulement s'avère utile. Si nous réagissions à la fréquence d'images actuelle, et si elle oscillait autour de 20 FPS, l'utilisateur verrait le pointeur de souris personnalisé se masquer et s'afficher en permanence.) Cela nous amène à:

Plage de fréquences d'images Comportement
> 10 ips Ralentir le jeu pour éviter de perdre plus d'images.
10 à 20 FPS Utilisez le pointeur de la souris natif au lieu d'un pointeur personnalisé.
20 à 60 ips Opération normale.
> 60 ips Limiter pour que la fréquence d'images ne dépasse pas cette valeur.
Résumé du comportement dépendant de la fréquence d'images.

Oh, et le pointeur de notre souris est sombre sur Mac, mais blanc sur PC. Pourquoi ? Parce que les guerres de plates-formes ont besoin de carburant, même dans des univers fictifs.

Conclusion

Ce n'est pas un moteur parfait, mais il n'essaie pas d'en être un. Élaboré parallèlement au doodle Lem, il est très spécifique. Ce n'est pas un problème. "L'optimisation prématurée est la racine de tout mal", comme l'a dit Don Knuth, et je ne pense pas qu'il soit logique d'écrire un moteur de manière isolée et de ne l'appliquer qu'après plus tard : la pratique informe tout autant la théorie que la théorie. Dans mon cas, le code a été abandonné, plusieurs parties ont été réécrites à l'infini, et de nombreux éléments courants ont été remarqués après la publication, plutôt que ante factum. Mais au final, ce que nous avons fait nous a permis de faire ce que nous voulions : célébrer la carrière de Stanisław Lem et les dessins de Daniel Mróz comme on pouvait l'imaginer.

J'espère que ces informations vous ont éclairé sur certains des choix de conception et des compromis que nous avons dû faire, ainsi que sur la façon dont nous avons utilisé le HTML5 dans un scénario spécifique et concret. Maintenant, jouez avec le code source, faites-le tourner et dites-nous ce que vous en pensez.

Je l'ai fait moi-même. Le contenu ci-dessous était en ligne au cours des derniers jours, avec le compte à rebours jusqu'au 23 novembre 2011 en Russie, premier fuseau horaire à avoir vu le doodle Lem. C'est peut-être quelque chose de loufoque, mais tout comme les doodles, les choses qui paraissent insignifiantes ont parfois une signification plus profonde : ce compteur était vraiment un bon "test de contrainte" pour le moteur.

Capture d&#39;écran du compte à rebours dans l&#39;univers avec le doodle Lem.
Capture d'écran du compte à rebours du compte à rebours dans l'univers Lem doodle.

C'est l'une des façons de découvrir la vie d'un doodle Google : des mois de travail, des semaines de tests, 48 heures d'intégration, le tout pour un jeu que les gens jouent pendant cinq minutes. Nous espérons que ces cinq minutes seront utilisées à bon escient pour chacune de ces milliers de lignes JavaScript. Il vous suffit juste d'en profiter.